summaryrefslogtreecommitdiff
path: root/src/process
diff options
context:
space:
mode:
authorscuri <scuri>2008-10-17 06:10:15 +0000
committerscuri <scuri>2008-10-17 06:10:15 +0000
commit5a422aba704c375a307a902bafe658342e209906 (patch)
tree5005011e086bb863d8fb587ad3319bbec59b2447 /src/process
First commit - moving from LuaForge to SourceForge
Diffstat (limited to 'src/process')
-rw-r--r--src/process/im_analyze.cpp1262
-rw-r--r--src/process/im_arithmetic_bin.cpp503
-rw-r--r--src/process/im_arithmetic_un.cpp210
-rw-r--r--src/process/im_canny.cpp254
-rw-r--r--src/process/im_color.cpp255
-rw-r--r--src/process/im_convolve.cpp1512
-rw-r--r--src/process/im_convolve_rank.cpp701
-rw-r--r--src/process/im_distance.cpp512
-rw-r--r--src/process/im_effects.cpp86
-rw-r--r--src/process/im_fft.cpp218
-rw-r--r--src/process/im_geometric.cpp724
-rw-r--r--src/process/im_histogram.cpp105
-rw-r--r--src/process/im_houghline.cpp435
-rw-r--r--src/process/im_kernel.cpp293
-rw-r--r--src/process/im_logic.cpp136
-rw-r--r--src/process/im_morphology_bin.cpp317
-rw-r--r--src/process/im_morphology_gray.cpp231
-rw-r--r--src/process/im_quantize.cpp65
-rw-r--r--src/process/im_render.cpp532
-rw-r--r--src/process/im_resize.cpp332
-rw-r--r--src/process/im_statistics.cpp341
-rw-r--r--src/process/im_threshold.cpp391
-rw-r--r--src/process/im_tonegamut.cpp322
23 files changed, 9737 insertions, 0 deletions
diff --git a/src/process/im_analyze.cpp b/src/process/im_analyze.cpp
new file mode 100644
index 0000000..50bcbcd
--- /dev/null
+++ b/src/process/im_analyze.cpp
@@ -0,0 +1,1262 @@
+/** \file
+ * \brief Image Analysis
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_analyze.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+
+#include "im_process_ana.h"
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+
+#define MAX_COUNT 65536 // maximum number of regions
+
+/* ajust the alias table to be a remap table (final step) */
+static void alias_update(imushort* alias_table, int &region_count)
+{
+ int i, real_count = region_count;
+
+ for (i = 0; i < region_count; i++)
+ {
+ if (alias_table[i])
+ {
+ // search for the first alias
+ imushort prev = alias_table[i];
+ while (alias_table[prev])
+ prev = alias_table[prev];
+
+ alias_table[i] = prev;
+ real_count--; // decrement aliases from the region count
+ }
+ }
+
+ // now all the aliases in the same group point to only one alias
+ // transform the alias table into a remap table
+
+ alias_table[0] = 0;
+ alias_table[1] = 0; // border is mapped to background
+
+ int r = 1;
+ for (i = 2; i < region_count; i++)
+ {
+ if (!alias_table[i])
+ {
+ alias_table[i] = (imushort)r; // only non alias get real values
+ r++;
+ }
+ else
+ alias_table[i] = (imushort)(alias_table[alias_table[i]]);
+ }
+
+ region_count = real_count-2; // remove the regions (background,border) from the count
+}
+
+/* find the smallest region number to be set as alias. */
+static void alias_getmin(imushort* alias_table, imushort region, imushort &min)
+{
+ while (alias_table[region])
+ {
+ if (min > alias_table[region])
+ min = alias_table[region];
+
+ region = alias_table[region];
+ }
+}
+
+/* replace all the aliases of a region by its smallest value. */
+static void alias_setmin(imushort* alias_table, imushort region, imushort min)
+{
+ while (alias_table[region])
+ {
+ imushort next_region = alias_table[region];
+ alias_table[region] = min;
+ region = next_region;
+ }
+
+ if (region != min)
+ alias_table[region] = min;
+}
+
+/* set a region number to be an alias of another */
+static void alias_set(imushort* alias_table, imushort region1, imushort region2)
+{
+ if (region1 == region2)
+ return;
+
+ imushort min = region1<region2? region1: region2;
+
+ alias_getmin(alias_table, region1, min);
+ alias_getmin(alias_table, region2, min);
+
+ if (region1 != min && alias_table[region1] != min)
+ alias_setmin(alias_table, region1, min);
+ if (region2 != min && alias_table[region2] != min)
+ alias_setmin(alias_table, region2, min);
+}
+
+static int DoAnalyzeFindRegions(int width, int height, imbyte* map, imushort* new_map, int connect)
+{
+ int i, j;
+
+ // mark the pixels that touch the border
+ // if a region touch the border, is the invalid region 1
+
+ imbyte* pmap = map;
+ imushort* new_pmap = new_map;
+ for (j = 0; j < width; j++) // first line
+ {
+ if (pmap[j])
+ new_pmap[j] = 1;
+ }
+ pmap += width;
+ new_pmap += width;
+
+ for (i = 1; i < height-1; i++) // first column
+ {
+ if (pmap[0])
+ new_pmap[0] = 1;
+
+ pmap += width;
+ new_pmap += width;
+ }
+
+ // find and connect the regions
+
+ imbyte* pmap1 = map; // previous line (line 0)
+ imushort* new_pmap1 = new_map;
+
+ pmap = map + width; // current line (line 1)
+ new_pmap = new_map + width;
+
+ int region_count = 2; // 0- background, 1-border
+ imushort* alias_table = new imushort [MAX_COUNT];
+ memset(alias_table, 0, MAX_COUNT); // aliases are all zero at start (not used)
+
+ for (i = 1; i < height; i++)
+ {
+ for (j = 1; j < width; j++)
+ {
+ int has_j1 = j < width-1? 1: 0;
+ if (pmap[j])
+ {
+ if (pmap[j-1] || pmap1[j] ||
+ (connect == 8 && (pmap1[j-1] || (has_j1&&pmap1[j+1])))) // 4 or 8 connected to the previous neighbors
+ {
+ imushort region = 0;
+ if (i == height-1 || j == width-1)
+ {
+ region = new_pmap[j] = 1;
+ }
+
+ if (pmap[j-1])
+ {
+ if (!region)
+ region = new_pmap[j-1]; // horizontal neighbor -00
+ else // X1
+ {
+ // this is a right border pixel that connects to an horizontal neighbor
+
+ // this pixel can connect two different regions
+ alias_set(alias_table, region, new_pmap[j-1]);
+ }
+ }
+
+ if (pmap1[j]) // vertical neighbor
+ {
+ if (!region)
+ region = new_pmap1[j]; // isolated vertical neighbor -X-
+ else // 01
+ {
+ // an horizontal neighbor connects to a vertical neighbor -X-
+ // X1
+
+ // this pixel can connect two different regions
+ alias_set(alias_table, region, new_pmap1[j]);
+ }
+ }
+ else if (region && connect==8 && (has_j1&&pmap1[j+1]))
+ {
+ // an horizontal neighbor connects to a right corner neighbor 00X
+ // X1
+
+ // this pixel can connect two different regions
+ alias_set(alias_table, region, new_pmap1[j+1]);
+ }
+
+ if (connect == 8 && (pmap1[j-1] || (has_j1&&pmap1[j+1])) && !region) // isolated corner
+ {
+ // a left corner neighbor or a right corner neighbor X0X
+ // 01
+
+ if (pmap1[j-1]) // left corner
+ region = new_pmap1[j-1];
+
+ if (pmap1[j+1]) // right corner
+ {
+ if (!region) // isolated right corner
+ region = new_pmap1[j+1];
+ else
+ {
+ // this pixel can connect two different regions
+ alias_set(alias_table, new_pmap1[j-1], new_pmap1[j+1]);
+ }
+ }
+ }
+
+ new_pmap[j] = region;
+ }
+ else
+ {
+ // this pixel touches no pixels
+
+ if (i == height-1 || j == width-1)
+ new_pmap[j] = 1;
+ else
+ {
+ // create a new region 000
+ // 01
+ new_pmap[j] = (imushort)region_count;
+ region_count++;
+
+ if (region_count > MAX_COUNT)
+ {
+ delete [] alias_table;
+ return -1;
+ }
+ }
+ }
+ }
+ }
+
+ pmap1 = pmap;
+ new_pmap1 = new_pmap;
+ pmap += width;
+ new_pmap += width;
+ }
+
+ // now all pixels are marked,
+ // but some marks are aliases to others
+
+ // ajust the alias table to be a remap table
+ // and return the real region count
+ alias_update(alias_table, region_count);
+
+ int count = width*height;
+ for (i = 0; i < count; i++)
+ {
+ new_map[i] = alias_table[new_map[i]];
+ }
+
+ delete [] alias_table;
+
+ return region_count;
+}
+
+static int DoAnalyzeFindRegionsBorder(int width, int height, imbyte* map, imushort* new_map, int connect)
+{
+ int i, j;
+
+ imbyte* pmap1 = map - width; // previous line (line -1 = invalid)
+ imushort* new_pmap1 = new_map - width;
+
+ imbyte* pmap = map; // current line (line 0)
+ imushort* new_pmap = new_map;
+
+ int region_count = 2; // still consider: 0- background, 1-border
+ imushort* alias_table = new imushort [MAX_COUNT];
+ memset(alias_table, 0, MAX_COUNT); // aliases are all zero at start (not used)
+
+ for (i = 0; i < height; i++)
+ {
+ for (j = 0; j < width; j++)
+ {
+ if (pmap[j])
+ {
+ int b01 = j > 0? 1: 0; // valid for pmap[j-1]
+ int b10 = i > 0? 1: 0; // valid for pmap1[j]
+ int b11 = i > 0 && j > 0? 1: 0; // valid for pmap1[j-1]
+ int b12 = i > 0 && j < width-1? 1: 0; // valid for pmap1[j+1]
+
+ if ((b01&&pmap[j-1]) || (b10&&pmap1[j]) ||
+ (connect == 8 && ((b11&&pmap1[j-1]) || (b12&&pmap1[j+1])))) // 4 or 8 connected to the previous neighbors
+ {
+ imushort region = 0;
+
+ if (b01&&pmap[j-1])
+ {
+ if (!region)
+ region = new_pmap[j-1]; // horizontal neighbor -00
+ else // X1
+ {
+ // this is a right border pixel that connects to an horizontal neighbor
+
+ // this pixel can connect two different regions
+ alias_set(alias_table, region, new_pmap[j-1]);
+ }
+ }
+
+ if (b10&&pmap1[j]) // vertical neighbor
+ {
+ if (!region)
+ region = new_pmap1[j]; // isolated vertical neighbor -X-
+ else // 01
+ {
+ // an horizontal neighbor connects to a vertical neighbor -X-
+ // X1
+
+ // this pixel can connect two different regions
+ alias_set(alias_table, region, new_pmap1[j]);
+ }
+ }
+ else if (region && connect == 8 && (b12&&pmap1[j+1]))
+ {
+ // an horizontal neighbor connects to a right corner neighbor 00X
+ // X1
+
+ // this pixel can connect two different regions
+ alias_set(alias_table, region, new_pmap1[j+1]);
+ }
+
+ if (connect == 8 && ((b11&&pmap1[j-1]) || (b12&&pmap1[j+1])) && !region) // isolated corner
+ {
+ // a left corner neighbor or a right corner neighbor X0X
+ // 01
+
+ if (b11&&pmap1[j-1]) // left corner
+ region = new_pmap1[j-1];
+
+ if (b12&&pmap1[j+1]) // right corner
+ {
+ if (!region) // isolated right corner
+ region = new_pmap1[j+1];
+ else
+ {
+ // this pixel can connect two different regions
+ alias_set(alias_table, new_pmap1[j-1], new_pmap1[j+1]);
+ }
+ }
+ }
+
+ new_pmap[j] = region;
+ }
+ else
+ {
+ // this pixel touches no pixels
+
+ // create a new region 000
+ // 01
+ new_pmap[j] = (imushort)region_count;
+ region_count++;
+
+ if (region_count > MAX_COUNT)
+ {
+ delete [] alias_table;
+ return -1;
+ }
+ }
+ }
+ }
+
+ pmap1 = pmap;
+ new_pmap1 = new_pmap;
+ pmap += width;
+ new_pmap += width;
+ }
+
+ // now all pixels are marked,
+ // but some marks are aliases to others
+
+ // ajust the alias table to be a remap table
+ // and return the real region count
+ alias_update(alias_table, region_count);
+
+ int count = width*height;
+ for (i = 0; i < count; i++)
+ {
+ new_map[i] = alias_table[new_map[i]];
+ }
+
+ delete [] alias_table;
+
+ return region_count;
+}
+
+int imAnalyzeFindRegions(const imImage* image, imImage* NewImage, int connect, int touch_border)
+{
+ imImageSetAttribute(NewImage, "REGION_CONNECT", IM_BYTE, 1, connect==4?"4":"8");
+ if (touch_border)
+ return DoAnalyzeFindRegionsBorder(image->width, image->height, (imbyte*)image->data[0], (imushort*)NewImage->data[0], connect);
+ else
+ return DoAnalyzeFindRegions(image->width, image->height, (imbyte*)image->data[0], (imushort*)NewImage->data[0], connect);
+}
+
+void imAnalyzeMeasureArea(const imImage* image, int* data_area, int region_count)
+{
+ imushort* img_data = (imushort*)image->data[0];
+
+ memset(data_area, 0, region_count*sizeof(int));
+
+ for (int i = 0; i < image->count; i++)
+ {
+ if (*img_data)
+ data_area[(*img_data) - 1]++;
+ img_data++;
+ }
+}
+
+void imAnalyzeMeasureCentroid(const imImage* image, const int* data_area, int region_count, float* data_cx, float* data_cy)
+{
+ imushort* img_data = (imushort*)image->data[0];
+ int* local_data_area = 0;
+
+ if (!data_area)
+ {
+ local_data_area = (int*)malloc(region_count*sizeof(int));
+ imAnalyzeMeasureArea(image, local_data_area, region_count);
+ data_area = (const int*)local_data_area;
+ }
+
+ if (data_cx) memset(data_cx, 0, region_count*sizeof(float));
+ if (data_cy) memset(data_cy, 0, region_count*sizeof(float));
+
+ for (int y = 0; y < image->height; y++)
+ {
+ int offset = y*image->width;
+
+ for (int x = 0; x < image->width; x++)
+ {
+ int region_index = img_data[offset+x];
+ if (region_index)
+ {
+ if (data_cx) data_cx[region_index-1] += (float)x;
+ if (data_cy) data_cy[region_index-1] += (float)y;
+ }
+ }
+ }
+
+ for (int i = 0; i < region_count; i++)
+ {
+ if (data_cx) data_cx[i] /= (float)data_area[i];
+ if (data_cy) data_cy[i] /= (float)data_area[i];
+ }
+
+ if (local_data_area)
+ free(local_data_area);
+}
+
+static inline double ipow(double x, int j)
+{
+ double r = 1.0;
+ for (int i = 0; i < j; i++)
+ r *= x;
+ return r;
+}
+
+static void iCalcMoment(double* cm, int px, int py, const imImage* image, const float* cx, const float* cy, int region_count)
+{
+ imushort* img_data = (imushort*)image->data[0];
+
+ memset(cm, 0, region_count*sizeof(double));
+
+ for (int y = 0; y < image->height; y++)
+ {
+ int offset = y*image->width;
+
+ for (int x = 0; x < image->width; x++)
+ {
+ int region_index = img_data[offset+x];
+ if (region_index)
+ {
+ int i = region_index-1;
+
+ if (px == 0)
+ cm[i] += ipow(y-cy[i],py);
+ else if (py == 0)
+ cm[i] += ipow(x-cx[i],px);
+ else
+ cm[i] += ipow(x-cx[i],px)*ipow(y-cy[i],py);
+ }
+ }
+ }
+}
+
+template<class T>
+static inline int IsPerimeterPoint(T* map, int width, int height, int x, int y)
+{
+ // map here points to the start of the line, even if its an invalid line.
+
+ // if outside the image, then is not a perimeter line.
+ if (x == -1 || x == width ||
+ y == -1 || y == height)
+ return 0;
+
+ T v = map[x]; // here v is image(x,y)
+ if (!v)
+ return 0;
+
+ // if touches the border, then is a perimeter line.
+ if (x == 0 || x == width-1 ||
+ y == 0 || y == height-1)
+ return 1;
+
+ // if has 4 connected neighbors, then is a perimeter line.
+ if (map[width+x] != v ||
+ map[x+1] != v ||
+ map[x-1] != v ||
+ map[-width+x] != v)
+ return 1;
+
+ return 0;
+}
+
+void imAnalyzeMeasurePrincipalAxis(const imImage* image, const int* data_area, const float* data_cx, const float* data_cy,
+ const int region_count, float* major_slope, float* major_length,
+ float* minor_slope, float* minor_length)
+{
+ int i;
+ int *local_data_area = 0;
+ float *local_data_cx = 0, *local_data_cy = 0;
+
+ if (!data_area)
+ {
+ local_data_area = (int*)malloc(region_count*sizeof(int));
+ imAnalyzeMeasureArea(image, local_data_area, region_count);
+ data_area = (const int*)local_data_area;
+ }
+
+ if (!data_cx || !data_cy)
+ {
+ if (!data_cx)
+ {
+ local_data_cx = (float*)malloc(region_count*sizeof(float));
+ data_cx = (const float*)local_data_cx;
+ }
+
+ if (!data_cy)
+ {
+ local_data_cy = (float*)malloc(region_count*sizeof(float));
+ data_cy = (const float*)local_data_cy;
+ }
+
+ if (local_data_cx && local_data_cy)
+ imAnalyzeMeasureCentroid(image, data_area, region_count, local_data_cx, local_data_cy);
+ else if (local_data_cx)
+ imAnalyzeMeasureCentroid(image, data_area, region_count, local_data_cx, NULL);
+ else if (local_data_cy)
+ imAnalyzeMeasureCentroid(image, data_area, region_count, NULL, local_data_cy);
+ }
+
+ // additional moments
+ double* cm20 = (double*)malloc(region_count*sizeof(double));
+ double* cm02 = (double*)malloc(region_count*sizeof(double));
+ double* cm11 = (double*)malloc(region_count*sizeof(double));
+
+ iCalcMoment(cm20, 2, 0, image, data_cx, data_cy, region_count);
+ iCalcMoment(cm02, 0, 2, image, data_cx, data_cy, region_count);
+ iCalcMoment(cm11, 1, 1, image, data_cx, data_cy, region_count);
+
+ float *local_major_slope = 0, *local_minor_slope = 0;
+ if (!major_slope)
+ {
+ local_major_slope = (float*)malloc(region_count*sizeof(float));
+ major_slope = local_major_slope;
+ }
+ if (!minor_slope)
+ {
+ local_minor_slope = (float*)malloc(region_count*sizeof(float));
+ minor_slope = local_minor_slope;
+ }
+
+#define RAD2DEG 57.296
+
+ // We are going to find 2 axis parameters.
+ // Axis 1 are located in quadrants 1-3
+ // Axis 2 are located in quadrants 2-4
+
+ // Quadrants
+ // 2 | 1
+ // -----
+ // 3 | 4
+
+ // line coeficients for lines that belongs to axis 1 and 2
+ float* A1 = (float*)malloc(region_count*sizeof(float));
+ float* A2 = (float*)malloc(region_count*sizeof(float));
+ float* C1 = (float*)malloc(region_count*sizeof(float));
+ float* C2 = (float*)malloc(region_count*sizeof(float));
+
+ float *slope1 = major_slope; // Use major_slope as a storage place,
+ float *slope2 = minor_slope; // and create an alias to make code clear.
+
+ for (i = 0; i < region_count; i++)
+ {
+ if (cm11[i] == 0)
+ {
+ slope1[i] = 0;
+ slope2[i] = 90;
+
+ // These should not be used
+ A1[i] = 0;
+ A2[i] = 0; // infinite
+ C1[i] = 0; // data_cy[i]
+ C2[i] = 0;
+ }
+ else
+ {
+ double b = (cm20[i] - cm02[i])/cm11[i];
+ double delta = sqrt(b*b + 4.0);
+ double r1 = (-b-delta)/2.0;
+ double r2 = (-b+delta)/2.0;
+ float a1 = (float)(atan(r1)*RAD2DEG + 90); // to avoid negative results
+ float a2 = (float)(atan(r2)*RAD2DEG + 90);
+
+ if (a1 == 180) a1 = 0;
+ if (a2 == 180) a2 = 0;
+
+ if (a1 < 90) // a1 is quadrants q1-q3
+ {
+ slope1[i] = a1;
+ slope2[i] = a2;
+ A1[i] = (float)r1;
+ A2[i] = (float)r2;
+ }
+ else // a2 is quadrants q1-q3
+ {
+ slope1[i] = a2;
+ slope2[i] = a1;
+ A1[i] = (float)r2;
+ A2[i] = (float)r1;
+ }
+
+ C1[i] = data_cy[i] - A1[i] * data_cx[i];
+ C2[i] = data_cy[i] - A2[i] * data_cx[i];
+ }
+ }
+
+ // moments are not necessary anymore
+ free(cm20); free(cm02); free(cm11);
+ cm20 = 0; cm02 = 0; cm11 = 0;
+
+ // maximum distance from a point in the perimeter to an axis in each side of the axis
+ // D1 is distance to axis 1, a and b are sides
+ float* D1a = (float*)malloc(region_count*sizeof(float));
+ float* D1b = (float*)malloc(region_count*sizeof(float));
+ float* D2a = (float*)malloc(region_count*sizeof(float));
+ float* D2b = (float*)malloc(region_count*sizeof(float));
+ memset(D1a, 0, region_count*sizeof(float));
+ memset(D1b, 0, region_count*sizeof(float));
+ memset(D2a, 0, region_count*sizeof(float));
+ memset(D2b, 0, region_count*sizeof(float));
+
+ imushort* img_data = (imushort*)image->data[0];
+ int width = image->width;
+ int height = image->height;
+ for (int y = 0; y < height; y++)
+ {
+ int offset = y*width;
+
+ for (int x = 0; x < width; x++)
+ {
+ if (IsPerimeterPoint(img_data+offset, width, height, x, y))
+ {
+ i = img_data[offset+x] - 1;
+
+ float d1, d2;
+ if (slope2[i] == 90)
+ {
+ d2 = y - data_cy[i]; // I ckecked this many times, looks odd but it is correct.
+ d1 = x - data_cx[i];
+ }
+ else
+ {
+ d1 = A1[i]*x - y + C1[i];
+ d2 = A2[i]*x - y + C2[i];
+ }
+
+ if (d1 < 0)
+ {
+ d1 = (float)fabs(d1);
+ if (d1 > D1a[i])
+ D1a[i] = d1;
+ }
+ else
+ {
+ if (d1 > D1b[i])
+ D1b[i] = d1;
+ }
+
+ if (d2 < 0)
+ {
+ d2 = (float)fabs(d2);
+ if (d2 > D2a[i])
+ D2a[i] = d2;
+ }
+ else
+ {
+ if (d2 > D2b[i])
+ D2b[i] = d2;
+ }
+ }
+ }
+ }
+
+ for (i = 0; i < region_count; i++)
+ {
+ float AB1 = (float)sqrt(A1[i]*A1[i] + 1);
+ float AB2 = (float)sqrt(A2[i]*A2[i] + 1);
+
+ float D1 = (D1a[i] + D1b[i]) / AB1;
+ float D2 = (D2a[i] + D2b[i]) / AB2;
+
+ if (D1 < D2) // Major Axis in 2-4 quadrants
+ {
+ // now remember that we did an alias before
+ // slope1 -> major_slope
+ // slope2 -> minor_slope
+
+ float tmp = major_slope[i];
+ major_slope[i] = minor_slope[i];
+ minor_slope[i] = tmp;
+
+ if (minor_length) minor_length[i] = D1;
+ if (major_length) major_length[i] = D2;
+ }
+ else
+ {
+ if (minor_length) minor_length[i] = D2;
+ if (major_length) major_length[i] = D1;
+ }
+ }
+
+ if (local_major_slope) free(local_major_slope);
+ if (local_minor_slope) free(local_minor_slope);
+ if (local_data_area) free(local_data_area);
+ if (local_data_cx) free(local_data_cx);
+ if (local_data_cy) free(local_data_cy);
+
+ free(A1);
+ free(A2);
+ free(C1);
+ free(C2);
+
+ free(D1b);
+ free(D2b);
+ free(D1a);
+ free(D2a);
+}
+
+void imAnalyzeMeasureHoles(const imImage* image, int connect, int* count_data, int* area_data, float* perim_data)
+{
+ int i;
+ imImage *inv_image = imImageCreate(image->width, image->height, IM_BINARY, IM_BYTE);
+ imbyte* inv_data = (imbyte*)inv_image->data[0];
+ imushort* img_data = (imushort*)image->data[0];
+
+ // finds the holes in the inverted image
+ for (i = 0; i < image->count; i++)
+ {
+ if (*img_data)
+ *inv_data = 0;
+ else
+ *inv_data = 1;
+
+ img_data++;
+ inv_data++;
+ }
+
+ imImage *holes_image = imImageClone(image);
+ if (!holes_image)
+ return;
+
+ int holes_count = imAnalyzeFindRegions(inv_image, holes_image, connect, 0);
+ imImageDestroy(inv_image);
+
+ if (!holes_count)
+ {
+ imImageDestroy(holes_image);
+ return;
+ }
+
+ // measure the holes area
+ int* holes_area = (int*)malloc(holes_count*sizeof(int));
+ imAnalyzeMeasureArea(holes_image, holes_area, holes_count);
+
+ float* holes_perim = 0;
+ if (perim_data)
+ {
+ holes_perim = (float*)malloc(holes_count*sizeof(int));
+ imAnalyzeMeasurePerimeter(holes_image, holes_perim, holes_count);
+ }
+
+ imushort* holes_data = (imushort*)holes_image->data[0];
+ img_data = (imushort*)image->data[0];
+
+ // holes do not touch the border
+ for (int y = 1; y < image->height-1; y++)
+ {
+ int offset_up = (y+1)*image->width;
+ int offset = y*image->width;
+ int offset_dw = (y-1)*image->width;
+
+ for (int x = 1; x < image->width-1; x++)
+ {
+ int hole_index = holes_data[offset+x];
+
+ if (hole_index && holes_area[hole_index-1]) // a hole not yet used
+ {
+ // if the hole has not been used,
+ // it is the first time we encounter a pixel of this hole.
+ // then it is a pixel from the hole border.
+ // now find which region this hole is inside.
+ // a 4 connected neighbour is necessarilly a valid region or 0.
+
+ int region_index = 0;
+ if (img_data[offset_up + x]) region_index = img_data[offset_up + x];
+ else if (img_data[offset + x+1]) region_index = img_data[offset + x+1];
+ else if (img_data[offset + x-1]) region_index = img_data[offset + x-1];
+ else if (img_data[offset_dw+x]) region_index = img_data[offset_dw+x];
+
+ if (!region_index) continue;
+
+ if (count_data) count_data[region_index-1]++;
+ if (area_data) area_data[region_index-1] += holes_area[hole_index-1];
+ if (perim_data) perim_data[region_index-1] += holes_perim[hole_index-1];
+ holes_area[hole_index-1] = 0; // mark hole as used
+ }
+ }
+ }
+
+ if (holes_perim) free(holes_perim);
+ free(holes_area);
+ imImageDestroy(holes_image);
+}
+
+template<class T>
+static void DoPerimeterLine(T* map, T* new_map, int width, int height)
+{
+ int x, y, offset;
+
+ for (y = 0; y < height; y++)
+ {
+ offset = y*width;
+
+ for (x = 0; x < width; x++)
+ {
+ if (IsPerimeterPoint(map+offset, width, height, x, y))
+ new_map[offset+x] = map[offset+x];
+ else
+ new_map[offset+x] = 0;
+ }
+ }
+}
+
+void imProcessPerimeterLine(const imImage* src_image, imImage* dst_image)
+{
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ DoPerimeterLine((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], src_image->width, src_image->height);
+ break;
+ case IM_USHORT:
+ DoPerimeterLine((imushort*)src_image->data[0], (imushort*)dst_image->data[0], src_image->width, src_image->height);
+ break;
+ case IM_INT:
+ DoPerimeterLine((int*)src_image->data[0], (int*)dst_image->data[0], src_image->width, src_image->height);
+ break;
+ }
+}
+
+/* Perimeter Templates idea based in
+ Parker, Pratical Computer Vision Using C
+
+For 1.414 (sqrt(2)/2 + sqrt(2)/2) [1]:
+ 1 0 0 0 0 1 1 0 0 0 0 1 0 0 0 1 0 1
+ 0 x 0 0 x 0 0 x 0 0 x 0 0 x 0 0 x 0
+ 0 0 1 1 0 0 1 0 0 0 0 1 1 0 1 0 0 0
+ 129 36 132 33 5 160
+
+For 1.207 (sqrt(2)/2 + 1.0/2) [2]:
+ 0 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 1 0 0 0 1 0 0
+ 1 x 0 1 x 0 0 x 0 0 x 0 0 x 0 0 x 0 0 x 1 0 x 1
+ 0 0 1 0 0 0 1 0 0 0 0 1 0 1 0 0 1 0 1 0 0 0 0 0
+ 17 48 68 65 130 34 12 136
+
+ 0 0 0 1 0 0 1 1 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 0
+ 1 x 0 1 x 0 0 x 0 0 x 0 0 x 1 0 x 1 0 x 0 0 x 0
+ 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 1 1 1 0
+ 20 144 192 96 40 9 3 6
+
+For 1.0 (1.0/2 + 1.0/2) [0]:
+ 0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 1 0
+ 1 x 1 0 x 0 1 x 0 0 x 1 1 x 0 0 x 1
+ 0 0 0 0 1 0 0 1 0 0 1 0 0 0 0 0 0 0
+ 24 66 18 10 80 72
+
+For 0.707 (sqrt(2)/2) [3]:
+ 1 0 0 0 0 1 0 0 0 0 0 0
+ 0 x 0 0 x 0 0 x 0 0 x 0 (For Line Length)
+ 0 0 0 0 0 0 0 0 1 1 0 0
+ 128 32 1 4
+
+For 0.5 (1.0/2) [4]:
+ 0 1 0 0 0 0 0 0 0 0 0 0
+ 0 x 0 0 x 1 0 x 0 1 x 0 (For Line Length)
+ 0 0 0 0 0 0 0 1 0 0 0 0
+ 64 8 2 16
+
+*/
+static void iInitPerimTemplate(imbyte *templ, float *v)
+{
+ memset(templ, 0, 256);
+
+ templ[129] = 1;
+ templ[36] = 1;
+ templ[132] = 1;
+ templ[33] = 1;
+ templ[5] = 1;
+ templ[160] = 1;
+
+ templ[17] = 2;
+ templ[48] = 2;
+ templ[68] = 2;
+ templ[65] = 2;
+ templ[130] = 2;
+ templ[34] = 2;
+ templ[12] = 2;
+ templ[136] = 2;
+ templ[20] = 2;
+ templ[144] = 2;
+ templ[192] = 2;
+ templ[96] = 2;
+ templ[40] = 2;
+ templ[9] = 2;
+ templ[3] = 2;
+ templ[6] = 2;
+
+ templ[24] = 0;
+ templ[66] = 0;
+ templ[18] = 0;
+ templ[10] = 0;
+ templ[80] = 0;
+ templ[72] = 0;
+
+ templ[128] = 3;
+ templ[32] = 3;
+ templ[1] = 3;
+ templ[4] = 3;
+
+ templ[64] = 4;
+ templ[8] = 4;
+ templ[2] = 4;
+ templ[16] = 4;
+
+const float DT_SQRT2 = 1.414213562373f;
+const float DT_SQRT2D2 = 0.707106781187f;
+
+ v[1] = DT_SQRT2;
+ v[2] = DT_SQRT2D2 + 0.5f;
+ v[0] = 1.0f;
+ v[3] = DT_SQRT2D2;
+ v[4] = 0.5f;
+}
+
+void imAnalyzeMeasurePerimeter(const imImage* image, float* perim_data, int region_count)
+{
+ static imbyte templ[256];
+ static float vt[5];
+ static int first = 1;
+ if (first)
+ {
+ iInitPerimTemplate(templ, vt);
+ first = 0;
+ }
+
+ imushort* map = (imushort*)image->data[0];
+
+ memset(perim_data, 0, region_count*sizeof(int));
+
+ int width = image->width;
+ int height = image->height;
+ for (int y = 0; y < height; y++)
+ {
+ int offset = y*image->width;
+
+ for (int x = 0; x < width; x++)
+ {
+ if (IsPerimeterPoint(map+offset, width, height, x, y))
+ {
+ int T = 0;
+
+ // check the 8 neighboors if they belong to the perimeter
+ if (IsPerimeterPoint(map+offset+width, width, height, x-1, y+1))
+ T |= 0x01;
+ if (IsPerimeterPoint(map+offset+width, width, height, x, y+1))
+ T |= 0x02;
+ if (IsPerimeterPoint(map+offset+width, width, height, x+1, y+1))
+ T |= 0x04;
+
+ if (IsPerimeterPoint(map+offset, width, height, x-1, y))
+ T |= 0x08;
+ if (IsPerimeterPoint(map+offset, width, height, x+1, y))
+ T |= 0x10;
+
+ if (IsPerimeterPoint(map+offset-width, width, height, x-1, y-1))
+ T |= 0x20;
+ if (IsPerimeterPoint(map+offset-width, width, height, x, y-1))
+ T |= 0x40;
+ if (IsPerimeterPoint(map+offset-width, width, height, x+1, y-1))
+ T |= 0x80;
+
+ if (T)
+ perim_data[map[offset+x] - 1] += vt[templ[T]];
+ }
+ }
+ }
+}
+
+/* Perimeter Area Templates
+
+For "1.0" (0):
+
+ 1 1 1
+ 1 x 1
+ 1 1 1
+ 255
+
+For "0.75" (1):
+
+ 1 1 1 1 1 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 1
+ 1 x 1 1 x 1 1 x 1 1 x 1 0 x 1 1 x 0 1 x 1 1 x 1
+ 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1
+ 251 254 127 223 239 247 253 191
+
+For "0.625" (2):
+
+ 1 1 1 0 0 1 0 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 0 0
+ 1 x 1 1 x 1 0 x 1 1 x 0 0 x 1 1 x 0 1 x 1 1 x 1
+ 0 0 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 0 1 0 0 1 1 1
+ 249 63 111 215 235 246 252 159
+
+For "0.5" (3):
+
+ 0 0 0 0 1 1 1 1 1 1 1 0 1 1 1 0 0 1 1 0 0 1 1 1
+ 1 x 1 0 x 1 1 x 1 1 x 0 0 x 1 0 x 1 1 x 0 1 x 0
+ 1 1 1 0 1 1 0 0 0 1 1 0 0 0 1 1 1 1 1 1 1 1 0 0
+ 31 107 248 214 233 47 151 244
+
+For "0.375" (4):
+
+ 0 0 0 1 1 1 1 1 0 0 1 1 1 0 0 0 0 1 0 0 0 1 1 1
+ 1 x 0 1 x 0 1 x 0 0 x 1 1 x 0 0 x 1 0 x 1 0 x 1
+ 1 1 1 0 0 0 1 0 0 0 0 1 1 1 0 0 1 1 1 1 1 0 0 0
+ 23 240 212 105 150 43 15 232
+
+For "0.25" (5):
+
+ 0 0 0 0 0 0 1 1 0 0 1 1 1 0 0 0 0 1 0 0 0 1 1 1
+ 1 x 0 0 x 1 1 x 0 0 x 1 1 x 0 0 x 1 0 x 0 0 x 0
+ 1 1 0 0 1 1 0 0 0 0 0 0 1 0 0 0 0 1 1 1 1 0 0 0
+ 22 11 208 104 148 41 7 224
+
+For "0.125" (6):
+
+ 0 0 0 0 0 0 1 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 1 1
+ 1 x 0 0 x 0 0 x 0 0 x 1 1 x 0 0 x 1 0 x 0 0 x 0
+ 1 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 0 0 0
+ 20 3 192 40 144 9 6 96
+
+*/
+static void iInitPerimAreaTemplate(imbyte *templ, float *v)
+{
+ memset(templ, 0, 256);
+
+ templ[255] = 0;
+
+ templ[251] = 1;
+ templ[254] = 1;
+ templ[127] = 1;
+ templ[223] = 1;
+ templ[239] = 1;
+ templ[247] = 1;
+ templ[253] = 1;
+ templ[191] = 1;
+
+ templ[249] = 2;
+ templ[63] = 2;
+ templ[111] = 2;
+ templ[215] = 2;
+ templ[235] = 2;
+ templ[246] = 2;
+ templ[252] = 2;
+ templ[159] = 2;
+
+ templ[31] = 3;
+ templ[107] = 3;
+ templ[248] = 3;
+ templ[214] = 3;
+ templ[233] = 3;
+ templ[47] = 3;
+ templ[151] = 3;
+ templ[244] = 3;
+
+ templ[23] = 4;
+ templ[240] = 4;
+ templ[212] = 4;
+ templ[105] = 4;
+ templ[150] = 4;
+ templ[43] = 4;
+ templ[15] = 4;
+ templ[232] = 4;
+
+ templ[22] = 5;
+ templ[11] = 5;
+ templ[208] = 5;
+ templ[104] = 5;
+ templ[148] = 5;
+ templ[41] = 5;
+ templ[7] = 5;
+ templ[224] = 5;
+
+ templ[20] = 6;
+ templ[3] = 6;
+ templ[192] = 6;
+ templ[40] = 6;
+ templ[144] = 6;
+ templ[9] = 6;
+ templ[6] = 6;
+ templ[96] = 6;
+
+ v[0] = 1.0f;
+ v[1] = 0.75f;
+ v[2] = 0.625f;
+ v[3] = 0.5f;
+ v[4] = 0.375f;
+ v[5] = 0.25f;
+ v[6] = 0.125f;
+}
+
+void imAnalyzeMeasurePerimArea(const imImage* image, float* area_data)
+{
+ static imbyte templ[256];
+ static float vt[7];
+ static int first = 1;
+ if (first)
+ {
+ iInitPerimAreaTemplate(templ, vt);
+ first = 0;
+ }
+
+ imushort* map = (imushort*)image->data[0];
+
+ int width = image->width;
+ int height = image->height;
+ for (int y = 0; y < height; y++)
+ {
+ int offset_up = (y+1)*width;
+ int offset = y*width;
+ int offset_dw = (y-1)*width;
+
+ for (int x = 0; x < width; x++)
+ {
+ imushort v = map[offset+x];
+ if (v)
+ {
+ int T = 0;
+ if (x>0 && y<height-1 && map[offset_up + x-1] == v) T |= 0x01;
+ if (y<height-1 && map[offset_up + x ] == v) T |= 0x02;
+ if (x<width-1 && y<height-1 && map[offset_up + x+1] == v) T |= 0x04;
+ if (x>0 && map[offset + x-1] == v) T |= 0x08;
+ if (x<width-1 && map[offset + x+1] == v) T |= 0x10;
+ if (x>0 && y>0 && map[offset_dw + x-1] == v) T |= 0x20;
+ if (y>0 && map[offset_dw + x ] == v) T |= 0x40;
+ if (x<width-1 && y>0 && map[offset_dw + x+1] == v) T |= 0x80;
+
+ if (T)
+ area_data[v-1] += vt[templ[T]];
+ }
+ }
+ }
+}
+
+void imProcessPrune(const imImage* image, imImage* NewImage, int connect, int start_size, int end_size)
+{
+ imImage *region_image = imImageCreate(image->width, image->height, IM_GRAY, IM_USHORT);
+ if (!region_image)
+ return;
+
+ int region_count = imAnalyzeFindRegions(image, region_image, connect, 1);
+ if (!region_count)
+ {
+ imImageClear(NewImage);
+ imImageDestroy(region_image);
+ return;
+ }
+
+ int* area_data = (int*)malloc(region_count*sizeof(int));
+ imAnalyzeMeasureArea(region_image, area_data, region_count);
+
+ imushort* region_data = (imushort*)region_image->data[0];
+ imbyte* img_data = (imbyte*)NewImage->data[0];
+
+ for (int i = 0; i < image->count; i++)
+ {
+ if (*region_data)
+ {
+ int area = area_data[(*region_data) - 1];
+ if (area < start_size || (end_size && area > end_size))
+ *img_data = 0;
+ else
+ *img_data = 1;
+ }
+ else
+ *img_data = 0;
+
+ region_data++;
+ img_data++;
+ }
+
+ free(area_data);
+ imImageDestroy(region_image);
+}
+
+void imProcessFillHoles(const imImage* image, imImage* NewImage, int connect)
+{
+ // finding regions in the inverted image will isolate only the holes.
+ imProcessNegative(image, NewImage);
+
+ imImage *region_image = imImageCreate(image->width, image->height, IM_GRAY, IM_USHORT);
+ if (!region_image)
+ return;
+
+ int holes_count = imAnalyzeFindRegions(NewImage, region_image, connect, 0);
+ if (!holes_count)
+ {
+ imImageCopy(image, NewImage);
+ imImageDestroy(region_image);
+ return;
+ }
+
+ imushort* region_data = (imushort*)region_image->data[0];
+ imbyte* dst_data = (imbyte*)NewImage->data[0];
+
+ for (int i = 0; i < image->count; i++)
+ {
+ if (*region_data)
+ *dst_data = 1;
+ else
+ *dst_data = !(*dst_data); // Fix negative data.
+
+ region_data++;
+ dst_data++;
+ }
+
+ imImageDestroy(region_image);
+}
diff --git a/src/process/im_arithmetic_bin.cpp b/src/process/im_arithmetic_bin.cpp
new file mode 100644
index 0000000..74fe010
--- /dev/null
+++ b/src/process/im_arithmetic_bin.cpp
@@ -0,0 +1,503 @@
+/** \file
+ * \brief Binary Arithmetic Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_arithmetic_bin.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+#include <im_complex.h>
+#include <im_counter.h>
+
+#include "im_process_pon.h"
+#include "im_math_op.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+
+template <class T1, class T2, class T3>
+static void DoBinaryOp(T1 *map1, T2 *map2, T3 *map, int count, int op)
+{
+ int i;
+
+ switch(op)
+ {
+ case IM_BIN_ADD:
+ for (i = 0; i < count; i++)
+ map[i] = add_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_SUB:
+ for (i = 0; i < count; i++)
+ map[i] = sub_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_MUL:
+ for (i = 0; i < count; i++)
+ map[i] = mul_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_DIV:
+ for (i = 0; i < count; i++)
+ map[i] = div_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_DIFF:
+ for (i = 0; i < count; i++)
+ map[i] = diff_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_MIN:
+ for (i = 0; i < count; i++)
+ map[i] = min_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_MAX:
+ for (i = 0; i < count; i++)
+ map[i] = max_op((T3)map1[i], (T3)map2[i]);
+ break;
+ case IM_BIN_POW:
+ for (i = 0; i < count; i++)
+ map[i] = pow_op((T3)map1[i], (T3)map2[i]);
+ break;
+ }
+}
+
+static void DoBinaryOpCpxReal(imcfloat *map1, float *map2, imcfloat *map, int count, int op)
+{
+ int i;
+
+ switch(op)
+ {
+ case IM_BIN_ADD:
+ for (i = 0; i < count; i++)
+ map[i] = add_op(map1[i], map2[i]);
+ break;
+ case IM_BIN_SUB:
+ for (i = 0; i < count; i++)
+ map[i] = sub_op(map1[i], map2[i]);
+ break;
+ case IM_BIN_MUL:
+ for (i = 0; i < count; i++)
+ map[i] = mul_op(map1[i], map2[i]);
+ break;
+ case IM_BIN_DIV:
+ for (i = 0; i < count; i++)
+ map[i] = div_op(map1[i], (imcfloat)map2[i]);
+ break;
+ case IM_BIN_DIFF:
+ for (i = 0; i < count; i++)
+ map[i] = diff_op(map1[i], map2[i]);
+ break;
+ case IM_BIN_MIN:
+ for (i = 0; i < count; i++)
+ map[i] = min_op(map1[i], map2[i]);
+ break;
+ case IM_BIN_MAX:
+ for (i = 0; i < count; i++)
+ map[i] = max_op(map1[i], map2[i]);
+ break;
+ case IM_BIN_POW:
+ for (i = 0; i < count; i++)
+ map[i] = pow_op(map1[i], map2[i]);
+ break;
+ }
+}
+
+void imProcessArithmeticOp(const imImage* src_image1, const imImage* src_image2, imImage* dst_image, int op)
+{
+ int count = src_image1->count;
+
+ for (int i = 0; i < src_image1->depth; i++)
+ {
+ switch(src_image1->data_type)
+ {
+ case IM_BYTE:
+ if (dst_image->data_type == IM_FLOAT)
+ DoBinaryOp((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (float*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_USHORT)
+ DoBinaryOp((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (imushort*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_INT)
+ DoBinaryOp((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (int*)dst_image->data[i], count, op);
+ else
+ DoBinaryOp((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (imbyte*)dst_image->data[i], count, op);
+ break;
+ case IM_USHORT:
+ if (dst_image->data_type == IM_FLOAT)
+ DoBinaryOp((imushort*)src_image1->data[i], (imushort*)src_image2->data[i], (float*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_INT)
+ DoBinaryOp((imushort*)src_image1->data[i], (imushort*)src_image2->data[i], (int*)dst_image->data[i], count, op);
+ else
+ DoBinaryOp((imushort*)src_image1->data[i], (imushort*)src_image2->data[i], (imushort*)dst_image->data[i], count, op);
+ break;
+ case IM_INT:
+ if (dst_image->data_type == IM_FLOAT)
+ DoBinaryOp((int*)src_image1->data[i], (int*)src_image2->data[i], (float*)dst_image->data[i], count, op);
+ else
+ DoBinaryOp((int*)src_image1->data[i], (int*)src_image2->data[i], (int*)dst_image->data[i], count, op);
+ break;
+ case IM_FLOAT:
+ DoBinaryOp((float*)src_image1->data[i], (float*)src_image2->data[i], (float*)dst_image->data[i], count, op);
+ break;
+ case IM_CFLOAT:
+ if (src_image2->data_type == IM_FLOAT)
+ DoBinaryOpCpxReal((imcfloat*)src_image1->data[i], (float*)src_image2->data[i], (imcfloat*)dst_image->data[i], count, op);
+ else
+ DoBinaryOp((imcfloat*)src_image1->data[i], (imcfloat*)src_image2->data[i], (imcfloat*)dst_image->data[i], count, op);
+ break;
+ }
+ }
+}
+
+template <class T>
+static inline T blend_op(const T& v1, const T& v2, const float& alpha)
+{
+ return (T)(alpha*v1 + (1.0f - alpha)*v2);
+}
+
+template <class T>
+static void DoBlendConst(T *map1, T *map2, T *map, int count, float alpha)
+{
+ for (int i = 0; i < count; i++)
+ map[i] = blend_op(map1[i], map2[i], alpha);
+}
+
+void imProcessBlendConst(const imImage* src_image1, const imImage* src_image2, imImage* dst_image, float alpha)
+{
+ int count = src_image1->count;
+
+ for (int i = 0; i < src_image1->depth; i++)
+ {
+ switch(src_image1->data_type)
+ {
+ case IM_BYTE:
+ DoBlendConst((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (imbyte*)dst_image->data[i], count, alpha);
+ break;
+ case IM_USHORT:
+ DoBlendConst((imushort*)src_image1->data[i], (imushort*)src_image2->data[i], (imushort*)dst_image->data[i], count, alpha);
+ break;
+ case IM_INT:
+ DoBlendConst((int*)src_image1->data[i], (int*)src_image2->data[i], (int*)dst_image->data[i], count, alpha);
+ break;
+ case IM_FLOAT:
+ DoBlendConst((float*)src_image1->data[i], (float*)src_image2->data[i], (float*)dst_image->data[i], count, alpha);
+ break;
+ case IM_CFLOAT:
+ DoBlendConst((imcfloat*)src_image1->data[i], (imcfloat*)src_image2->data[i], (imcfloat*)dst_image->data[i], count, alpha);
+ break;
+ }
+ }
+}
+
+template <class T, class TA>
+static void DoBlend(T *map1, T *map2, TA *alpha, T *map, int count, TA max)
+{
+ for (int i = 0; i < count; i++)
+ map[i] = blend_op(map1[i], map2[i], ((float)alpha[i])/max);
+}
+
+void imProcessBlend(const imImage* src_image1, const imImage* src_image2, const imImage* alpha, imImage* dst_image)
+{
+ int count = src_image1->count;
+
+ for (int i = 0; i < src_image1->depth; i++)
+ {
+ switch(src_image1->data_type)
+ {
+ case IM_BYTE:
+ DoBlend((imbyte*)src_image1->data[i], (imbyte*)src_image2->data[i], (imbyte*)alpha->data[0], (imbyte*)dst_image->data[i], count, (imbyte)255);
+ break;
+ case IM_USHORT:
+ DoBlend((imushort*)src_image1->data[i], (imushort*)src_image2->data[i], (imushort*)alpha->data[0], (imushort*)dst_image->data[i], count, (imushort)65535);
+ break;
+ case IM_INT:
+ DoBlend((int*)src_image1->data[i], (int*)src_image2->data[i], (int*)alpha->data[0], (int*)dst_image->data[i], count, (int)2147483647);
+ break;
+ case IM_FLOAT:
+ DoBlend((float*)src_image1->data[i], (float*)src_image2->data[i], (float*)alpha->data[0], (float*)dst_image->data[i], count, 1.0f);
+ break;
+ case IM_CFLOAT:
+ DoBlend((imcfloat*)src_image1->data[i], (imcfloat*)src_image2->data[i], (float*)alpha->data[0], (imcfloat*)dst_image->data[i], count, 1.0f);
+ break;
+ }
+ }
+}
+
+static void DoBinaryConstOpCpxReal(imcfloat *map1, float value, imcfloat *map, int count, int op)
+{
+ int i;
+
+ switch(op)
+ {
+ case IM_BIN_ADD:
+ for (i = 0; i < count; i++)
+ map[i] = add_op(map1[i], value);
+ break;
+ case IM_BIN_SUB:
+ for (i = 0; i < count; i++)
+ map[i] = sub_op(map1[i], value);
+ break;
+ case IM_BIN_MUL:
+ for (i = 0; i < count; i++)
+ map[i] = mul_op(map1[i], value);
+ break;
+ case IM_BIN_DIV:
+ for (i = 0; i < count; i++)
+ map[i] = div_op(map1[i], (imcfloat)value);
+ break;
+ case IM_BIN_DIFF:
+ for (i = 0; i < count; i++)
+ map[i] = diff_op(map1[i], value);
+ break;
+ case IM_BIN_MIN:
+ for (i = 0; i < count; i++)
+ map[i] = min_op(map1[i], value);
+ break;
+ case IM_BIN_MAX:
+ for (i = 0; i < count; i++)
+ map[i] = max_op(map1[i], value);
+ break;
+ case IM_BIN_POW:
+ for (i = 0; i < count; i++)
+ map[i] = pow_op(map1[i], value);
+ break;
+ }
+}
+
+template <class T1, class T2, class T3>
+static void DoBinaryConstOp(T1 *map1, T2 value, T3 *map, int count, int op)
+{
+ int i;
+
+ switch(op)
+ {
+ case IM_BIN_ADD:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)add_op((T2)map1[i], value);
+ break;
+ case IM_BIN_SUB:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)sub_op((T2)map1[i], value);
+ break;
+ case IM_BIN_MUL:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)mul_op((T2)map1[i], value);
+ break;
+ case IM_BIN_DIV:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)div_op((T2)map1[i], value);
+ break;
+ case IM_BIN_DIFF:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)diff_op((T2)map1[i], value);
+ break;
+ case IM_BIN_MIN:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)min_op((T2)map1[i], value);
+ break;
+ case IM_BIN_MAX:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)max_op((T2)map1[i], value);
+ break;
+ case IM_BIN_POW:
+ for (i = 0; i < count; i++)
+ map[i] = (T3)pow_op((T2)map1[i], value);
+ break;
+ }
+}
+
+void imProcessArithmeticConstOp(const imImage* src_image1, float value, imImage* dst_image, int op)
+{
+ int count = src_image1->count;
+
+ for (int i = 0; i < src_image1->depth; i++)
+ {
+ switch(src_image1->data_type)
+ {
+ case IM_BYTE:
+ if (dst_image->data_type == IM_FLOAT)
+ DoBinaryConstOp((imbyte*)src_image1->data[i], (float)value, (float*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_USHORT)
+ DoBinaryConstOp((imbyte*)src_image1->data[i], (imushort)value, (imushort*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_INT)
+ DoBinaryConstOp((imbyte*)src_image1->data[i], (int)value, (int*)dst_image->data[i], count, op);
+ else
+ DoBinaryConstOp((imbyte*)src_image1->data[i], (imushort)value, (imbyte*)dst_image->data[i], count, op);
+ break;
+ case IM_USHORT:
+ if (dst_image->data_type == IM_FLOAT)
+ DoBinaryConstOp((imushort*)src_image1->data[i], (float)value, (float*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_INT)
+ DoBinaryConstOp((imushort*)src_image1->data[i], (int)value, (int*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_BYTE)
+ DoBinaryConstOp((imushort*)src_image1->data[i], (imushort)value, (imbyte*)dst_image->data[i], count, op);
+ else
+ DoBinaryConstOp((imushort*)src_image1->data[i], (imushort)value, (imushort*)dst_image->data[i], count, op);
+ break;
+ case IM_INT:
+ if (dst_image->data_type == IM_FLOAT)
+ DoBinaryConstOp((int*)src_image1->data[i], (float)value, (float*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_USHORT)
+ DoBinaryConstOp((int*)src_image1->data[i], (int)value, (imushort*)dst_image->data[i], count, op);
+ else if (dst_image->data_type == IM_BYTE)
+ DoBinaryConstOp((int*)src_image1->data[i], (int)value, (imbyte*)dst_image->data[i], count, op);
+ else
+ DoBinaryConstOp((int*)src_image1->data[i], (int)value, (int*)dst_image->data[i], count, op);
+ break;
+ case IM_FLOAT:
+ DoBinaryConstOp((float*)src_image1->data[i], (float)value, (float*)dst_image->data[i], count, op);
+ break;
+ case IM_CFLOAT:
+ DoBinaryConstOpCpxReal((imcfloat*)src_image1->data[i], (float)value, (imcfloat*)dst_image->data[i], count, op);
+ break;
+ }
+ }
+}
+
+void imProcessMultipleMean(const imImage** src_image_list, int src_image_count, imImage* dst_image)
+{
+ const imImage* image1 = src_image_list[0];
+
+ int data_type = image1->data_type;
+ if (image1->data_type == IM_BYTE)
+ data_type = IM_USHORT;
+
+ imImage *acum_image = imImageCreate(image1->width, image1->height, image1->color_space, data_type);
+ if (!acum_image)
+ return;
+
+ for(int i = 0; i < src_image_count; i++)
+ {
+ const imImage *image = src_image_list[i];
+ imProcessUnArithmeticOp(image, acum_image, IM_UN_INC);
+ }
+
+ imProcessArithmeticConstOp(acum_image, float(src_image_count), dst_image, IM_BIN_DIV);
+
+ imImageDestroy(acum_image);
+}
+
+void imProcessMultipleStdDev(const imImage** src_image_list, int src_image_count, const imImage *mean_image, imImage* dst_image)
+{
+ imImage* aux_image = imImageClone(dst_image);
+ if (!aux_image)
+ return;
+
+ // sdtdev = sqrt( sum(sqr(x - m)) / N)
+
+ // a = sum(sqr(x - m))
+ for(int i = 0; i < src_image_count; i++)
+ {
+ // aux_image = image - mean_image
+ imProcessArithmeticOp(src_image_list[i], mean_image, aux_image, IM_BIN_SUB);
+
+ // aux_image = aux_image * aux_image
+ imProcessUnArithmeticOp(aux_image, aux_image, IM_UN_SQR);
+
+ // dst_image += aux_image
+ imProcessUnArithmeticOp(aux_image, dst_image, IM_UN_INC);
+ }
+
+ // dst_image = dst_image / src_image_count;
+ imProcessArithmeticConstOp(dst_image, float(src_image_count), dst_image, IM_BIN_DIV);
+
+ // dst_image = sqrt(dst_image);
+ imProcessUnArithmeticOp(dst_image, dst_image, IM_UN_SQRT);
+
+ imImageDestroy(aux_image);
+}
+
+template <class DT>
+static float AutoCovCalc(int width, int height, DT *src_map, DT *mean_map, int x, int y, float count)
+{
+ float value = 0;
+ int ni = height - y;
+ int nj = width - x;
+ int offset, offset1;
+ int next = width*y + x;
+
+ for (int i = 0; i < ni; i++)
+ {
+ for (int j = 0; j < nj; j++)
+ {
+ offset = width*i + j;
+ offset1 = offset + next;
+ value += float(src_map[offset] - mean_map[offset]) * float(src_map[offset1] - mean_map[offset1]);
+ }
+ }
+
+ return (value/count);
+}
+
+template <class DT>
+static int AutoCov(int width, int height, DT *src_map, DT *mean_map, float *dst_map, int counter)
+{
+ int count = width*height;
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ *dst_map = AutoCovCalc(width, height, src_map, mean_map, x, y, (float)count);
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+int imProcessAutoCovariance(const imImage* image, const imImage* mean_image, imImage* dst_image)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("Auto Convariance");
+ imCounterTotal(counter, image->depth*image->height, "Processing...");
+
+ for (int i = 0; i < image->depth; i++)
+ {
+ switch(image->data_type)
+ {
+ case IM_BYTE:
+ ret = AutoCov(image->width, image->height, (imbyte*)image->data[i], (imbyte*)mean_image->data[i], (float*)dst_image->data[i], counter);
+ break;
+ case IM_USHORT:
+ ret = AutoCov(image->width, image->height, (imushort*)image->data[i], (imushort*)mean_image->data[i], (float*)dst_image->data[i], counter);
+ break;
+ case IM_INT:
+ ret = AutoCov(image->width, image->height, (int*)image->data[i], (int*)mean_image->data[i], (float*)dst_image->data[i], counter);
+ break;
+ case IM_FLOAT:
+ ret = AutoCov(image->width, image->height, (float*)image->data[i], (float*)mean_image->data[i], (float*)dst_image->data[i], counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+void imProcessMultiplyConj(const imImage* image1, const imImage* image2, imImage* NewImage)
+{
+ int total_count = image1->count*image1->depth;
+
+ imcfloat* map = (imcfloat*)NewImage->data[0];
+ imcfloat* map1 = (imcfloat*)image1->data[0];
+ imcfloat* map2 = (imcfloat*)image2->data[0];
+ imcfloat tmp; // this will allow an in-place operation
+
+ for (int i = 0; i < total_count; i++)
+ {
+ tmp.real = map1->real * map2->real + map1->imag * map2->imag;
+ tmp.imag = map1->real * map2->imag - map1->imag * map2->real;
+ *map = tmp;
+
+ map++;
+ map1++;
+ map2++;
+ }
+}
diff --git a/src/process/im_arithmetic_un.cpp b/src/process/im_arithmetic_un.cpp
new file mode 100644
index 0000000..59e384c
--- /dev/null
+++ b/src/process/im_arithmetic_un.cpp
@@ -0,0 +1,210 @@
+/** \file
+ * \brief Unary Arithmetic Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_arithmetic_un.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+#include <im_complex.h>
+
+#include "im_process_pon.h"
+#include "im_math_op.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+// Fake complex operations for real types
+static inline imbyte conj_op(const imbyte& v) {return v;}
+static inline imushort conj_op(const imushort& v) {return v;}
+static inline int conj_op(const int& v) {return v;}
+static inline float conj_op(const float& v) {return v;}
+static inline imbyte cpxnorm_op(const imbyte& v) {return v;}
+static inline imushort cpxnorm_op(const imushort& v) {return v;}
+static inline int cpxnorm_op(const int& v) {return v;}
+static inline float cpxnorm_op(const float& v) {return v;}
+
+static inline imcfloat conj_op(const imcfloat& v)
+{
+ imcfloat r;
+ r.real = v.real;
+ r.imag = -v.imag;
+ return r;
+}
+
+static inline imcfloat cpxnorm_op(const imcfloat& v)
+{
+ imcfloat r;
+ float rmag = cpxmag(v);
+ if (rmag != 0.0f)
+ {
+ r.real = v.real/rmag;
+ r.imag = v.imag/rmag;
+ }
+ else
+ {
+ r.real = 0.0f;
+ r.imag = 0.0f;
+ }
+ return r;
+}
+
+template <class T1, class T2>
+static void DoUnaryOp(T1 *map, T2 *new_map, int count, int op)
+{
+ int i;
+
+ switch(op)
+ {
+ case IM_UN_ABS:
+ for (i = 0; i < count; i++)
+ new_map[i] = abs_op((T2)map[i]);
+ break;
+ case IM_UN_INV:
+ for (i = 0; i < count; i++)
+ new_map[i] = inv_op((T2)map[i]);
+ break;
+ case IM_UN_EQL:
+ for (i = 0; i < count; i++)
+ new_map[i] = (T2)map[i];
+ break;
+ case IM_UN_INC:
+ for (i = 0; i < count; i++)
+ new_map[i] = (T2)(new_map[i] + map[i]);
+ break;
+ case IM_UN_LESS:
+ for (i = 0; i < count; i++)
+ new_map[i] = less_op((T2)map[i]);
+ break;
+ case IM_UN_SQR:
+ for (i = 0; i < count; i++)
+ new_map[i] = sqr_op((T2)map[i]);
+ break;
+ case IM_UN_SQRT:
+ for (i = 0; i < count; i++)
+ new_map[i] = (T2)sqrt_op(map[i]);
+ break;
+ case IM_UN_LOG:
+ for (i = 0; i < count; i++)
+ new_map[i] = log_op((T2)map[i]);
+ break;
+ case IM_UN_SIN:
+ for (i = 0; i < count; i++)
+ new_map[i] = sin_op((T2)map[i]);
+ break;
+ case IM_UN_COS:
+ for (i = 0; i < count; i++)
+ new_map[i] = cos_op((T2)map[i]);
+ break;
+ case IM_UN_EXP:
+ for (i = 0; i < count; i++)
+ new_map[i] = exp_op((T2)map[i]);
+ break;
+ case IM_UN_CONJ:
+ for (i = 0; i < count; i++)
+ new_map[i] = conj_op((T2)map[i]);
+ break;
+ case IM_UN_CPXNORM:
+ for (i = 0; i < count; i++)
+ new_map[i] = cpxnorm_op((T2)map[i]);
+ break;
+ }
+}
+
+void imProcessUnArithmeticOp(const imImage* src_image, imImage* dst_image, int op)
+{
+ int total_count = src_image->count * src_image->depth;
+
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ if (dst_image->data_type == IM_FLOAT)
+ DoUnaryOp((imbyte*)src_image->data[0], (float*)dst_image->data[0], total_count, op);
+ else if (dst_image->data_type == IM_INT)
+ DoUnaryOp((imbyte*)src_image->data[0], (int*)dst_image->data[0], total_count, op);
+ else if (dst_image->data_type == IM_USHORT)
+ DoUnaryOp((imbyte*)src_image->data[0], (imushort*)dst_image->data[0], total_count, op);
+ else
+ DoUnaryOp((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], total_count, op);
+ break;
+ case IM_USHORT:
+ if (dst_image->data_type == IM_BYTE)
+ DoUnaryOp((imushort*)src_image->data[0], (imbyte*)dst_image->data[0], total_count, op);
+ else if (dst_image->data_type == IM_INT)
+ DoUnaryOp((imushort*)src_image->data[0], (int*)dst_image->data[0], total_count, op);
+ else if (dst_image->data_type == IM_FLOAT)
+ DoUnaryOp((imushort*)src_image->data[0], (float*)dst_image->data[0], total_count, op);
+ else
+ DoUnaryOp((imushort*)src_image->data[0], (imushort*)dst_image->data[0], total_count, op);
+ break;
+ case IM_INT:
+ if (dst_image->data_type == IM_BYTE)
+ DoUnaryOp((int*)src_image->data[0], (imbyte*)dst_image->data[0], total_count, op);
+ else if (dst_image->data_type == IM_USHORT)
+ DoUnaryOp((int*)src_image->data[0], (imushort*)dst_image->data[0], total_count, op);
+ else if (dst_image->data_type == IM_FLOAT)
+ DoUnaryOp((int*)src_image->data[0], (float*)dst_image->data[0], total_count, op);
+ else
+ DoUnaryOp((int*)src_image->data[0], (int*)dst_image->data[0], total_count, op);
+ break;
+ case IM_FLOAT:
+ DoUnaryOp((float*)src_image->data[0], (float*)dst_image->data[0], total_count, op);
+ break;
+ case IM_CFLOAT:
+ DoUnaryOp((imcfloat*)src_image->data[0], (imcfloat*)dst_image->data[0], total_count, op);
+ break;
+ }
+}
+
+void imProcessSplitComplex(const imImage* image, imImage* NewImage1, imImage* NewImage2, int polar)
+{
+ int total_count = image->count*image->depth;
+
+ imcfloat* map = (imcfloat*)image->data[0];
+ float* map1 = (float*)NewImage1->data[0];
+ float* map2 = (float*)NewImage2->data[0];
+
+ for (int i = 0; i < total_count; i++)
+ {
+ if (polar)
+ {
+ map1[i] = cpxmag(map[i]);
+ map2[i] = cpxphase(map[i]);
+ }
+ else
+ {
+ map1[i] = map[i].real;
+ map2[i] = map[i].imag;
+ }
+ }
+}
+
+void imProcessMergeComplex(const imImage* image1, const imImage* image2, imImage* NewImage, int polar)
+{
+ int total_count = image1->count*image1->depth;
+
+ imcfloat* map = (imcfloat*)NewImage->data[0];
+ float* map1 = (float*)image1->data[0];
+ float* map2 = (float*)image2->data[0];
+
+ for (int i = 0; i < total_count; i++)
+ {
+ if (polar)
+ {
+ float phase = map2[i];
+ if (phase > 180) phase -= 360;
+ phase /= 57.2957795f;
+
+ map[i].real = (float)(map1[i] * cos(phase));
+ map[i].imag = (float)(map1[i] * sin(phase));
+ }
+ else
+ {
+ map[i].real = map1[i];
+ map[i].imag = map2[i];
+ }
+ }
+}
diff --git a/src/process/im_canny.cpp b/src/process/im_canny.cpp
new file mode 100644
index 0000000..d749fc0
--- /dev/null
+++ b/src/process/im_canny.cpp
@@ -0,0 +1,254 @@
+/** \file
+ * \brief Canny Edge Detector
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_canny.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+#include <im.h>
+
+#include "im_process_loc.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <memory.h>
+
+/* Scale floating point magnitudes to 8 bits */
+static float MAG_SCALE;
+
+/* Biggest possible filter mask */
+#define MAX_MASK_SIZE 100
+
+static float ** f2d (int nr, int nc);
+static float gauss(float x, float sigma);
+static float dGauss (float x, float sigma);
+static float meanGauss (float x, float sigma);
+static void seperable_convolution (const imImage* im, float *gau, int width, float **smx, float **smy);
+static void dxy_seperable_convolution (float** im, int nr, int nc, float *gau, int width, float **sm, int which);
+static void nonmax_suppress (float **dx, float **dy, imImage* mag);
+
+void imProcessCanny(const imImage* im, imImage* NewImage, float stddev)
+{
+ int width = 1;
+ float **smx,**smy;
+ float **dx,**dy;
+ int i;
+ float gau[MAX_MASK_SIZE], dgau[MAX_MASK_SIZE];
+
+/* Create a Gaussian and a derivative of Gaussian filter mask */
+ for(i=0; i<MAX_MASK_SIZE; i++)
+ {
+ gau[i] = meanGauss ((float)i, stddev);
+ if (gau[i] < 0.005)
+ {
+ width = i;
+ break;
+ }
+ dgau[i] = dGauss ((float)i, stddev);
+ }
+
+ smx = f2d (im->height, im->width);
+ smy = f2d (im->height, im->width);
+
+/* Convolution of source image with a Gaussian in X and Y directions */
+ seperable_convolution (im, gau, width, smx, smy);
+
+ MAG_SCALE = 0;
+
+/* Now convolve smoothed data with a derivative */
+ dx = f2d (im->height, im->width);
+ dxy_seperable_convolution (smx, im->height, im->width, dgau, width, dx, 1);
+ free(smx[0]); free(smx);
+
+ dy = f2d (im->height, im->width);
+ dxy_seperable_convolution (smy, im->height, im->width, dgau, width, dy, 0);
+ free(smy[0]); free(smy);
+
+ if (MAG_SCALE)
+ MAG_SCALE = 255.0f/(1.4142f*MAG_SCALE);
+
+ /* Non-maximum suppression - edge pixels should be a local max */
+ nonmax_suppress (dx, dy, NewImage);
+
+ free(dx[0]); free(dx);
+ free(dy[0]); free(dy);
+}
+
+static float norm (float x, float y)
+{
+ return (float) sqrt ( (double)(x*x + y*y) );
+}
+
+static float ** f2d (int nr, int nc)
+{
+ float **x, *y;
+ int i;
+
+ x = (float **)calloc ( nr, sizeof (float *) );
+ if (!x)
+ return NULL;
+
+ y = (float *)calloc ( nr*nc, sizeof (float) );
+ if (!y)
+ return NULL;
+
+ for (i=0; i<nr; i++)
+ {
+ x[i] = y + i*nc;
+ }
+
+ return x;
+}
+
+/* Gaussian */
+static float gauss(float x, float sigma)
+{
+ return (float)exp((double) ((-x*x)/(2*sigma*sigma)));
+}
+
+static float meanGauss (float x, float sigma)
+{
+ float z;
+ z = (gauss(x,sigma)+gauss(x+0.5f,sigma)+gauss(x-0.5f,sigma))/3.0f;
+// z = z/(3.1415f*2.0f*sigma*sigma);
+ return z;
+}
+
+/* First derivative of Gaussian */
+static float dGauss (float x, float sigma)
+{
+// return -x/(sigma*sigma) * gauss(x, sigma);
+ return -x * gauss(x, sigma);
+}
+
+static void seperable_convolution (const imImage* im, float *gau, int width, float **smx, float **smy)
+{
+ int i,j,k, I1, I2, nr, nc;
+ float x, y;
+ unsigned char* im_data = (unsigned char*)im->data[0];
+
+ nr = im->height;
+ nc = im->width;
+
+ for (i=0; i<nr; i++)
+ {
+ for (j=0; j<nc; j++)
+ {
+ x = gau[0] * im_data[i*im->width + j]; y = gau[0] * im_data[i*im->width + j];
+ for (k=1; k<width; k++)
+ {
+ I1 = (i+k)%nr; I2 = (i-k+nr)%nr;
+ y += gau[k]*im_data[I1*im->width + j] + gau[k]*im_data[I2*im->width + j];
+ I1 = (j+k)%nc; I2 = (j-k+nc)%nc;
+ x += gau[k]*im_data[i*im->width + I1] + gau[k]*im_data[i*im->width + I2];
+ }
+ smx[i][j] = x; smy[i][j] = y;
+ }
+ }
+}
+
+static void dxy_seperable_convolution (float** im, int nr, int nc, float *gau, int width, float **sm, int which)
+{
+ int i,j,k, I1, I2;
+ float x;
+
+ for (i=0; i<nr; i++)
+ {
+ for (j=0; j<nc; j++)
+ {
+ x = 0.0;
+ for (k=1; k<width; k++)
+ {
+ if (which == 0)
+ {
+ I1 = (i+k)%nr; I2 = (i-k+nr)%nr;
+ x += -gau[k]*im[I1][j] + gau[k]*im[I2][j];
+ }
+ else
+ {
+ I1 = (j+k)%nc; I2 = (j-k+nc)%nc;
+ x += -gau[k]*im[i][I1] + gau[k]*im[i][I2];
+ }
+ }
+ sm[i][j] = x;
+
+ if (x > MAG_SCALE)
+ MAG_SCALE = x;
+ }
+ }
+}
+
+static unsigned char tobyte(float x)
+{
+ if (x > 255) return 255;
+ return (unsigned char)x;
+}
+
+static void nonmax_suppress (float **dx, float **dy, imImage* mag)
+{
+ int i,j;
+ float xx, yy, g2, g1, g3, g4, g, xc, yc;
+ unsigned char* mag_data = (unsigned char*)mag->data[0];
+
+ for (i=1; i<mag->height-1; i++)
+ {
+ for (j=1; j<mag->width-1; j++)
+ {
+ /* Treat the x and y derivatives as components of a vector */
+ xc = dx[i][j];
+ yc = dy[i][j];
+ if (fabs(xc)<0.01 && fabs(yc)<0.01) continue;
+
+ g = norm (xc, yc);
+
+ /* Follow the gradient direction, as indicated by the direction of
+ the vector (xc, yc); retain pixels that are a local maximum. */
+
+ if (fabs(yc) > fabs(xc))
+ {
+ /* The Y component is biggest, so gradient direction is basically UP/DOWN */
+ xx = (float)(fabs(xc)/fabs(yc));
+ yy = 1.0;
+
+ g2 = norm (dx[i-1][j], dy[i-1][j]);
+ g4 = norm (dx[i+1][j], dy[i+1][j]);
+ if (xc*yc > 0.0)
+ {
+ g3 = norm (dx[i+1][j+1], dy[i+1][j+1]);
+ g1 = norm (dx[i-1][j-1], dy[i-1][j-1]);
+ }
+ else
+ {
+ g3 = norm (dx[i+1][j-1], dy[i+1][j-1]);
+ g1 = norm (dx[i-1][j+1], dy[i-1][j+1]);
+ }
+
+ }
+ else
+ {
+ /* The X component is biggest, so gradient direction is basically LEFT/RIGHT */
+ xx = (float)(fabs(yc)/fabs(xc));
+ yy = 1.0;
+
+ g2 = norm (dx[i][j+1], dy[i][j+1]);
+ g4 = norm (dx[i][j-1], dy[i][j-1]);
+ if (xc*yc > 0.0)
+ {
+ g3 = norm (dx[i-1][j-1], dy[i-1][j-1]);
+ g1 = norm (dx[i+1][j+1], dy[i+1][j+1]);
+ }
+ else
+ {
+ g1 = norm (dx[i-1][j+1], dy[i-1][j+1]);
+ g3 = norm (dx[i+1][j-1], dy[i+1][j-1]);
+ }
+ }
+
+ /* Compute the interpolated value of the gradient magnitude */
+ if ( (g > (xx*g1 + (yy-xx)*g2)) && (g > (xx*g3 + (yy-xx)*g4)) )
+ {
+ mag_data[i*mag->width + j] = tobyte(g*MAG_SCALE);
+ }
+ }
+ }
+}
diff --git a/src/process/im_color.cpp b/src/process/im_color.cpp
new file mode 100644
index 0000000..b27d4b3
--- /dev/null
+++ b/src/process/im_color.cpp
@@ -0,0 +1,255 @@
+/** \file
+ * \brief Color Processing Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_color.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+#include <im.h>
+#include <im_util.h>
+#include <im_colorhsi.h>
+#include <im_palette.h>
+
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+
+static void rgb2yrgb(imbyte* r, imbyte* g, imbyte* b, imbyte* y)
+{
+ int ri,gi,bi;
+
+ *y = (imbyte)((299*(*r) + 587*(*g) + 114*(*b)) / 1000);
+ ri = (*r) - (*y) + 128;
+ gi = (*g) - (*y) + 128;
+ bi = (*b) - (*y) + 128;
+
+ if (ri < 0) ri = 0;
+ if (gi < 0) gi = 0;
+ if (bi < 0) bi = 0;
+
+ *r = (imbyte)ri;
+ *g = (imbyte)gi;
+ *b = (imbyte)bi;
+}
+
+void imProcessSplitYChroma(const imImage* src_image, imImage* y_image, imImage* chroma_image)
+{
+ imbyte Y,
+ *red=(imbyte*)src_image->data[0],
+ *green=(imbyte*)src_image->data[1],
+ *blue=(imbyte*)src_image->data[2],
+ *red2=(imbyte*)chroma_image->data[0],
+ *green2=(imbyte*)chroma_image->data[1],
+ *blue2=(imbyte*)chroma_image->data[2],
+ *map1=(imbyte*)y_image->data[0];
+
+ for (int i = 0; i < src_image->count; i++)
+ {
+ imbyte R = red[i];
+ imbyte G = green[i];
+ imbyte B = blue[i];
+
+ rgb2yrgb(&R, &G, &B, &Y);
+
+ map1[i] = Y;
+
+ red2[i] = R;
+ green2[i] = G;
+ blue2[i] = B;
+ }
+}
+
+static void DoSplitHSIFloat(float** data, float* hue, float* saturation, float* intensity, int count)
+{
+ float *red=data[0],
+ *green=data[1],
+ *blue=data[2];
+
+ for (int i = 0; i < count; i++)
+ {
+ imColorRGB2HSI(red[i], green[i], blue[i], &hue[i], &saturation[i], &intensity[i]);
+ }
+}
+
+static void DoSplitHSIByte(imbyte** data, float* hue, float* saturation, float* intensity, int count)
+{
+ imbyte *red=data[0],
+ *green=data[1],
+ *blue=data[2];
+
+ for (int i = 0; i < count; i++)
+ {
+ imColorRGB2HSIbyte(red[i], green[i], blue[i], &hue[i], &saturation[i], &intensity[i]);
+ }
+}
+
+void imProcessSplitHSI(const imImage* image, imImage* image1, imImage* image2, imImage* image3)
+{
+ switch(image->data_type)
+ {
+ case IM_BYTE:
+ DoSplitHSIByte((imbyte**)image->data, (float*)image1->data[0], (float*)image2->data[0], (float*)image3->data[0], image->count);
+ break;
+ case IM_FLOAT:
+ DoSplitHSIFloat((float**)image->data, (float*)image1->data[0], (float*)image2->data[0], (float*)image3->data[0], image->count);
+ break;
+ }
+
+ imImageSetPalette(image1, imPaletteHues(), 256);
+}
+
+static void DoMergeHSIFloat(float** data, float* hue, float* saturation, float* intensity, int count)
+{
+ float *red=data[0],
+ *green=data[1],
+ *blue=data[2];
+
+ for (int i = 0; i < count; i++)
+ {
+ imColorHSI2RGB(hue[i], saturation[i], intensity[i], &red[i], &green[i], &blue[i]);
+ }
+}
+
+static void DoMergeHSIByte(imbyte** data, float* hue, float* saturation, float* intensity, int count)
+{
+ imbyte *red=data[0],
+ *green=data[1],
+ *blue=data[2];
+
+ for (int i = 0; i < count; i++)
+ {
+ imColorHSI2RGBbyte(hue[i], saturation[i], intensity[i], &red[i], &green[i], &blue[i]);
+ }
+}
+
+void imProcessMergeHSI(const imImage* image1, const imImage* image2, const imImage* image3, imImage* image)
+{
+ switch(image->data_type)
+ {
+ case IM_BYTE:
+ DoMergeHSIByte((imbyte**)image->data, (float*)image1->data[0], (float*)image2->data[0], (float*)image3->data[0], image->count);
+ break;
+ case IM_FLOAT:
+ DoMergeHSIFloat((float**)image->data, (float*)image1->data[0], (float*)image2->data[0], (float*)image3->data[0], image->count);
+ break;
+ }
+}
+
+void imProcessSplitComponents(const imImage* src_image, imImage** dst_image)
+{
+ memcpy(dst_image[0]->data[0], src_image->data[0], src_image->plane_size);
+ memcpy(dst_image[1]->data[0], src_image->data[1], src_image->plane_size);
+ memcpy(dst_image[2]->data[0], src_image->data[2], src_image->plane_size);
+ if (imColorModeDepth(src_image->color_space) == 4)
+ memcpy(dst_image[3]->data[0], src_image->data[3], src_image->plane_size);
+}
+
+void imProcessMergeComponents(const imImage** src_image, imImage* dst_image)
+{
+ memcpy(dst_image->data[0], src_image[0]->data[0], dst_image->plane_size);
+ memcpy(dst_image->data[1], src_image[1]->data[0], dst_image->plane_size);
+ memcpy(dst_image->data[2], src_image[2]->data[0], dst_image->plane_size);
+ if (imColorModeDepth(dst_image->color_space) == 4)
+ memcpy(dst_image->data[3], src_image[3]->data[0], dst_image->plane_size);
+}
+
+template <class T>
+static void DoNormalizeComp(T** src_data, float** dst_data, int count, int depth)
+{
+ int d;
+ T* src_pdata[4];
+ float* dst_pdata[4];
+
+ for(d = 0; d < depth; d++)
+ {
+ dst_pdata[d] = dst_data[d];
+ src_pdata[d] = src_data[d];
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ float sum = 0;
+ for(d = 0; d < depth; d++)
+ sum += (float)*(src_pdata[d]);
+
+ for(d = 0; d < depth; d++)
+ {
+ if (sum == 0)
+ *(dst_pdata[d]) = 0;
+ else
+ *(dst_pdata[d]) = (float)*(src_pdata[d]) / sum;
+
+ dst_pdata[d]++;
+ src_pdata[d]++;
+ }
+ }
+}
+
+void imProcessNormalizeComponents(const imImage* src_image, imImage* dst_image)
+{
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ DoNormalizeComp((imbyte**)src_image->data, (float**)dst_image->data, src_image->count, src_image->depth);
+ break;
+ case IM_USHORT:
+ DoNormalizeComp((imushort**)src_image->data, (float**)dst_image->data, src_image->count, src_image->depth);
+ break;
+ case IM_INT:
+ DoNormalizeComp((int**)src_image->data, (float**)dst_image->data, src_image->count, src_image->depth);
+ break;
+ case IM_FLOAT:
+ DoNormalizeComp((float**)src_image->data, (float**)dst_image->data, src_image->count, src_image->depth);
+ break;
+ }
+}
+
+template <class T>
+static void DoReplaceColor(T *src_data, T *dst_data, int width, int height, int depth, float* src_color, float* dst_color)
+{
+ int d, count = width*height;
+ for (int i = 0; i < count; i++)
+ {
+ int equal = 1;
+ for (d = 0; d < depth; d++)
+ {
+ if (*(src_data+d*count) != (T)src_color[d])
+ {
+ equal = 0;
+ break;
+ }
+ }
+
+ for (d = 0; d < depth; d++)
+ {
+ if (equal)
+ *(dst_data+d*count) = (T)dst_color[d];
+ else
+ *(dst_data+d*count) = *(src_data+d*count);
+ }
+
+ src_data++;
+ dst_data++;
+ }
+}
+
+void imProcessReplaceColor(const imImage* src_image, imImage* dst_image, float* src_color, float* dst_color)
+{
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ DoReplaceColor((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], src_image->width, src_image->height, src_image->depth, src_color, dst_color);
+ break;
+ case IM_USHORT:
+ DoReplaceColor((imushort*)src_image->data[0], (imushort*)dst_image->data[0], src_image->width, src_image->height, src_image->depth, src_color, dst_color);
+ break;
+ case IM_INT:
+ DoReplaceColor((int*)src_image->data[0], (int*)dst_image->data[0], src_image->width, src_image->height, src_image->depth, src_color, dst_color);
+ break;
+ case IM_FLOAT:
+ DoReplaceColor((float*)src_image->data[0], (float*)dst_image->data[0], src_image->width, src_image->height, src_image->depth, src_color, dst_color);
+ break;
+ }
+}
diff --git a/src/process/im_convolve.cpp b/src/process/im_convolve.cpp
new file mode 100644
index 0000000..bca2dcd
--- /dev/null
+++ b/src/process/im_convolve.cpp
@@ -0,0 +1,1512 @@
+/** \file
+ * \brief Convolution Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_convolve.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_counter.h>
+#include <im_complex.h>
+#include <im_math_op.h>
+#include <im_image.h>
+#include <im_kernel.h>
+
+#include "im_process_loc.h"
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+/* Rotating Kernels
+3x3
+ 6 7 8 7 8 5
+ 3 4 5 6 4 2
+ 0 1 2 3 0 1
+
+5x5
+ 20 21 22 23 24 22 23 24 19 14
+ 15 16 17 18 19 21 17 18 13 9
+ 10 11 12 13 14 20 16 12 8 4
+ 5 6 7 8 9 15 11 6 7 3
+ 0 1 2 3 4 10 5 0 1 2
+
+7x7
+ 42 43 44 45 46 47 48 45 46 47 48 41 34 27
+ 35 36 37 38 39 40 41 44 38 39 40 33 26 20
+ 28 29 30 31 32 33 34 43 37 31 32 25 19 13
+ 21 22 23 24 25 26 27 42 36 30 24 18 12 6
+ 14 15 16 17 18 19 20 35 29 23 16 17 11 5
+ 7 8 9 10 11 12 13 28 22 15 8 9 10 4
+ 0 1 2 3 4 5 6 21 14 7 0 1 2 3
+
+ TO DO: a generic odd rotation function...
+*/
+
+template <class KT>
+static void iRotateKernel(KT* kernel_map, int kernel_size)
+{
+ KT temp;
+
+ switch (kernel_size)
+ {
+ case 3:
+ {
+ temp = kernel_map[0];
+ kernel_map[0] = kernel_map[3];
+ kernel_map[3] = kernel_map[6];
+ kernel_map[6] = kernel_map[7];
+ kernel_map[7] = kernel_map[8];
+ kernel_map[8] = kernel_map[5];
+ kernel_map[5] = kernel_map[2];
+ kernel_map[2] = kernel_map[1];
+ kernel_map[1] = temp;
+ }
+ break;
+ case 5:
+ {
+ temp = kernel_map[0];
+ kernel_map[0] = kernel_map[10];
+ kernel_map[10] = kernel_map[20];
+ kernel_map[20] = kernel_map[22];
+ kernel_map[22] = kernel_map[24];
+ kernel_map[24] = kernel_map[14];
+ kernel_map[14] = kernel_map[4];
+ kernel_map[4] = kernel_map[2];
+ kernel_map[2] = temp;
+
+ temp = kernel_map[5];
+ kernel_map[5] = kernel_map[15];
+ kernel_map[15] = kernel_map[21];
+ kernel_map[21] = kernel_map[23];
+ kernel_map[23] = kernel_map[19];
+ kernel_map[19] = kernel_map[9];
+ kernel_map[9] = kernel_map[3];
+ kernel_map[3] = kernel_map[1];
+ kernel_map[1] = temp;
+
+ temp = kernel_map[6];
+ kernel_map[6] = kernel_map[11];
+ kernel_map[11] = kernel_map[16];
+ kernel_map[16] = kernel_map[17];
+ kernel_map[17] = kernel_map[18];
+ kernel_map[18] = kernel_map[13];
+ kernel_map[13] = kernel_map[8];
+ kernel_map[8] = kernel_map[7];
+ kernel_map[7] = temp;
+ }
+ break;
+ case 7:
+ {
+ temp = kernel_map[2];
+ kernel_map[2] = kernel_map[7];
+ kernel_map[7] = kernel_map[28];
+ kernel_map[28] = kernel_map[43];
+ kernel_map[43] = kernel_map[46];
+ kernel_map[46] = kernel_map[41];
+ kernel_map[41] = kernel_map[20];
+ kernel_map[20] = kernel_map[5];
+ kernel_map[5] = temp;
+
+ temp = kernel_map[1];
+ kernel_map[1] = kernel_map[14];
+ kernel_map[14] = kernel_map[35];
+ kernel_map[35] = kernel_map[44];
+ kernel_map[44] = kernel_map[47];
+ kernel_map[47] = kernel_map[34];
+ kernel_map[34] = kernel_map[13];
+ kernel_map[13] = kernel_map[4];
+ kernel_map[4] = temp;
+
+ temp = kernel_map[0];
+ kernel_map[0] = kernel_map[21];
+ kernel_map[21] = kernel_map[42];
+ kernel_map[42] = kernel_map[45];
+ kernel_map[45] = kernel_map[48];
+ kernel_map[48] = kernel_map[27];
+ kernel_map[27] = kernel_map[6];
+ kernel_map[6] = kernel_map[3];
+ kernel_map[3] = temp;
+
+ temp = kernel_map[9];
+ kernel_map[9] = kernel_map[15];
+ kernel_map[15] = kernel_map[29];
+ kernel_map[29] = kernel_map[37];
+ kernel_map[37] = kernel_map[39];
+ kernel_map[39] = kernel_map[33];
+ kernel_map[33] = kernel_map[19];
+ kernel_map[19] = kernel_map[11];
+ kernel_map[11] = temp;
+
+ temp = kernel_map[8];
+ kernel_map[8] = kernel_map[22];
+ kernel_map[22] = kernel_map[36];
+ kernel_map[36] = kernel_map[38];
+ kernel_map[38] = kernel_map[40];
+ kernel_map[40] = kernel_map[26];
+ kernel_map[26] = kernel_map[12];
+ kernel_map[12] = kernel_map[10];
+ kernel_map[10] = temp;
+
+ temp = kernel_map[16];
+ kernel_map[16] = kernel_map[23];
+ kernel_map[23] = kernel_map[30];
+ kernel_map[30] = kernel_map[31];
+ kernel_map[31] = kernel_map[32];
+ kernel_map[32] = kernel_map[25];
+ kernel_map[25] = kernel_map[18];
+ kernel_map[18] = kernel_map[17];
+ kernel_map[17] = temp;
+ }
+ break;
+ }
+}
+
+void imProcessRotateKernel(imImage* kernel)
+{
+ if (kernel->data_type == IM_INT)
+ iRotateKernel((int*)kernel->data[0], kernel->width);
+ else
+ iRotateKernel((float*)kernel->data[0], kernel->width);
+}
+
+template <class T, class KT, class CT>
+static int DoCompassConvolve(T* map, T* new_map, int width, int height, KT* orig_kernel_map, int kernel_size, int counter, CT)
+{
+ CT value;
+ KT total, *kernel_line, kvalue;
+ int offset, new_offset, i, j, x, y, kcount;
+
+ // duplicate the kernel data so we can rotate it
+ kcount = kernel_size*kernel_size;
+ KT* kernel_map = (KT*)malloc(kcount*sizeof(KT));
+
+ int ks2 = kernel_size/2;
+
+ total = 0;
+ for(j = 0; j < kcount; j++)
+ {
+ kvalue = orig_kernel_map[j];
+ kernel_map[j] = kvalue;
+ total += kvalue;
+ }
+
+ if (total == 0)
+ total = 1;
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ CT max_value = 0;
+
+ for(int k = 0; k < 8; k++) // Rotate 8 times
+ {
+ value = 0;
+
+ for(y = -ks2; y <= ks2; y++)
+ {
+ kernel_line = kernel_map + (y+ks2)*kernel_size;
+
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ for(x = -ks2; x <= ks2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value += kernel_line[x+ks2] * map[offset - (i + x + 1)];
+ else if (i + x >= width) // pass the right border
+ value += kernel_line[x+ks2] * map[offset + 2*width - 1 - (i + x)];
+ else if (offset != -1)
+ value += kernel_line[x+ks2] * map[offset + (i + x)];
+ }
+ }
+
+ if (abs_op(value) > max_value)
+ max_value = abs_op(value);
+
+ iRotateKernel(kernel_map, kernel_size);
+ }
+
+ max_value /= total;
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ new_map[new_offset + i] = (T)IM_BYTECROP(max_value);
+ else
+ new_map[new_offset + i] = (T)max_value;
+ }
+
+ if (!imCounterInc(counter))
+ {
+ free(kernel_map);
+ return 0;
+ }
+ }
+
+ free(kernel_map);
+ return 1;
+}
+
+int imProcessCompassConvolve(const imImage* src_image, imImage* dst_image, imImage *kernel)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("Compass Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel, "Description", NULL, NULL);
+ if (!msg) msg = "Filtering...";
+ imCounterTotal(counter, src_image->depth*src_image->height, msg);
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ if (kernel->data_type == IM_INT)
+ ret = DoCompassConvolve((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, counter, (int)0);
+ else
+ ret = DoCompassConvolve((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, counter, (float)0);
+ break;
+ case IM_USHORT:
+ if (kernel->data_type == IM_INT)
+ ret = DoCompassConvolve((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, counter, (int)0);
+ else
+ ret = DoCompassConvolve((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, counter, (float)0);
+ break;
+ case IM_INT:
+ if (kernel->data_type == IM_INT)
+ ret = DoCompassConvolve((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, counter, (int)0);
+ else
+ ret = DoCompassConvolve((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, counter, (float)0);
+ break;
+ case IM_FLOAT:
+ if (kernel->data_type == IM_INT)
+ ret = DoCompassConvolve((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, counter, (float)0);
+ else
+ ret = DoCompassConvolve((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, counter, (float)0);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+template <class T, class KT, class CT>
+static int DoConvolveDual(T* map, T* new_map, int width, int height, KT* kernel_map1, KT* kernel_map2, int kernel_width, int kernel_height, int counter, CT)
+{
+ CT value1, value2, value;
+ KT total1, total2, *kernel_line;
+ int offset, new_offset, i, j, x, y;
+
+ int kh2 = kernel_height/2;
+ int kw2 = kernel_width/2;
+
+ if (kernel_height % 2 == 0) kh2--;
+ if (kernel_width % 2 == 0) kw2--;
+
+ total1 = 0;
+ for(j = 0; j < kernel_height; j++)
+ {
+ for(i = 0; i < kernel_width; i++)
+ total1 += kernel_map1[j*kernel_width + i];
+ }
+
+ if (total1 == 0)
+ total1 = 1;
+
+ total2 = 0;
+ for(j = 0; j < kernel_height; j++)
+ {
+ for(i = 0; i < kernel_width; i++)
+ total2 += kernel_map2[j*kernel_width + i];
+ }
+
+ if (total2 == 0)
+ total2 = 1;
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ value1 = 0;
+ value2 = 0;
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ kernel_line = kernel_map1 + (y+kh2)*kernel_width;
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value1 += kernel_line[x+kw2] * map[offset - (i + x + 1)];
+ else if (i + x >= width) // pass the right border
+ value1 += kernel_line[x+kw2] * map[offset + 2*width - 1 - (i + x)];
+ else if (offset != -1)
+ value1 += kernel_line[x+kw2] * map[offset + (i + x)];
+ }
+
+ kernel_line = kernel_map2 + (y+kh2)*kernel_width;
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value2 += kernel_line[x+kw2] * map[offset - (i + x + 1)];
+ else if (i + x >= width) // pass the right border
+ value2 += kernel_line[x+kw2] * map[offset + 2*width - 1 - (i + x)];
+ else if (offset != -1)
+ value2 += kernel_line[x+kw2] * map[offset + (i + x)];
+ }
+ }
+
+ value1 /= total1;
+ value2 /= total2;
+
+ value = (CT)sqrt((double)(value1*value1 + value2*value2));
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ new_map[new_offset + i] = (T)IM_BYTECROP(value);
+ else
+ new_map[new_offset + i] = (T)value;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+template <class KT>
+static int DoConvolveDualCpx(imcfloat* map, imcfloat* new_map, int width, int height, KT* kernel_map1, KT* kernel_map2, int kernel_width, int kernel_height, int counter)
+{
+ imcfloat value1, value2;
+ KT total1, total2, *kernel_line;
+ int offset, new_offset, i, j, x, y;
+
+ int kh2 = kernel_height/2;
+ int kw2 = kernel_width/2;
+
+ if (kernel_height % 2 == 0) kh2--;
+ if (kernel_width % 2 == 0) kw2--;
+
+ total1 = 0;
+ for(j = 0; j < kernel_height; j++)
+ {
+ for(i = 0; i < kernel_width; i++)
+ total1 += kernel_map1[j*kernel_width + i];
+ }
+
+ if (total1 == 0)
+ total1 = 1;
+
+ total2 = 0;
+ for(j = 0; j < kernel_height; j++)
+ {
+ for(i = 0; i < kernel_width; i++)
+ total2 += kernel_map1[j*kernel_width + i];
+ }
+
+ if (total2 == 0)
+ total2 = 1;
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ value1 = 0;
+ value2 = 0;
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ kernel_line = kernel_map1 + (y+kh2)*kernel_width;
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value1 += map[offset - (i + x + 1)] * (float)kernel_line[x+kw2];
+ else if (i + x >= width) // pass the right border
+ value1 += map[offset + 2*width - 1 - (i + x)] * (float)kernel_line[x+kw2];
+ else if (offset != -1)
+ value1 += map[offset + (i + x)] * (float)kernel_line[x+kw2];
+ }
+
+ kernel_line = kernel_map2 + (y+kh2)*kernel_width;
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value2 += map[offset - (i + x + 1)] * (float)kernel_line[x+kw2];
+ else if (i + x >= width) // pass the right border
+ value2 += map[offset + 2*width - 1 - (i + x)] * (float)kernel_line[x+kw2];
+ else if (offset != -1)
+ value2 += map[offset + (i + x)] * (float)kernel_line[x+kw2];
+ }
+ }
+
+ value1 /= (float)total1;
+ value2 /= (float)total2;
+
+ new_map[new_offset + i] = sqrt(value1*value1 + value2*value2);
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+int imProcessConvolveDual(const imImage* src_image, imImage* dst_image, const imImage *kernel1, const imImage *kernel2)
+{
+ int counter = imCounterBegin("Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel1, "Description", NULL, NULL);
+ if (!msg) msg = "Filtering...";
+ imCounterTotal(counter, src_image->depth*src_image->height, msg);
+
+ int ret = 0;
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ if (kernel1->data_type == IM_INT)
+ ret = DoConvolveDual((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel1->data[0], (int*)kernel2->data[0], kernel1->width, kernel1->height, counter, (int)0);
+ else
+ ret = DoConvolveDual((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel1->data[0], (float*)kernel2->data[0], kernel1->width, kernel1->height, counter, (float)0);
+ break;
+ case IM_USHORT:
+ if (kernel1->data_type == IM_INT)
+ ret = DoConvolveDual((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel1->data[0], (int*)kernel2->data[0], kernel1->width, kernel1->height, counter, (int)0);
+ else
+ ret = DoConvolveDual((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel1->data[0], (float*)kernel2->data[0], kernel1->width, kernel1->height, counter, (float)0);
+ break;
+ case IM_INT:
+ if (kernel1->data_type == IM_INT)
+ ret = DoConvolveDual((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel1->data[0], (int*)kernel2->data[0], kernel1->width, kernel1->height, counter, (int)0);
+ else
+ ret = DoConvolveDual((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel1->data[0], (float*)kernel2->data[0], kernel1->width, kernel1->height, counter, (float)0);
+ break;
+ case IM_FLOAT:
+ if (kernel1->data_type == IM_INT)
+ ret = DoConvolveDual((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel1->data[0], (int*)kernel2->data[0], kernel1->width, kernel1->height, counter, (float)0);
+ else
+ ret = DoConvolveDual((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel1->data[0], (float*)kernel2->data[0], kernel1->width, kernel1->height, counter, (float)0);
+ break;
+ case IM_CFLOAT:
+ if (kernel1->data_type == IM_INT)
+ ret = DoConvolveDualCpx((imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel1->data[0], (int*)kernel2->data[0], kernel1->width, kernel1->height, counter);
+ else
+ ret = DoConvolveDualCpx((imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel1->data[0], (float*)kernel2->data[0], kernel1->width, kernel1->height, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+template <class T, class KT, class CT>
+static int DoConvolve(T* map, T* new_map, int width, int height, KT* kernel_map, int kernel_width, int kernel_height, int counter, CT)
+{
+ CT value;
+ KT total, *kernel_line;
+ int offset, new_offset, i, j, x, y;
+
+ int kh2 = kernel_height/2;
+ int kw2 = kernel_width/2;
+
+ if (kernel_height % 2 == 0) kh2--;
+ if (kernel_width % 2 == 0) kw2--;
+
+ total = 0;
+ for(j = 0; j < kernel_height; j++)
+ {
+ for(i = 0; i < kernel_width; i++)
+ total += kernel_map[j*kernel_width + i];
+ }
+
+ if (total == 0)
+ total = 1;
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ value = 0;
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ kernel_line = kernel_map + (y+kh2)*kernel_width;
+
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value += kernel_line[x+kw2] * map[offset - (i + x + 1)];
+ else if (i + x >= width) // pass the right border
+ value += kernel_line[x+kw2] * map[offset + 2*width - 1 - (i + x)];
+ else if (offset != -1)
+ value += kernel_line[x+kw2] * map[offset + (i + x)];
+ }
+ }
+
+ value /= total;
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ new_map[new_offset + i] = (T)IM_BYTECROP(value);
+ else
+ new_map[new_offset + i] = (T)value;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+template <class KT>
+static int DoConvolveCpx(imcfloat* map, imcfloat* new_map, int width, int height, KT* kernel_map, int kernel_width, int kernel_height, int counter)
+{
+ imcfloat value;
+ KT total, *kernel_line;
+ int offset, new_offset, i, j, x, y;
+
+ int kh2 = kernel_height/2;
+ int kw2 = kernel_width/2;
+
+ if (kernel_height % 2 == 0) kh2--;
+ if (kernel_width % 2 == 0) kw2--;
+
+ total = 0;
+ for(j = 0; j < kernel_height; j++)
+ {
+ for(i = 0; i < kernel_width; i++)
+ total += kernel_map[j*kernel_width + i];
+ }
+
+ if (total == 0)
+ total = 1;
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ value = 0;
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ kernel_line = kernel_map + (y+kh2)*kernel_width;
+
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value += map[offset - (i + x + 1)] * (float)kernel_line[x+kw2];
+ else if (i + x >= width) // pass the right border
+ value += map[offset + 2*width - 1 - (i + x)] * (float)kernel_line[x+kw2];
+ else if (offset != -1)
+ value += map[offset + (i + x)] * (float)kernel_line[x+kw2];
+ }
+ }
+
+ value /= (float)total;
+
+ new_map[new_offset + i] = value;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int DoConvolveStep(const imImage* src_image, imImage* dst_image, const imImage *kernel, int counter)
+{
+ int ret = 0;
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolve((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (int)0);
+ else
+ ret = DoConvolve((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_USHORT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolve((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (int)0);
+ else
+ ret = DoConvolve((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_INT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolve((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (int)0);
+ else
+ ret = DoConvolve((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_FLOAT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolve((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ else
+ ret = DoConvolve((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_CFLOAT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolveCpx((imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter);
+ else
+ ret = DoConvolveCpx((imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ return ret;
+}
+
+int imProcessConvolve(const imImage* src_image, imImage* dst_image, const imImage *kernel)
+{
+ int counter = imCounterBegin("Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel, "Description", NULL, NULL);
+ if (!msg) msg = "Filtering...";
+ imCounterTotal(counter, src_image->depth*src_image->height, msg);
+
+ int ret = DoConvolveStep(src_image, dst_image, kernel, counter);
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+int imProcessConvolveRep(const imImage* src_image, imImage* dst_image, const imImage *kernel, int ntimes)
+{
+ imImage *AuxImage = imImageClone(dst_image);
+ if (!AuxImage)
+ return 0;
+
+ int counter = imCounterBegin("Repeated Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel, "Description", NULL, NULL);
+ if (!msg) msg = "Filtering...";
+ imCounterTotal(counter, src_image->depth*src_image->height*ntimes, msg);
+
+ const imImage *image1 = src_image;
+ imImage *image2 = dst_image;
+
+ for (int i = 0; i < ntimes; i++)
+ {
+ if (!DoConvolveStep(image1, image2, kernel, counter))
+ {
+ imCounterEnd(counter);
+ imImageDestroy(AuxImage);
+ return 0;
+ }
+
+ image1 = image2;
+
+ if (image1 == dst_image)
+ image2 = AuxImage;
+ else
+ image2 = dst_image;
+ }
+
+ // The result is in image1, if in the Aux swap the data
+ if (image1 == AuxImage)
+ {
+ void** temp = (void**)dst_image->data;
+ dst_image->data = AuxImage->data;
+ AuxImage->data = (void**)temp;
+ }
+
+ imCounterEnd(counter);
+ imImageDestroy(AuxImage);
+
+ return 1;
+}
+
+template <class T, class KT, class CT>
+static int DoConvolveSep(T* map, T* new_map, int width, int height, KT* kernel_map, int kernel_width, int kernel_height, int counter, CT)
+{
+ CT value;
+ KT totalV, totalH, *kernel_line;
+ T* aux_line;
+ int offset, new_offset, i, j;
+
+ int kh2 = kernel_height/2;
+ int kw2 = kernel_width/2;
+
+ if (kernel_height % 2 == 0) kh2--;
+ if (kernel_width % 2 == 0) kw2--;
+
+ // use only the first line and the first column of the kernel
+
+ totalV = 0;
+ for(j = 0; j < kernel_height; j++)
+ totalV += kernel_map[j*kernel_width];
+
+ if (totalV == 0)
+ totalV = 1;
+
+ totalH = 0;
+ for(i = 0; i < kernel_width; i++)
+ totalH += kernel_map[i];
+
+ if (totalH == 0)
+ totalH = 1;
+
+ aux_line = (T*)malloc(width*sizeof(T));
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ int y;
+ value = 0;
+
+ // first pass, only for columns
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ kernel_line = kernel_map + (y+kh2)*kernel_width;
+
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ if (offset != -1)
+ value += kernel_line[0] * map[offset + i];
+ }
+
+ value /= totalV;
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ new_map[new_offset + i] = (T)IM_BYTECROP(value);
+ else
+ new_map[new_offset + i] = (T)value;
+ }
+
+ if (!imCounterInc(counter))
+ {
+ free(aux_line);
+ return 0;
+ }
+ }
+
+ for(j = 0; j < height; j++)
+ {
+ offset = new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ int x;
+ value = 0;
+
+ // second pass, only for lines, but has to use an auxiliar buffer
+
+ kernel_line = kernel_map;
+
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value += kernel_line[x+kw2] * new_map[offset - (i + x + 1)];
+ else if (i + x >= width) // pass the right border
+ value += kernel_line[x+kw2] * new_map[offset + 2*width - 1 - (i + x)];
+ else
+ value += kernel_line[x+kw2] * new_map[offset + (i + x)];
+ }
+
+ value /= totalH;
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ aux_line[i] = (T)IM_BYTECROP(value);
+ else
+ aux_line[i] = (T)value;
+ }
+
+ memcpy(new_map + new_offset, aux_line, width*sizeof(T));
+
+ if (!imCounterInc(counter))
+ {
+ free(aux_line);
+ return 0;
+ }
+ }
+
+ free(aux_line);
+ return 1;
+}
+
+
+template <class KT>
+static int DoConvolveSepCpx(imcfloat* map, imcfloat* new_map, int width, int height, KT* kernel_map, int kernel_width, int kernel_height, int counter)
+{
+ imcfloat value;
+ KT totalV, totalH, *kernel_line;
+ imcfloat* aux_line;
+ int offset, new_offset, i, j;
+
+ int kh2 = kernel_height/2;
+ int kw2 = kernel_width/2;
+
+ if (kernel_height % 2 == 0) kh2--;
+ if (kernel_width % 2 == 0) kw2--;
+
+ // use only the first line and the first column of the kernel
+
+ totalV = 0;
+ for(j = 0; j < kernel_height; j++)
+ totalV += kernel_map[j*kernel_width];
+
+ if (totalV == 0)
+ totalV = 1;
+
+ totalH = 0;
+ for(i = 0; i < kernel_width; i++)
+ totalH += kernel_map[i];
+
+ if (totalH == 0)
+ totalH = 1;
+
+ aux_line = (imcfloat*)malloc(width*sizeof(imcfloat));
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ int y;
+ value = 0;
+
+ // first pass, only for columns
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ kernel_line = kernel_map + (y+kh2)*kernel_width;
+
+ if (j + y < 0) // pass the bottom border
+ offset = -(y + j + 1) * width;
+ else if (j + y >= height) // pass the top border
+ offset = (2*height - 1 - (j + y)) * width;
+ else
+ offset = (j + y) * width;
+
+ if (offset != -1)
+ value += map[offset + i] * (float)kernel_line[0];
+ }
+
+ value /= (float)totalV;
+
+ new_map[new_offset + i] = value;
+ }
+
+ if (!imCounterInc(counter))
+ {
+ free(aux_line);
+ return 0;
+ }
+ }
+
+ for(j = 0; j < height; j++)
+ {
+ offset = new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ int x;
+ value = 0;
+
+ // second pass, only for lines, but has to use an auxiliar buffer
+
+ kernel_line = kernel_map;
+
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (i + x < 0) // pass the left border
+ value += new_map[offset - (i + x + 1)] * (float)kernel_line[x+kw2];
+ else if (i + x >= width) // pass the right border
+ value += new_map[offset + 2*width - 1 - (i + x)] * (float)kernel_line[x+kw2];
+ else if (offset != -1)
+ value += new_map[offset + (i + x)] * (float)kernel_line[x+kw2];
+ }
+
+ value /= (float)totalH;
+
+ aux_line[i] = value;
+ }
+
+ memcpy(new_map + new_offset, aux_line, width*sizeof(imcfloat));
+
+ if (!imCounterInc(counter))
+ {
+ free(aux_line);
+ return 0;
+ }
+ }
+
+ free(aux_line);
+ return 1;
+}
+
+int imProcessConvolveSep(const imImage* src_image, imImage* dst_image, const imImage *kernel)
+{
+ int counter = imCounterBegin("Separable Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel, "Description", NULL, NULL);
+ if (!msg) msg = "Filtering...";
+ imCounterTotal(counter, 2*src_image->depth*src_image->height, msg);
+
+ int ret = 0;
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolveSep((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (int)0);
+ else
+ ret = DoConvolveSep((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_USHORT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolveSep((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (int)0);
+ else
+ ret = DoConvolveSep((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_INT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolveSep((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (int)0);
+ else
+ ret = DoConvolveSep((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_FLOAT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolveSep((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ else
+ ret = DoConvolveSep((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter, (float)0);
+ break;
+ case IM_CFLOAT:
+ if (kernel->data_type == IM_INT)
+ ret = DoConvolveSepCpx((imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], src_image->width, src_image->height, (int*)kernel->data[0], kernel->width, kernel->height, counter);
+ else
+ ret = DoConvolveSepCpx((imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], src_image->width, src_image->height, (float*)kernel->data[0], kernel->width, kernel->height, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+/*
+Description:
+ Can be used to find zero crossing of second derivative,
+ laplace. Can also be used to determine any other kind
+ of crossing. Pixels below or equal to 't' are set if the pixel
+ to the right or below is above 't', pixels above 't' are
+ set if the pixel to the right or below is below or equal to
+ 't'. Pixels that are "set" are set to the maximum absolute
+ difference of the two neighbours, to indicate the strength
+ of the edge.
+
+ | IF (crossing t)
+ | out(x,y) = MAX(ABS(in(x,y)-in(x+1,y)), ABS(in(x,y)-in(x,y+1)))
+ | ELSE
+ | out(x,y) = 0
+
+Author: Tor Lønnestad, BLAB, Ifi, UiO
+
+Copyright 1991, Blab, UiO
+Image processing lab, Department of Informatics
+University of Oslo
+*/
+template <class T>
+static void do_crossing(T* iband, T* oband, int width, int height, T t)
+{
+ int x, y, offset00 = 0, offset10 = 0, offset01 = 0;
+ T v, diff;
+
+ for (y=0; y < height-1; y++)
+ {
+ offset00 = y*width;
+ offset10 = (y+1)*width;
+ offset01 = offset00 + 1;
+
+ for (x=0; x < width-1; x++)
+ {
+ v = 0;
+
+ if (iband[offset00] <= t)
+ {
+ if (iband[offset10] > t)
+ v = iband[offset10]-iband[offset00];
+
+ if (iband[offset01] > t)
+ {
+ diff = iband[offset01]-iband[offset00];
+ if (diff > v) v = diff;
+ }
+ }
+ else
+ {
+ if (iband[offset10] <= t)
+ v = iband[offset00]-iband[offset10];
+
+ if (iband[offset01] <= t)
+ {
+ diff = iband[offset00]-iband[offset01];
+ if (diff > v) v = diff;
+ }
+ }
+
+ oband[offset00] = v;
+
+ offset00++;
+ offset10++;
+ offset01++;
+ }
+
+ /* last pixel on line */
+ offset00++;
+ offset10++;
+
+ v = 0;
+
+ if (iband[offset00] <= t)
+ {
+ if (iband[offset10] > t)
+ v = iband[offset10]-iband[offset00];
+ }
+ else
+ {
+ if (iband[offset10] <= t)
+ v = iband[offset00]-iband[offset10];
+ }
+
+ oband[offset00] = v;
+ }
+
+ /* last line */
+ offset00 = y*width;
+ offset01 = offset00 + 1;
+
+ for (x=0; x < width-1; x++)
+ {
+ v = 0;
+
+ if (iband[offset00] <= t)
+ {
+ if (iband[offset01] > t)
+ v = iband[offset01]-iband[offset00];
+ }
+ else
+ {
+ if (iband[offset01] <= t)
+ v = iband[offset00]-iband[offset01];
+ }
+
+ oband[offset00] = v;
+
+ offset00++;
+ offset01++;
+ }
+
+ offset00++;
+
+ /* last pixel */
+ oband[offset00] = 0;
+}
+
+void imProcessZeroCrossing(const imImage* src_image, imImage* dst_image)
+{
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_INT:
+ do_crossing((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, 0);
+ break;
+ case IM_FLOAT:
+ do_crossing((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, 0.0f);
+ break;
+ }
+ }
+}
+
+int imProcessBarlettConvolve(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_INT);
+ if (!kernel)
+ return 0;
+
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Barlett");
+
+ int* kernel_data = (int*)kernel->data[0];
+ int half = kernel_size / 2;
+ for (int i = 0; i < kernel_size; i++)
+ {
+ if (i <= half)
+ kernel_data[i] = i+1;
+ else
+ kernel_data[i] = kernel_size-i;
+ }
+ for (int j = 0; j < kernel_size; j++)
+ {
+ if (j <= half)
+ kernel_data[j*kernel_size] = j+1;
+ else
+ kernel_data[j*kernel_size] = kernel_size-j;
+ }
+
+ int ret = imProcessConvolveSep(src_image, dst_image, kernel);
+
+ imImageDestroy(kernel);
+
+ return ret;
+}
+
+int imProcessSobelConvolve(const imImage* src_image, imImage* dst_image)
+{
+ int ret = 0;
+
+ imImage* kernel1 = imKernelSobel();
+ imImage* kernel2 = imImageCreate(3, 3, IM_GRAY, IM_INT);
+ imProcessRotate90(kernel1, kernel2, 1);
+
+ ret = imProcessConvolveDual(src_image, dst_image, kernel1, kernel2);
+
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+
+ return ret;
+}
+
+int imProcessPrewittConvolve(const imImage* src_image, imImage* dst_image)
+{
+ int ret = 0;
+
+ imImage* kernel1 = imKernelPrewitt();
+ imImage* kernel2 = imImageClone(kernel1);
+ imProcessRotate90(kernel1, kernel2, 1);
+
+ ret = imProcessConvolveDual(src_image, dst_image, kernel1, kernel2);
+
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+
+ return ret;
+}
+
+int imProcessSplineEdgeConvolve(const imImage* src_image, imImage* dst_image)
+{
+ int ret = 0;
+
+ imImage* tmp_image = imImageClone(src_image);
+ if (!tmp_image) return 0;
+
+ imImage* kernel1 = imImageCreate(5, 5, IM_GRAY, IM_INT);
+ imImageSetAttribute(kernel1, "Description", IM_BYTE, -1, (void*)"SplineEdge");
+
+ int* kernel_data = (int*)kernel1->data[0];
+ kernel_data[10] = -1;
+ kernel_data[11] = 8;
+ kernel_data[12] = 0;
+ kernel_data[13] = -8;
+ kernel_data[14] = 1;
+
+ imImage* kernel2 = imImageClone(kernel1);
+ imProcessRotate90(kernel1, kernel2, 1);
+
+ imImage* kernel3 = imImageClone(kernel1);
+ imProcessRotateKernel(kernel3);
+
+ imImage* kernel4 = imImageClone(kernel1);
+ imProcessRotate90(kernel3, kernel4, 1);
+
+ ret = imProcessConvolveDual(src_image, tmp_image, kernel1, kernel2);
+ ret = imProcessConvolveDual(src_image, dst_image, kernel3, kernel4);
+
+ imProcessArithmeticConstOp(tmp_image, (float)sqrt(2.0), tmp_image, IM_BIN_MUL);
+ imProcessArithmeticOp(tmp_image, dst_image, dst_image, IM_BIN_ADD);
+
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+ imImageDestroy(kernel3);
+ imImageDestroy(kernel4);
+ imImageDestroy(tmp_image);
+
+ return ret;
+}
+
+int imGaussianStdDev2KernelSize(float stddev)
+{
+ if (stddev < 0)
+ return (int)-stddev;
+ else
+ {
+ int width = (int)(3.35*stddev + 0.3333);
+ return 2*width + 1;
+ }
+}
+
+float imGaussianKernelSize2StdDev(int kernel_size)
+{
+ int width = (kernel_size - 1)/2;
+ return (width - 0.3333f)/3.35f;
+}
+
+int imProcessGaussianConvolve(const imImage* src_image, imImage* dst_image, float stddev)
+{
+ int kernel_size = imGaussianStdDev2KernelSize(stddev);
+
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_FLOAT);
+ if (!kernel)
+ return 0;
+
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Gaussian");
+ imProcessRenderGaussian(kernel, stddev);
+
+ int ret = imProcessConvolveSep(src_image, dst_image, kernel);
+
+ imImageDestroy(kernel);
+
+ return ret;
+}
+
+int imProcessLapOfGaussianConvolve(const imImage* src_image, imImage* dst_image, float stddev)
+{
+ int kernel_size = imGaussianStdDev2KernelSize(stddev);
+
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_FLOAT);
+ if (!kernel)
+ return 0;
+
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Laplacian Of Gaussian");
+ imProcessRenderLapOfGaussian(kernel, stddev);
+
+ int ret;
+ if (src_image->data_type == IM_BYTE || src_image->data_type == IM_USHORT)
+ {
+ imImage* aux_image = imImageClone(dst_image);
+ if (!aux_image)
+ {
+ imImageDestroy(kernel);
+ return 0;
+ }
+
+ imProcessUnArithmeticOp(src_image, aux_image, IM_UN_EQL); // Convert to IM_INT
+ ret = imProcessConvolve(aux_image, dst_image, kernel);
+ imImageDestroy(aux_image);
+ }
+ else
+ ret = imProcessConvolve(src_image, dst_image, kernel);
+
+ imImageDestroy(kernel);
+
+ return ret;
+}
+
+int imProcessDiffOfGaussianConvolve(const imImage* src_image, imImage* dst_image, float stddev1, float stddev2)
+{
+ imImage* aux_image1 = imImageClone(src_image);
+ imImage* aux_image2 = imImageClone(src_image);
+ if (!aux_image1 || !aux_image2)
+ {
+ if (aux_image1) imImageDestroy(aux_image1);
+ return 0;
+ }
+
+ int kernel_size1 = imGaussianStdDev2KernelSize(stddev1);
+ int kernel_size2 = imGaussianStdDev2KernelSize(stddev2);
+ int size = kernel_size1;
+ if (kernel_size1 < kernel_size2) size = kernel_size2;
+
+ imImage* kernel1 = imImageCreate(size, size, IM_GRAY, IM_FLOAT);
+ imImage* kernel2 = imImageCreate(size, size, IM_GRAY, IM_FLOAT);
+ if (!kernel1 || !kernel2)
+ {
+ if (kernel1) imImageDestroy(kernel1);
+ if (kernel2) imImageDestroy(kernel2);
+ imImageDestroy(aux_image1);
+ imImageDestroy(aux_image2);
+ return 0;
+ }
+
+ imImageSetAttribute(kernel1, "Description", IM_BYTE, -1, (void*)"Gaussian1");
+ imImageSetAttribute(kernel2, "Description", IM_BYTE, -1, (void*)"Gaussian2");
+
+ imProcessRenderGaussian(kernel1, stddev1);
+ imProcessRenderGaussian(kernel2, stddev2);
+
+ if (!imProcessConvolve(src_image, aux_image1, kernel1) ||
+ !imProcessConvolve(src_image, aux_image2, kernel2))
+ {
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+ imImageDestroy(aux_image1);
+ imImageDestroy(aux_image2);
+ return 0;
+ }
+
+ imProcessArithmeticOp(aux_image1, aux_image2, dst_image, IM_BIN_SUB);
+
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+ imImageDestroy(aux_image1);
+ imImageDestroy(aux_image2);
+
+ return 1;
+}
+
+#ifdef _TEST_CODE_
+int imProcessDiffOfGaussianConvolveTEST(const imImage* src_image, imImage* dst_image, float stddev1, float stddev2)
+{
+ int kernel_size1 = imGaussianStdDev2KernelSize(stddev1);
+ int kernel_size2 = imGaussianStdDev2KernelSize(stddev2);
+ int size = kernel_size1;
+ if (kernel_size1 < kernel_size2) size = kernel_size2;
+
+ imImage* kernel1 = imImageCreate(size, size, IM_GRAY, IM_FLOAT);
+ imImage* kernel2 = imImageCreate(size, size, IM_GRAY, IM_FLOAT);
+ if (!kernel1 || !kernel2)
+ {
+ if (kernel1) imImageDestroy(kernel1);
+ if (kernel2) imImageDestroy(kernel2);
+ return 0;
+ }
+
+ imImageSetAttribute(kernel1, "Description", IM_BYTE, -1, (void*)"Gaussian");
+ imImageSetAttribute(kernel2, "Description", IM_BYTE, -1, (void*)"Gaussian");
+
+ imProcessRenderGaussian(kernel1, stddev1);
+ imProcessRenderGaussian(kernel2, stddev2);
+
+ // ERROR: kernel 1 should be multiplied by a factor to improve the difference.
+
+ imProcessArithmeticOp(kernel1, kernel2, kernel1, IM_BIN_SUB);
+ imImageSetAttribute(kernel1, "Description", IM_BYTE, -1, (void*)"Difference of Gaussian");
+
+ int ret = 0;
+ if (src_image->data_type == IM_BYTE || src_image->data_type == IM_USHORT)
+ {
+ imImage* aux_image = imImageClone(dst_image);
+ if (!aux_image)
+ {
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+ return 0;
+ }
+
+ imProcessUnArithmeticOp(src_image, aux_image, IM_UN_EQL); // Convert to IM_INT
+ ret = imProcessConvolve(aux_image, dst_image, kernel1);
+ imImageDestroy(aux_image);
+ }
+ else
+ ret = imProcessConvolve(src_image, dst_image, kernel1);
+
+ imImageDestroy(kernel1);
+ imImageDestroy(kernel2);
+
+ return ret;
+}
+#endif
+
+int imProcessMeanConvolve(const imImage* src_image, imImage* dst_image, int ks)
+{
+ int counter = imCounterBegin("Mean Convolve");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ imImage* kernel = imImageCreate(ks, ks, IM_GRAY, IM_INT);
+
+ int* kernel_data = (int*)kernel->data[0];
+
+ int ks2 = ks/2;
+ for(int ky = 0; ky < ks; ky++)
+ {
+ int ky2 = ky-ks2;
+ ky2 = ky2*ky2;
+ for(int kx = 0; kx < ks; kx++)
+ {
+ int kx2 = kx-ks2;
+ kx2 = kx2*kx2;
+ int radius = imRound(sqrt(double(kx2 + ky2)));
+ if (radius <= ks2)
+ kernel_data[ky*ks + kx] = 1;
+ }
+ }
+
+ int ret = DoConvolveStep(src_image, dst_image, kernel, counter);
+
+ imImageDestroy(kernel);
+ imCounterEnd(counter);
+
+ return ret;
+}
diff --git a/src/process/im_convolve_rank.cpp b/src/process/im_convolve_rank.cpp
new file mode 100644
index 0000000..5488a78
--- /dev/null
+++ b/src/process/im_convolve_rank.cpp
@@ -0,0 +1,701 @@
+/** \file
+ * \brief Rank Convolution Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_convolve_rank.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_counter.h>
+#include <im_math.h>
+
+#include "im_process_loc.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+
+template <class T, class DT>
+static int DoConvolveRankFunc(T *map, DT* new_map, int width, int height, int kw, int kh, T (*func)(T* value, int count, int center), int counter)
+{
+ T* value = new T[kw*kh];
+ int offset, new_offset, i, j, x, y, v, c;
+ int kh1, kw1, kh2, kw2;
+
+ kh2 = kh/2;
+ kw2 = kw/2;
+ kh1 = -kh2;
+ kw1 = -kw2;
+ if (kh%2==0) kh2--; // if not odd decrease 1
+ if (kw%2==0) kw2--;
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ v = 0; c = 0;
+
+ for(y = kh1; y <= kh2; y++)
+ {
+ if ((j + y < 0) || // pass the bottom border
+ (j + y >= height)) // pass the top border
+ continue;
+
+ offset = (j + y) * width;
+
+ for(x = kw1; x <= kw2; x++)
+ {
+ if ((i + x < 0) || // pass the left border
+ (i + x >= width)) // pass the right border
+ continue;
+
+ if (x == 0 && y == 0)
+ c = v;
+
+ value[v] = map[offset + (i + x)];
+ v++;
+ }
+ }
+
+ new_map[new_offset + i] = (DT)func(value, v, c);
+ }
+
+ if (!imCounterInc(counter))
+ {
+ delete[] value;
+ return 0;
+ }
+ }
+
+ delete[] value;
+ return 1;
+}
+
+static int compare_imReal(const void *elem1, const void *elem2)
+{
+ float* v1 = (float*)elem1;
+ float* v2 = (float*)elem2;
+
+ if (*v1 < *v2)
+ return -1;
+
+ if (*v1 > *v2)
+ return 1;
+
+ return 0;
+}
+
+static int compare_imInt(const void *elem1, const void *elem2)
+{
+ int* v1 = (int*)elem1;
+ int* v2 = (int*)elem2;
+
+ if (*v1 < *v2)
+ return -1;
+
+ if (*v1 > *v2)
+ return 1;
+
+ return 0;
+}
+
+static int compare_imUShort(const void *elem1, const void *elem2)
+{
+ imushort* v1 = (imushort*)elem1;
+ imushort* v2 = (imushort*)elem2;
+
+ if (*v1 < *v2)
+ return -1;
+
+ if (*v1 > *v2)
+ return 1;
+
+ return 0;
+}
+
+static int compare_imByte(const void *elem1, const void *elem2)
+{
+ imbyte* v1 = (imbyte*)elem1;
+ imbyte* v2 = (imbyte*)elem2;
+
+ if (*v1 < *v2)
+ return -1;
+
+ if (*v1 > *v2)
+ return 1;
+
+ return 0;
+}
+
+static imbyte median_op_byte(imbyte* value, int count, int center)
+{
+ (void)center;
+ qsort(value, count, sizeof(imbyte), compare_imByte);
+ return value[count/2];
+}
+
+static imushort median_op_ushort(imushort* value, int count, int center)
+{
+ (void)center;
+ qsort(value, count, sizeof(imushort), compare_imUShort);
+ return value[count/2];
+}
+
+static int median_op_int(int* value, int count, int center)
+{
+ (void)center;
+ qsort(value, count, sizeof(int), compare_imInt);
+ return value[count/2];
+}
+
+static float median_op_real(float* value, int count, int center)
+{
+ (void)center;
+ qsort(value, count, sizeof(float), compare_imReal);
+ return value[count/2];
+}
+
+int imProcessMedianConvolve(const imImage* src_image, imImage* dst_image, int ks)
+{
+ int i, ret = 0;
+ int counter;
+
+ counter = imCounterBegin("Median Filter");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, median_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[i], (imushort*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, median_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[i], (int*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, median_op_int, counter);
+ break;
+ case IM_FLOAT:
+ ret = DoConvolveRankFunc((float*)src_image->data[i], (float*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, median_op_real, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static imbyte range_op_byte(imbyte* value, int count, int center)
+{
+ imbyte min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max-min;
+}
+
+static imushort range_op_ushort(imushort* value, int count, int center)
+{
+ imushort min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max-min;
+}
+
+static int range_op_int(int* value, int count, int center)
+{
+ int min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max-min;
+}
+
+static float range_op_real(float* value, int count, int center)
+{
+ float min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max-min;
+}
+
+int imProcessRangeConvolve(const imImage* src_image, imImage* dst_image, int ks)
+{
+ int i, ret = 0;
+ int counter;
+
+ counter = imCounterBegin("Range Filter");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, range_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[i], (imushort*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, range_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[i], (int*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, range_op_int, counter);
+ break;
+ case IM_FLOAT:
+ ret = DoConvolveRankFunc((float*)src_image->data[i], (float*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, range_op_real, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+/*
+Local variable threshold by the method of Bernsen.
+
+Description:
+ If the difference between the largest and the smallest
+ pixel value within the 'dx'*'dy' window is greater than
+ or equal to 'cmin' (local contrast threshold), the average
+ of the two values is used as threshold.
+
+ Pixels in homogenous areas (difference below 'cmin')
+ are assumed to be below the threshold.
+
+Reference:
+ Bernsen, J: "Dynamic thresholding of grey-level images"
+ Proc. of the 8th ICPR, Paris, Oct 1986, 1251-1255.
+
+Author: Oivind Due Trier
+
+Copyright 1990, Blab, UiO
+Image processing lab, Department of Informatics
+University of Oslo
+*/
+
+static int thresAux = 0;
+
+static imbyte contrast_thres_op_byte(imbyte* value, int count, int center)
+{
+ int c, t;
+ imbyte v = value[center], min, max;
+
+ imMinMax(value, count, min, max);
+
+ c = max-min;
+
+ if (c < thresAux)
+ return 0;
+ else
+ {
+ t = ((int)max + (int)min) / 2;
+
+ if (v >= t)
+ return 1;
+ else
+ return 0;
+ }
+}
+
+static imushort contrast_thres_op_ushort(imushort* value, int count, int center)
+{
+ int c, t;
+ imushort v = value[center], min, max;
+
+ imMinMax(value, count, min, max);
+
+ c = max-min;
+
+ if (c < thresAux)
+ return 0;
+ else
+ {
+ t = ((int)max + (int)min) / 2;
+
+ if (v >= t)
+ return 1;
+ else
+ return 0;
+ }
+}
+
+static int contrast_thres_op_int(int* value, int count, int center)
+{
+ int c, t;
+ int v = value[center], min, max;
+
+ imMinMax(value, count, min, max);
+
+ c = max-min;
+
+ if (c < thresAux)
+ return 0;
+ else
+ {
+ t = ((int)max + (int)min) / 2;
+
+ if (v >= t)
+ return 1;
+ else
+ return 0;
+ }
+}
+
+int imProcessRangeContrastThreshold(const imImage* src_image, imImage* dst_image, int ks, int min_range)
+{
+ int ret = 0;
+ int counter = imCounterBegin("Range Contrast Threshold");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ thresAux = min_range;
+
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->width, src_image->height, ks, ks, contrast_thres_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->width, src_image->height, ks, ks, contrast_thres_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->width, src_image->height, ks, ks, contrast_thres_op_int, counter);
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static imbyte max_thres_op_byte(imbyte* value, int count, int center)
+{
+ imbyte v = value[center], min, max;
+
+ if (v < thresAux)
+ return 0;
+
+ imMinMax(value, count, min, max);
+
+ if (v < max)
+ return 0;
+
+ return 1;
+}
+
+static imushort max_thres_op_ushort(imushort* value, int count, int center)
+{
+ imushort v = value[center], min, max;
+
+ if (v < thresAux)
+ return 0;
+
+ imMinMax(value, count, min, max);
+
+ if (v < max)
+ return 0;
+
+ return 1;
+}
+
+static int max_thres_op_int(int* value, int count, int center)
+{
+ int v = value[center], min, max;
+
+ if (v < thresAux)
+ return 0;
+
+ imMinMax(value, count, min, max);
+
+ if (v < max)
+ return 0;
+
+ return 1;
+}
+
+int imProcessLocalMaxThreshold(const imImage* src_image, imImage* dst_image, int ks, int min_thres)
+{
+ int ret = 0;
+ int counter = imCounterBegin("Local Max Threshold");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ thresAux = min_thres;
+
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->width, src_image->height, ks, ks, max_thres_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->width, src_image->height, ks, ks, max_thres_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->width, src_image->height, ks, ks, max_thres_op_int, counter);
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static imbyte rank_closest_op_byte(imbyte* value, int count, int center)
+{
+ imbyte v = value[center];
+ imbyte min, max;
+
+ imMinMax(value, count, min, max);
+
+ if (v - min < max - v)
+ return min;
+ else
+ return max;
+}
+
+static imushort rank_closest_op_ushort(imushort* value, int count, int center)
+{
+ imushort v = value[center];
+ imushort min, max;
+
+ imMinMax(value, count, min, max);
+
+ if (v - min < max - v)
+ return min;
+ else
+ return max;
+}
+
+static int rank_closest_op_int(int* value, int count, int center)
+{
+ int v = value[center];
+ int min, max;
+
+ imMinMax(value, count, min, max);
+
+ if (v - min < max - v)
+ return min;
+ else
+ return max;
+}
+
+static float rank_closest_op_real(float* value, int count, int center)
+{
+ float v = value[center];
+ float min, max;
+
+ imMinMax(value, count, min, max);
+
+ if (v - min < max - v)
+ return min;
+ else
+ return max;
+}
+
+
+int imProcessRankClosestConvolve(const imImage* src_image, imImage* dst_image, int ks)
+{
+ int i, ret = 0;
+ int counter;
+
+ counter = imCounterBegin("Rank Closest");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_closest_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[i], (imushort*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_closest_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[i], (int*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_closest_op_int, counter);
+ break;
+ case IM_FLOAT:
+ ret = DoConvolveRankFunc((float*)src_image->data[i], (float*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_closest_op_real, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static imbyte rank_max_op_byte(imbyte* value, int count, int center)
+{
+ imbyte min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max;
+}
+
+static imushort rank_max_op_ushort(imushort* value, int count, int center)
+{
+ imushort min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max;
+}
+
+static int rank_max_op_int(int* value, int count, int center)
+{
+ int min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max;
+}
+
+static float rank_max_op_real(float* value, int count, int center)
+{
+ float min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return max;
+}
+
+int imProcessRankMaxConvolve(const imImage* src_image, imImage* dst_image, int ks)
+{
+ int i, ret = 0;
+ int counter;
+
+ counter = imCounterBegin("Rank Max");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_max_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[i], (imushort*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_max_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[i], (int*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_max_op_int, counter);
+ break;
+ case IM_FLOAT:
+ ret = DoConvolveRankFunc((float*)src_image->data[i], (float*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_max_op_real, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static imbyte rank_min_op_byte(imbyte* value, int count, int center)
+{
+ imbyte min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return min;
+}
+
+static imushort rank_min_op_ushort(imushort* value, int count, int center)
+{
+ imushort min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return min;
+}
+
+static int rank_min_op_int(int* value, int count, int center)
+{
+ int min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return min;
+}
+
+static float rank_min_op_real(float* value, int count, int center)
+{
+ float min, max;
+ (void)center;
+ imMinMax(value, count, min, max);
+ return min;
+}
+
+int imProcessRankMinConvolve(const imImage* src_image, imImage* dst_image, int ks)
+{
+ int i, ret = 0;
+ int counter;
+
+ counter = imCounterBegin("Rank Min");
+ imCounterTotal(counter, src_image->depth*src_image->height, "Filtering...");
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoConvolveRankFunc((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_min_op_byte, counter);
+ break;
+ case IM_USHORT:
+ ret = DoConvolveRankFunc((imushort*)src_image->data[i], (imushort*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_min_op_ushort, counter);
+ break;
+ case IM_INT:
+ ret = DoConvolveRankFunc((int*)src_image->data[i], (int*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_min_op_int, counter);
+ break;
+ case IM_FLOAT:
+ ret = DoConvolveRankFunc((float*)src_image->data[i], (float*)dst_image->data[i],
+ src_image->width, src_image->height, ks, ks, rank_min_op_real, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
diff --git a/src/process/im_distance.cpp b/src/process/im_distance.cpp
new file mode 100644
index 0000000..019356d
--- /dev/null
+++ b/src/process/im_distance.cpp
@@ -0,0 +1,512 @@
+/** \file
+ * \brief Distance Transform
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_distance.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+#include <im.h>
+#include <im_util.h>
+
+#include "im_process_glo.h"
+
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+
+const float DT_ONE = 1.0f; // 1x0
+const float DT_SQRT2 = 1.414213562373f; // 1x1
+const float DT_SQRT5 = 2.2360679775f; // 2x1
+const float DT_SQRT10 = 3.1622776601684f; // 3x1
+const float DT_SQRT13 = 3.605551275464f; // 3x2
+const float DT_SQRT17 = 4.12310562562f; // 4x1
+const float DT_SQRT25 = 5.0f; // 4x3
+
+static inline void setValue(int r, int r1, int r2, int r3, int r4, float *image_data, int f)
+{
+ float v;
+ float minv = image_data[r]; // (x,y)
+
+ if (f)
+ v = image_data[r - 1] + DT_ONE; // (x-1,y)
+ else
+ v = image_data[r + 1] + DT_ONE; // (x+1,y)
+ if (v < minv) minv = v;
+
+ v = image_data[r1] + DT_ONE; // (x,y-1) (x,y+1)
+ if (v < minv) minv = v;
+
+ if (minv < DT_SQRT2)
+ goto min_attrib;
+
+ v = image_data[r1 - 1] + DT_SQRT2; // (x-1,y-1) (x-1,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r1 + 1] + DT_SQRT2; // (x+1,y-1) (x+1,y+1)
+ if (v < minv) minv = v;
+
+ if (minv < DT_SQRT5)
+ goto min_attrib;
+
+ v = image_data[r1 + 2] + DT_SQRT5; // (x+2,y-1) (x+2,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r1 - 2] + DT_SQRT5; // (x-2,y-1) (x-2,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r2 - 1] + DT_SQRT5; // (x-1,y-2) (x-1,y+2)
+ if (v < minv) minv = v;
+
+ v = image_data[r2 + 1] + DT_SQRT5; // (x+1,y-2) (x+1,y+2)
+ if (v < minv) minv = v;
+
+ if (minv < DT_SQRT10)
+ goto min_attrib;
+
+ v = image_data[r1 + 3] + DT_SQRT10; // (x+3,y-1) (x+3,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r1 - 3] + DT_SQRT10; // (x-3,y-1) (x-3,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r3 - 1] + DT_SQRT10; // (x-1,y-3) (x-1,y+3)
+ if (v < minv) minv = v;
+
+ v = image_data[r3 + 1] + DT_SQRT10; // (x+1,y-3) (x+1,y+3)
+ if (v < minv) minv = v;
+
+ if (minv < DT_SQRT13)
+ goto min_attrib;
+
+ v = image_data[r2 - 3] + DT_SQRT13; // (x-3,y-2) (x-3,y+2)
+ if (v < minv) minv = v;
+
+ v = image_data[r2 + 3] + DT_SQRT13; // (x+3,y-2) (x+3,y+2)
+ if (v < minv) minv = v;
+
+ v = image_data[r3 + 2] + DT_SQRT13; // (x+2,y-3) (x+2,y+3)
+ if (v < minv) minv = v;
+
+ v = image_data[r3 - 2] + DT_SQRT13; // (x-2,y-3) (x-2,y+3)
+ if (v < minv) minv = v;
+
+ if (minv < DT_SQRT17)
+ goto min_attrib;
+
+ v = image_data[r1 + 4] + DT_SQRT17; // (x+4,y-1) (x+4,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r1 - 4] + DT_SQRT17; // (x-4,y-1) (x-4,y+1)
+ if (v < minv) minv = v;
+
+ v = image_data[r4 - 1] + DT_SQRT17; // (x-1,y-4) (x-1,y+4)
+ if (v < minv) minv = v;
+
+ v = image_data[r4 + 1] + DT_SQRT17; // (x+1,y-4) (x+1,y+4)
+ if (v < minv) minv = v;
+
+ if (minv < DT_SQRT25)
+ goto min_attrib;
+
+ v = image_data[r3 - 4] + DT_SQRT25; // (x-4,y-3) (x-4,y+3)
+ if (v < minv) minv = v;
+
+ v = image_data[r3 + 4] + DT_SQRT25; // (x+4,y-3) (x+4,y+3)
+ if (v < minv) minv = v;
+
+ v = image_data[r4 + 3] + DT_SQRT25; // (x+3,y-4) (x+3,y+4)
+ if (v < minv) minv = v;
+
+ v = image_data[r4 - 3] + DT_SQRT25; // (x-3,y-4) (x-3,y+4)
+ if (v < minv) minv = v;
+
+min_attrib:
+ image_data[r] = minv;
+}
+
+static inline void setValueForwardEdge(int r, int r1, int r2, int width, int x, int y, float *image_data)
+{
+ float v;
+ float minv = image_data[r]; // (x,y)
+
+ if (y > 0)
+ {
+ v = image_data[r1] + DT_ONE; // (x,y-1)
+ if (v < minv) minv = v;
+ }
+
+ if (x > 0)
+ {
+ v = image_data[r - 1] + DT_ONE; // (x-1,y)
+ if (v < minv) minv = v;
+ }
+
+ if (x > 0 && y > 0)
+ {
+ v = image_data[r1 - 1] + DT_SQRT2; // (x-1,y-1)
+ if (v < minv) minv = v;
+ }
+
+ if (x < width-2 && y > 0)
+ {
+ v = image_data[r1 + 1] + DT_SQRT2; // (x+1,y-1)
+ if (v < minv) minv = v;
+ }
+
+ if (x > 0 && y > 1)
+ {
+ v = image_data[r2 - 1] + DT_SQRT5; // (x-1,y-2)
+ if (v < minv) minv = v;
+ }
+
+ if (x < width-2 && y > 1)
+ {
+ v = image_data[r2 + 1] + DT_SQRT5; // (x+1,y-2)
+ if (v < minv) minv = v;
+ }
+
+ if (x < width-3 && y > 0)
+ {
+ v = image_data[r1 + 2] + DT_SQRT5; // (x+2,y-1)
+ if (v < minv) minv = v;
+ }
+
+ if (x > 1 && y > 0)
+ {
+ v = image_data[r1 - 2] + DT_SQRT5; // (x-2,y-1)
+ if (v < minv) minv = v;
+ }
+
+ image_data[r] = minv;
+}
+
+static inline void setValueBackwardEdge(int r, int r1, int r2, int width, int height, int x, int y, float *image_data)
+{
+ float v;
+ float minv = image_data[r]; // (x,y)
+
+ if (x < width-2)
+ {
+ v = image_data[r + 1] + DT_ONE; // (x+1,y)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-2)
+ {
+ v = image_data[r1] + DT_ONE; // (x,y+1)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-2 && x > 0)
+ {
+ v = image_data[r1 - 1] + DT_SQRT2; // (x-1,y+1)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-2 && x < width-2)
+ {
+ v = image_data[r1 + 1] + DT_SQRT2; // (x+1,y+1)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-2 && x < width-3)
+ {
+ v = image_data[r1 + 2] + DT_SQRT5; // (x+2,y+1)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-3 && x < width-2)
+ {
+ v = image_data[r2 + 1] + DT_SQRT5; // (x+1,y+2)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-3 && x > 0)
+ {
+ v = image_data[r2 - 1] + DT_SQRT5; // (x-1,y+2)
+ if (v < minv) minv = v;
+ }
+
+ if (y < height-2 && x > 1)
+ {
+ v = image_data[r1 - 2] + DT_SQRT5; // (x-2,y+1)
+ if (v < minv) minv = v;
+ }
+
+ image_data[r] = minv;
+}
+
+void imProcessDistanceTransform(const imImage* src_image, imImage* dst_image)
+{
+ int i, x, y,
+ offset, offset1, offset2, offset3, offset4,
+ width = src_image->width,
+ height = src_image->height;
+
+ imbyte* src_data = (imbyte*)src_image->data[0];
+ float* dst_data = (float*)dst_image->data[0];
+
+ float max_dist = (float)sqrt(double(width*width + height*height));
+
+ for (i = 0; i < src_image->count; i++)
+ {
+ // if pixel is background, then distance is zero.
+ if (src_data[i])
+ dst_data[i] = max_dist;
+ }
+
+ /* down->top, left->right */
+ for (y = 0; y < height; y++)
+ {
+ offset = y * width;
+ offset1 = offset - width;
+ offset2 = offset - 2*width;
+ offset3 = offset - 3*width;
+ offset4 = offset - 4*width;
+
+ for (x = 0; x < width; x++)
+ {
+ if (src_data[offset])
+ {
+ if (x < 4 || x > width-5 || y < 4 || y > height-5)
+ setValueForwardEdge(offset, offset1, offset2, width, x, y, dst_data);
+ else
+ setValue(offset, offset1, offset2, offset3, offset4, dst_data, 1);
+ }
+
+ offset++;
+ offset1++;
+ offset2++;
+ offset3++;
+ offset4++;
+ }
+ }
+
+ /* top->down, right->left */
+ for (y = height-1; y >= 0; y--)
+ {
+ offset = y * width + width-1;
+ offset1 = offset + width;
+ offset2 = offset + 2*width;
+ offset3 = offset + 3*width;
+ offset4 = offset + 4*width;
+
+ for (x = width-1; x >= 0; x--)
+ {
+ if (src_data[offset])
+ {
+ if (x < 4 || x > width-5 || y < 4 || y > height-5)
+ setValueBackwardEdge(offset, offset1, offset2, width, height, x, y, dst_data);
+ else
+ setValue(offset, offset1, offset2, offset3, offset4, dst_data, 0);
+ }
+
+ offset--;
+ offset1--;
+ offset2--;
+ offset3--;
+ offset4--;
+ }
+ }
+}
+
+static void iFillValue(imbyte* img_data, int x, int y, int width, int value)
+{
+ int r = y * width + x;
+ int r1a = r - width;
+ int r1b = r + width;
+ int v;
+
+ int old_value = img_data[r];
+ img_data[r] = (imbyte)value;
+
+ v = img_data[r1a]; // (x,y-1)
+ if (v == old_value)
+ iFillValue(img_data, x, y-1, width, value);
+
+ v = img_data[r - 1]; // (x-1,y)
+ if (v == old_value)
+ iFillValue(img_data, x-1, y, width, value);
+
+ v = img_data[r1a - 1]; // (x-1,y-1)
+ if (v == old_value)
+ iFillValue(img_data, x-1, y-1, width, value);
+
+ v = img_data[r1a + 1]; // (x+1,y-1)
+ if (v == old_value)
+ iFillValue(img_data, x+1, y-1, width, value);
+
+ v = img_data[r + 1]; // (x+1,y)
+ if (v == old_value)
+ iFillValue(img_data, x+1, y, width, value);
+
+ v = img_data[r1b]; // (x,y+1)
+ if (v == old_value)
+ iFillValue(img_data, x, y+1, width, value);
+
+ v = img_data[r1b - 1]; // (x-1,y+1)
+ if (v == old_value)
+ iFillValue(img_data, x-1, y+1, width, value);
+
+ v = img_data[r1b + 1]; // (x+1,y+1)
+ if (v == old_value)
+ iFillValue(img_data, x+1, y+1, width, value);
+}
+
+static inline int iCheckFalseMaximum(int r, int r2a, int r2b, int width, float *src_data)
+{
+ /* we are ignoring valeys of 1 pixel. */
+ /* this is not 100% fail proof */
+ float v;
+ float maxv = src_data[r]; // (x,y)
+ int r1a = r - width;
+ int r1b = r + width;
+
+ v = src_data[r2a - 1]; // (x-1,y-2)
+ if (v > maxv) return 1;
+
+ v = src_data[r2a]; // (x,y-2)
+ if (v > maxv) return 1;
+
+ v = src_data[r2a + 1]; // (x+1,y-2)
+ if (v > maxv) return 1;
+
+ v = src_data[r2b - 1]; // (x-1,y+2)
+ if (v > maxv) return 1;
+
+ v = src_data[r2b]; // (x,y+2)
+ if (v > maxv) return 1;
+
+ v = src_data[r2b + 1]; // (x+1,y+2)
+ if (v > maxv) return 1;
+
+
+ v = src_data[r2b - 2]; // (x-2,y+2)
+ if (v > maxv) return 1;
+
+ v = src_data[r1b - 2]; // (x-2,y+1)
+ if (v > maxv) return 1;
+
+ v = src_data[r - 2]; // (x-2,y)
+ if (v > maxv) return 1;
+
+ v = src_data[r1a - 2]; // (x-2,y-1)
+ if (v > maxv) return 1;
+
+ v = src_data[r2a - 2]; // (x-2,y-2)
+ if (v > maxv) return 1;
+
+
+ v = src_data[r2a + 2]; // (x+2,y-2)
+ if (v > maxv) return 1;
+
+ v = src_data[r1a + 2]; // (x+2,y-1)
+ if (v > maxv) return 1;
+
+ v = src_data[r + 2]; // (x+2,y)
+ if (v > maxv) return 1;
+
+ v = src_data[r1b + 2]; // (x+2,y+1)
+ if (v > maxv) return 1;
+
+ v = src_data[r2b + 2]; // (x+2,y+2)
+ if (v > maxv) return 1;
+
+ return 0;
+}
+
+static inline void iCheckMaximum(int r, int r1a, int r1b, float *src_data, imbyte* dst_data)
+{
+ int unique = 1;
+ float v;
+ float maxv = src_data[r]; // (x,y)
+
+ v = src_data[r1a]; // (x,y-1)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r - 1]; // (x-1,y)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r1a - 1]; // (x-1,y-1)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r1a + 1]; // (x+1,y-1)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r + 1]; // (x+1,y)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r1b]; // (x,y+1)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r1b - 1]; // (x-1,y+1)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ v = src_data[r1b + 1]; // (x+1,y+1)
+ if (v >= maxv) { maxv = v; unique = 0; }
+
+ if (src_data[r] < maxv) // not a maximum
+ dst_data[r] = 0;
+ else
+ {
+ if (unique) // unique maximum
+ dst_data[r] = 1;
+ else // can be maximum
+ dst_data[r] = 2;
+ }
+}
+
+void imProcessRegionalMaximum(const imImage* src_image, imImage* dst_image)
+{
+ int i, x, y, offset, offsetA, offsetB,
+ width = src_image->width,
+ height = src_image->height;
+
+ float* src_data = (float*)src_image->data[0];
+ imbyte* dst_data = (imbyte*)dst_image->data[0];
+
+ for (y = 1; y < height-1; y++)
+ {
+ offset = y * width + 1;
+ offsetA = offset - width;
+ offsetB = offset + width;
+
+ for (x = 1; x < width-1; x++)
+ {
+ if (src_data[offset])
+ iCheckMaximum(offset, offsetA, offsetB, src_data, dst_data);
+
+ offset++;
+ offsetA++;
+ offsetB++;
+ }
+ }
+
+ // remove false maximum
+ for (y = 2; y < height-2; y++)
+ {
+ offset = y * width + 2;
+ offsetA = offset - 2*width;
+ offsetB = offset + 2*width;
+
+ for (x = 2; x < width-2; x++)
+ {
+ if (dst_data[offset] == 2)
+ {
+ if (iCheckFalseMaximum(offset, offsetA, offsetB, width, src_data))
+ iFillValue(dst_data, x, y, width, 0);
+ }
+
+ offset++;
+ offsetA++;
+ offsetB++;
+ }
+ }
+
+ // update destiny with remaining maximum
+ for (i = 0; i < src_image->count; i++)
+ {
+ if (dst_data[i] == 2)
+ dst_data[i] = 1;
+ }
+}
diff --git a/src/process/im_effects.cpp b/src/process/im_effects.cpp
new file mode 100644
index 0000000..7f65ce6
--- /dev/null
+++ b/src/process/im_effects.cpp
@@ -0,0 +1,86 @@
+/** \file
+ * \brief Effects
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_effects.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+#include <im_complex.h>
+
+#include "im_process_pon.h"
+#include "im_math_op.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+static unsigned char BoxMean(imbyte *map, int offset, int shift, int hbox_size, int vbox_size)
+{
+ map += offset;
+ int acum = 0;
+ for (int i = 0; i < vbox_size; i++)
+ {
+ for (int j = 0; j < hbox_size; j++)
+ {
+ acum += *map++;
+ }
+
+ map += shift;
+ }
+
+ return (unsigned char)(acum / (vbox_size*hbox_size));
+}
+
+static void BoxSet(imbyte *map, int offset, int shift, int hbox_size, int vbox_size, unsigned char value)
+{
+ map += offset;
+ for (int i = 0; i < vbox_size; i++)
+ {
+ for (int j = 0; j < hbox_size; j++)
+ {
+ *map++ = value;
+ }
+
+ map += shift;
+ }
+}
+
+void imProcessPixelate(const imImage* src_image, imImage* dst_image, int box_size)
+{
+ int hbox = ((src_image->width + box_size-1)/ box_size);
+ int vbox = ((src_image->height + box_size-1)/ box_size);
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ imbyte *src_map=(imbyte*)src_image->data[i];
+ imbyte *dst_map=(imbyte*)dst_image->data[i];
+ int vbox_size = box_size;
+
+ for (int bv = 0; bv < vbox; bv++)
+ {
+ int bv_pos = bv*box_size;
+ if (bv == vbox-1) vbox_size = src_image->height - bv_pos;
+ int hbox_size = box_size;
+
+ for (int bh = 0; bh < hbox; bh++)
+ {
+ int bh_pos = bh*box_size;
+ if (bh == hbox-1) hbox_size = src_image->width - bh_pos;
+ int offset = bv_pos*src_image->width + bh_pos;
+ int shift = src_image->width - hbox_size;
+ unsigned char mean = BoxMean(src_map, offset, shift, hbox_size, vbox_size);
+ BoxSet(dst_map, offset, shift, hbox_size, vbox_size, mean);
+ }
+ }
+ }
+}
+
+void imProcessPosterize(const imImage* src_image, imImage* dst_image, int level)
+{
+ unsigned char mask = (unsigned char)(0xFF << level);
+ imProcessBitMask(src_image, dst_image, mask, IM_BIT_AND);
+}
+
diff --git a/src/process/im_fft.cpp b/src/process/im_fft.cpp
new file mode 100644
index 0000000..5ab1642
--- /dev/null
+++ b/src/process/im_fft.cpp
@@ -0,0 +1,218 @@
+/** \file
+ * \brief Fast Fourier Transform using FFTW library
+ *
+ * Comments only for FFTW 3:
+ *
+ * Where used only non optimal file for better portability.
+ * You must change the makefile to add other files.
+ *
+ * Duplicated files: buffered.c conf.c direct.c indirect.c generic.c
+ * nop.c plan.c problem.c rader.c rank0.c rank-geq2.c
+ * vrank-geq1.c solve.c ct.c codlist.c
+ * These were renamed to "r*" when in the rdft folder, and to "k*" when in the kernel folder.
+ *
+ * New File: api\config.h
+ *
+ * From the FTW manual:
+\verbatim
+ "FFTW is best at handling sizes of the form 2a 3b 5c 7d 11e 13f,
+ where e+f is either 0 or 1, and the other exponents are arbitrary.
+ Other sizes are computed by means of a slow,
+ general-purpose algorithm (which nevertheless retains O(n log n)."
+\endverbatim
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_fft.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+#include <im.h>
+#include <im_util.h>
+#include <im_complex.h>
+#include <im_convert.h>
+
+#include "im_process.h"
+
+#include <stdlib.h>
+#include <assert.h>
+#include <memory.h>
+
+#ifdef USE_FFTW3
+#include "fftw3.h"
+#else
+#include "fftw.h"
+#endif
+
+static void iCopyCol(imcfloat *map1, imcfloat *map2, int height, int width1, int width2)
+{
+ int i;
+ for(i = 0; i < height; i++)
+ {
+ *map1 = *map2;
+ map1 += width1;
+ map2 += width2;
+ }
+}
+
+static void iCenterFFT(imcfloat *map, int width, int height, int inverse)
+{
+ imcfloat *map1, *map2, *map3, *tmp;
+ int i, half1_width, half2_width, half1_height, half2_height;
+
+ if (inverse)
+ {
+ half1_width = width/2;
+ half1_height = height/2;
+
+ half2_width = (width+1)/2;
+ half2_height = (height+1)/2;
+ }
+ else
+ {
+ half1_width = (width+1)/2;
+ half1_height = (height+1)/2;
+
+ half2_width = width/2;
+ half2_height = height/2;
+ }
+
+ tmp = (imcfloat*)malloc(half1_width*sizeof(imcfloat));
+
+ map1 = map;
+ map2 = map + half1_width;
+ map3 = map + half2_width;
+ for(i = 0; i < height; i++)
+ {
+ memcpy(tmp, map1, half1_width*sizeof(imcfloat));
+ memcpy(map1, map2, half2_width*sizeof(imcfloat));
+ memcpy(map3, tmp, half1_width*sizeof(imcfloat));
+
+ map1 += width;
+ map2 += width;
+ map3 += width;
+ }
+
+ free(tmp);
+
+ tmp = (imcfloat*)malloc(half1_height*sizeof(imcfloat));
+
+ map1 = map;
+ map2 = map + half1_height*width;
+ map3 = map + half2_height*width;
+ for(i = 0; i < width; i++)
+ {
+ iCopyCol(tmp, map1, half1_height, 1, width);
+ iCopyCol(map1, map2, half2_height, width, width);
+ iCopyCol(map3, tmp, half1_height, width, 1);
+
+ map1++;
+ map2++;
+ map3++;
+ }
+
+ free(tmp);
+}
+
+static void iDoFFT(void *map, int width, int height, int inverse, int center, int normalize)
+{
+ if (inverse && center)
+ iCenterFFT((imcfloat*)map, width, height, inverse);
+
+#ifdef USE_FFTW3
+ fftwf_plan plan = fftwf_plan_dft_2d(height, width,
+ (fftwf_complex*)map, (fftwf_complex*)map, // in-place transform
+ inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE);
+ fftwf_execute(plan);
+ fftwf_destroy_plan(plan);
+#else
+ fftwnd_plan plan = fftw2d_create_plan(height, width, inverse?FFTW_BACKWARD:FFTW_FORWARD, FFTW_ESTIMATE|FFTW_IN_PLACE);
+ fftwnd(plan, 1, (FFTW_COMPLEX*)map, 1, 0, 0, 0, 0);
+ fftwnd_destroy_plan(plan);
+#endif
+
+ if (!inverse && center)
+ iCenterFFT((imcfloat*)map, width, height, inverse);
+
+ if (normalize)
+ {
+ float NM = (float)(width * height);
+ int count = (int)(2*NM);
+
+ if (normalize == 1)
+ NM = (float)sqrt(NM);
+
+ float *fmap = (float*)map;
+ for (int i = 0; i < count; i++)
+ *fmap++ /= NM;
+ }
+}
+
+void imProcessSwapQuadrants(imImage* image, int inverse)
+{
+ for (int i = 0; i < image->depth; i++)
+ iCenterFFT((imcfloat*)image->data[i], image->width, image->height, inverse);
+}
+
+void imProcessFFTraw(imImage* image, int inverse, int center, int normalize)
+{
+ for (int i = 0; i < image->depth; i++)
+ iDoFFT(image->data[i], image->width, image->height, inverse, center, normalize);
+}
+
+void imProcessFFT(const imImage* src_image, imImage* dst_image)
+{
+ if (src_image->data_type != IM_CFLOAT)
+ imConvertDataType(src_image, dst_image, 0, 0, 0, 0);
+ else
+ imImageCopy(src_image, dst_image);
+
+ imProcessFFTraw(dst_image, 0, 1, 0); // forward, centered, unnormalized
+}
+
+void imProcessIFFT(const imImage* src_image, imImage* dst_image)
+{
+ imImageCopy(src_image, dst_image);
+
+ imProcessFFTraw(dst_image, 1, 1, 2); // inverse, uncentered, double normalized
+}
+
+void imProcessCrossCorrelation(const imImage* src_image1, const imImage* src_image2, imImage* dst_image)
+{
+ imImage *tmp_image = imImageCreate(src_image2->width, src_image2->height, src_image2->color_space, IM_CFLOAT);
+ if (!tmp_image)
+ return;
+
+ if (src_image2->data_type != IM_CFLOAT)
+ imConvertDataType(src_image2, tmp_image, 0, 0, 0, 0);
+ else
+ imImageCopy(src_image2, tmp_image);
+
+ if (src_image1->data_type != IM_CFLOAT)
+ imConvertDataType(src_image1, dst_image, 0, 0, 0, 0);
+ else
+ imImageCopy(src_image1, dst_image);
+
+ imProcessFFTraw(tmp_image, 0, 1, 1); // forward, centered, normalized
+ imProcessFFTraw(dst_image, 0, 1, 1);
+
+ imProcessMultiplyConj(dst_image, tmp_image, dst_image);
+
+ imProcessFFTraw(dst_image, 1, 1, 1); // inverse, uncentered, normalized
+ imProcessSwapQuadrants(dst_image, 0); // from origin to center
+
+ imImageDestroy(tmp_image);
+}
+
+void imProcessAutoCorrelation(const imImage* src_image, imImage* dst_image)
+{
+ if (src_image->data_type != IM_CFLOAT)
+ imConvertDataType(src_image, dst_image, 0, 0, 0, 0);
+ else
+ imImageCopy(src_image, dst_image);
+
+ imProcessFFTraw(dst_image, 0, 0, 1); // forward, at origin, normalized
+
+ imProcessMultiplyConj(dst_image, dst_image, dst_image);
+
+ imProcessFFTraw(dst_image, 1, 0, 1); // inverse, at origin, normalized
+ imProcessSwapQuadrants(dst_image, 0); // from origin to center
+}
diff --git a/src/process/im_geometric.cpp b/src/process/im_geometric.cpp
new file mode 100644
index 0000000..a0b5129
--- /dev/null
+++ b/src/process/im_geometric.cpp
@@ -0,0 +1,724 @@
+/** \file
+ * \brief Geometric Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_geometric.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_counter.h>
+
+#include "im_process_loc.h"
+#include "im_math_op.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+static inline void imRect2Polar(float x, float y, float *radius, float *theta)
+{
+ *radius = sqrtf(x*x + y*y);
+ *theta = atan2f(y, x);
+}
+
+static inline void imPolar2Rect(float radius, float theta, float *x, float *y)
+{
+ *x = radius * cosf(theta);
+ *y = radius * sinf(theta);
+}
+
+static inline void swirl_invtransf(int x, int y, float *xl, float *yl, float k, float xc, float yc)
+{
+ float radius, theta;
+ x -= (int)xc;
+ y -= (int)yc;
+
+ imRect2Polar((float)x, (float)y, &radius, &theta);
+
+ theta += k * radius;
+
+ imPolar2Rect(radius, theta, xl, yl);
+
+ *xl += xc;
+ *yl += yc;
+}
+
+template <class DT, class DTU>
+static int Swirl(int width, int height, DT *src_map, DT *dst_map,
+ float k, int counter, DTU Dummy, int order)
+{
+ float xl, yl;
+ float xc = float(width/2.);
+ float yc = float(height/2.);
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ swirl_invtransf(x, y, &xl, &yl, k, xc, yc);
+
+ // if inside the original image broad area
+ if (xl > 0.0 && yl > 0.0 && xl < width && yl < height)
+ {
+ if (order == 1)
+ *dst_map = imBilinearInterpolation(width, height, src_map, xl, yl);
+ else if (order == 3)
+ *dst_map = imBicubicInterpolation(width, height, src_map, xl, yl, Dummy);
+ else
+ *dst_map = imZeroOrderInterpolation(width, height, src_map, xl, yl);
+ }
+
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+static inline void radial_invtransf(int x, int y, float *xl, float *yl, float k1, float xc, float yc)
+{
+ float aux;
+ x -= (int)xc;
+ y -= (int)yc;
+ aux = 1.0f + k1*(x*x + y*y);
+ *xl = x*aux + xc;
+ *yl = y*aux + yc;
+}
+
+template <class DT, class DTU>
+static int Radial(int width, int height, DT *src_map, DT *dst_map,
+ float k1, int counter, DTU Dummy, int order)
+{
+ float xl, yl;
+ float xc = float(width/2.);
+ float yc = float(height/2.);
+ int diag = (int)sqrt(float(width*width + height*height));
+
+ k1 /= (diag * diag);
+
+ for (int y = 0; y < height; y++)
+ {
+ for (int x = 0; x < width; x++)
+ {
+ radial_invtransf(x, y, &xl, &yl, k1, xc, yc);
+
+ // if inside the original image broad area
+ if (xl > 0.0 && yl > 0.0 && xl < width && yl < height)
+ {
+ if (order == 1)
+ *dst_map = imBilinearInterpolation(width, height, src_map, xl, yl);
+ else if (order == 3)
+ *dst_map = imBicubicInterpolation(width, height, src_map, xl, yl, Dummy);
+ else
+ *dst_map = imZeroOrderInterpolation(width, height, src_map, xl, yl);
+ }
+
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+//*******************************************************************************************
+//rotate_invtransf
+// shift the center to the origin of the destiny image
+// rotates centrered in the origin
+// shift the origin back to the center of the original image
+//*******************************************************************************************
+
+inline void rotate_invtransf(int x, int y, float *xl, float *yl, double cos0, double sin0, float dcx, float dcy, float scx, float scy)
+{
+ double xr = x+0.5 - dcx;
+ double yr = y+0.5 - dcy;
+ *xl = float(xr * cos0 - yr * sin0 + scx);
+ *yl = float(xr * sin0 + yr * cos0 + scy);
+}
+
+template <class DT, class DTU>
+static int RotateCenter(int src_width, int src_height, DT *src_map,
+ int dst_width, int dst_height, DT *dst_map,
+ double cos0, double sin0, int counter, DTU Dummy, int order)
+{
+ float xl, yl;
+ float dcx = float(dst_width/2.);
+ float dcy = float(dst_height/2.);
+ float scx = float(src_width/2.);
+ float scy = float(src_height/2.);
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ for (int x = 0; x < dst_width; x++)
+ {
+ rotate_invtransf(x, y, &xl, &yl, cos0, sin0, dcx, dcy, scx, scy);
+
+ // if inside the original image broad area
+ if (xl > 0.0 && yl > 0.0 && xl < src_width && yl < src_height)
+ {
+ if (order == 1)
+ *dst_map = imBilinearInterpolation(src_width, src_height, src_map, xl, yl);
+ else if (order == 3)
+ *dst_map = imBicubicInterpolation(src_width, src_height, src_map, xl, yl, Dummy);
+ else
+ *dst_map = imZeroOrderInterpolation(src_width, src_height, src_map, xl, yl);
+ }
+
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+template <class DT, class DTU>
+static int Rotate(int src_width, int src_height, DT *src_map,
+ int dst_width, int dst_height, DT *dst_map,
+ double cos0, double sin0, int ref_x, int ref_y, int to_origin,
+ int counter, DTU Dummy, int order)
+{
+ float xl, yl;
+ float sx = float(ref_x);
+ float sy = float(ref_y);
+ float dx = sx;
+ float dy = sy;
+ if (to_origin)
+ {
+ dx = 0;
+ dy = 0;
+ }
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ for (int x = 0; x < dst_width; x++)
+ {
+ rotate_invtransf(x, y, &xl, &yl, cos0, sin0, dx, dy, sx, sy);
+
+ // if inside the original image broad area
+ if (xl > 0.0 && yl > 0.0 && xl < src_width && yl < src_height)
+ {
+ if (order == 1)
+ *dst_map = imBilinearInterpolation(src_width, src_height, src_map, xl, yl);
+ else if (order == 3)
+ *dst_map = imBicubicInterpolation(src_width, src_height, src_map, xl, yl, Dummy);
+ else
+ *dst_map = imZeroOrderInterpolation(src_width, src_height, src_map, xl, yl);
+ }
+
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+template <class DT>
+static void Rotate90(int src_width,
+ int src_height,
+ DT *src_map,
+ int dst_width,
+ int dst_height,
+ DT *dst_map,
+ int dir)
+{
+ int xd,yd,x,y;
+
+ if (dir == 1)
+ xd = 0;
+ else
+ xd = dst_width - 1;
+
+ for(y = 0 ; y < src_height ; y++)
+ {
+ if (dir == 1)
+ yd = dst_height - 1;
+ else
+ yd = 0;
+
+ for(x = 0 ; x < src_width ; x++)
+ {
+ dst_map[yd * dst_width + xd] = src_map[y * src_width + x];
+
+ if (dir == 1)
+ yd--;
+ else
+ yd++;
+ }
+
+ if (dir == 1)
+ xd++;
+ else
+ xd--;
+ }
+}
+
+template <class DT>
+static void Rotate180(int src_width,
+ int src_height,
+ DT *src_map,
+ int dst_width,
+ int dst_height,
+ DT *dst_map)
+{
+ int xd,yd,x,y;
+
+ yd = dst_height - 1;
+
+ for(y = 0 ; y < src_height ; y++)
+ {
+ xd = dst_width - 1;
+
+ for(x = 0 ; x < src_width ; x++)
+ {
+ dst_map[yd * dst_width + xd] = src_map[y * src_width + x];
+ xd--;
+ }
+
+ yd--;
+ }
+}
+
+template <class DT>
+static void Mirror(int src_width,
+ int src_height,
+ DT *src_map,
+ int dst_width,
+ int dst_height,
+ DT *dst_map)
+{
+ int xd,x,y;
+ (void)dst_height;
+
+ if (src_map == dst_map) // check of in-place operation
+ {
+ int half_width = src_width/2;
+ for(y = 0 ; y < src_height; y++)
+ {
+ xd = dst_width - 1;
+
+ for(x = 0 ; x < half_width; x++)
+ {
+ DT temp_value = src_map[y * dst_width + xd];
+ src_map[y * dst_width + xd] = src_map[y * src_width + x];
+ src_map[y * src_width + x] = temp_value;
+ xd--;
+ }
+ }
+ }
+ else
+ {
+ for(y = 0 ; y < src_height; y++)
+ {
+ xd = dst_width - 1;
+
+ for(x = 0 ; x < src_width; x++)
+ {
+ dst_map[y * dst_width + xd] = src_map[y * src_width + x];
+ xd--;
+ }
+ }
+ }
+}
+
+template <class DT>
+static void Flip(int src_width,
+ int src_height,
+ DT *src_map,
+ int dst_width,
+ int dst_height,
+ DT *dst_map)
+{
+ int yd,y;
+
+ yd = dst_height - 1;
+
+ if (src_map == dst_map) // check of in-place operation
+ {
+ DT* temp_line = (DT*)malloc(src_width*sizeof(DT));
+ int half_height = src_height/2;
+
+ for(y = 0 ; y < half_height; y++)
+ {
+ memcpy(temp_line, dst_map+yd*dst_width, src_width * sizeof(DT));
+ memcpy(dst_map+yd*dst_width, src_map+y*src_width, src_width * sizeof(DT));
+ memcpy(src_map+y*src_width, temp_line,src_width * sizeof(DT));
+ yd--;
+ }
+
+ free(temp_line);
+ }
+ else
+ {
+ for(y = 0 ; y < src_height; y++)
+ {
+ memcpy(dst_map+yd*dst_width,src_map+y*src_width,src_width * sizeof(DT));
+ yd--;
+ }
+ }
+}
+
+template <class DT>
+static void InterlaceSplit(int src_width,
+ int src_height,
+ DT *src_map,
+ int dst_width,
+ DT *dst_map1,
+ DT *dst_map2)
+{
+ int yd = 0, y;
+
+ for(y = 0; y < src_height; y++)
+ {
+ if (y%2)
+ {
+ memcpy(dst_map2+yd*dst_width, src_map+y*src_width, src_width * sizeof(DT));
+ yd++; // increment only when odd
+ }
+ else
+ memcpy(dst_map1+yd*dst_width, src_map+y*src_width, src_width * sizeof(DT));
+ }
+}
+
+void imProcessRotate90(const imImage* src_image, imImage* dst_image, int dir)
+{
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ Rotate90(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i], dir);
+ break;
+ case IM_USHORT:
+ Rotate90(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i], dir);
+ break;
+ case IM_INT:
+ Rotate90(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i], dir);
+ break;
+ case IM_FLOAT:
+ Rotate90(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i], dir);
+ break;
+ case IM_CFLOAT:
+ Rotate90(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i], dir);
+ break;
+ }
+ }
+}
+
+void imProcessRotate180(const imImage* src_image, imImage* dst_image)
+{
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ Rotate180(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i]);
+ break;
+ case IM_USHORT:
+ Rotate180(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i]);
+ break;
+ case IM_INT:
+ Rotate180(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i]);
+ break;
+ case IM_FLOAT:
+ Rotate180(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i]);
+ break;
+ case IM_CFLOAT:
+ Rotate180(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i]);
+ break;
+ }
+ }
+}
+
+int imProcessRadial(const imImage* src_image, imImage* dst_image, float k1, int order)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("Radial Distort");
+ imCounterTotal(counter, dst_image->depth*dst_image->height, "Processing...");
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = Radial(src_image->width, src_image->height, (imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], k1, counter, float(0), order);
+ break;
+ case IM_USHORT:
+ ret = Radial(src_image->width, src_image->height, (imushort*)src_image->data[i], (imushort*)dst_image->data[i], k1, counter, float(0), order);
+ break;
+ case IM_INT:
+ ret = Radial(src_image->width, src_image->height, (int*)src_image->data[i], (int*)dst_image->data[i], k1, counter, float(0), order);
+ break;
+ case IM_FLOAT:
+ ret = Radial(src_image->width, src_image->height, (float*)src_image->data[i], (float*)dst_image->data[i], k1, counter, float(0), order);
+ break;
+ case IM_CFLOAT:
+ ret = Radial(src_image->width, src_image->height, (imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], k1, counter, imcfloat(0,0), order);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+int imProcessSwirl(const imImage* src_image, imImage* dst_image, float k, int order)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("Swirl Distort");
+ imCounterTotal(counter, dst_image->depth*dst_image->height, "Processing...");
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = Swirl(src_image->width, src_image->height, (imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], k, counter, float(0), order);
+ break;
+ case IM_USHORT:
+ ret = Swirl(src_image->width, src_image->height, (imushort*)src_image->data[i], (imushort*)dst_image->data[i], k, counter, float(0), order);
+ break;
+ case IM_INT:
+ ret = Swirl(src_image->width, src_image->height, (int*)src_image->data[i], (int*)dst_image->data[i], k, counter, float(0), order);
+ break;
+ case IM_FLOAT:
+ ret = Swirl(src_image->width, src_image->height, (float*)src_image->data[i], (float*)dst_image->data[i], k, counter, float(0), order);
+ break;
+ case IM_CFLOAT:
+ ret = Swirl(src_image->width, src_image->height, (imcfloat*)src_image->data[i], (imcfloat*)dst_image->data[i], k, counter, imcfloat(0,0), order);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+//*******************************************************************************************
+//rotate_transf
+// In this case shift to the origin, rotate, but do NOT shift back
+//*******************************************************************************************
+
+static void rotate_transf(float cx, float cy, int x, int y, float *xl, float *yl, double cos0, double sin0)
+{
+ double xr = x+0.5 - cx;
+ double yr = y+0.5 - cy;
+ *xl = float( xr*cos0 + yr*sin0);
+ *yl = float(-xr*sin0 + yr*cos0);
+}
+
+void imProcessCalcRotateSize(int width, int height, int *new_width, int *new_height, double cos0, double sin0)
+{
+ float xl, yl, xmin, xmax, ymin, ymax;
+ float wd2 = float(width)/2;
+ float hd2 = float(height)/2;
+
+ rotate_transf(wd2, hd2, 0, 0, &xl, &yl, cos0, sin0);
+ xmin = xl; ymin = yl;
+ xmax = xl; ymax = yl;
+
+ rotate_transf(wd2, hd2, width-1, height-1, &xl, &yl, cos0, sin0);
+ xmin = min_op(xmin, xl); ymin = min_op(ymin, yl);
+ xmax = max_op(xmax, xl); ymax = max_op(ymax, yl);
+
+ rotate_transf(wd2, hd2, 0, height-1, &xl, &yl, cos0, sin0);
+ xmin = min_op(xmin, xl); ymin = min_op(ymin, yl);
+ xmax = max_op(xmax, xl); ymax = max_op(ymax, yl);
+
+ rotate_transf(wd2, hd2, width-1, 0, &xl, &yl, cos0, sin0);
+ xmin = min_op(xmin, xl); ymin = min_op(ymin, yl);
+ xmax = max_op(xmax, xl); ymax = max_op(ymax, yl);
+
+ *new_width = (int)(xmax - xmin + 2.0);
+ *new_height = (int)(ymax - ymin + 2.0);
+}
+
+int imProcessRotate(const imImage* src_image, imImage* dst_image, double cos0, double sin0, int order)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("Rotate");
+ imCounterTotal(counter, dst_image->depth*dst_image->height, "Processing...");
+
+ if (src_image->color_space == IM_MAP)
+ {
+ ret = RotateCenter(src_image->width, src_image->height, (imbyte*)src_image->data[0], dst_image->width, dst_image->height, (imbyte*)dst_image->data[0], cos0, sin0, counter, float(0), 0);
+ }
+ else
+ {
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = RotateCenter(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i], cos0, sin0, counter, float(0), order);
+ break;
+ case IM_USHORT:
+ ret = RotateCenter(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i], cos0, sin0, counter, float(0), order);
+ break;
+ case IM_INT:
+ ret = RotateCenter(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i], cos0, sin0, counter, float(0), order);
+ break;
+ case IM_FLOAT:
+ ret = RotateCenter(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i], cos0, sin0, counter, float(0), order);
+ break;
+ case IM_CFLOAT:
+ ret = RotateCenter(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i], cos0, sin0, counter, imcfloat(0,0), order);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+int imProcessRotateRef(const imImage* src_image, imImage* dst_image, double cos0, double sin0, int x, int y, int to_origin, int order)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("RotateRef");
+ imCounterTotal(counter, dst_image->depth*dst_image->height, "Processing...");
+
+ if (src_image->color_space == IM_MAP)
+ {
+ ret = Rotate(src_image->width, src_image->height, (imbyte*)src_image->data[0], dst_image->width, dst_image->height, (imbyte*)dst_image->data[0], cos0, sin0, x, y, to_origin, counter, float(0), 0);
+ }
+ else
+ {
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = Rotate(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i], cos0, sin0, x, y, to_origin, counter, float(0), order);
+ break;
+ case IM_USHORT:
+ ret = Rotate(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i], cos0, sin0, x, y, to_origin, counter, float(0), order);
+ break;
+ case IM_INT:
+ ret = Rotate(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i], cos0, sin0, x, y, to_origin, counter, float(0), order);
+ break;
+ case IM_FLOAT:
+ ret = Rotate(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i], cos0, sin0, x, y, to_origin, counter, float(0), order);
+ break;
+ case IM_CFLOAT:
+ ret = Rotate(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i], cos0, sin0, x, y, to_origin, counter, imcfloat(0,0), order);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+void imProcessMirror(const imImage* src_image, imImage* dst_image)
+{
+ int i;
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ Mirror(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i]);
+ break;
+ case IM_USHORT:
+ Mirror(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i]);
+ break;
+ case IM_INT:
+ Mirror(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i]);
+ break;
+ case IM_FLOAT:
+ Mirror(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i]);
+ break;
+ case IM_CFLOAT:
+ Mirror(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i]);
+ break;
+ }
+ }
+}
+
+void imProcessFlip(const imImage* src_image, imImage* dst_image)
+{
+ int i;
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ Flip(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i]);
+ break;
+ case IM_USHORT:
+ Flip(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i]);
+ break;
+ case IM_INT:
+ Flip(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i]);
+ break;
+ case IM_FLOAT:
+ Flip(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i]);
+ break;
+ case IM_CFLOAT:
+ Flip(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i]);
+ break;
+ }
+ }
+}
+
+void imProcessInterlaceSplit(const imImage* src_image, imImage* dst_image1, imImage* dst_image2)
+{
+ int i;
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ InterlaceSplit(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image1->width, (imbyte*)dst_image1->data[i], (imbyte*)dst_image2->data[i]);
+ break;
+ case IM_USHORT:
+ InterlaceSplit(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image1->width, (imushort*)dst_image1->data[i], (imushort*)dst_image2->data[i]);
+ break;
+ case IM_INT:
+ InterlaceSplit(src_image->width, src_image->height, (int*)src_image->data[i], dst_image1->width, (int*)dst_image1->data[i], (int*)dst_image2->data[i]);
+ break;
+ case IM_FLOAT:
+ InterlaceSplit(src_image->width, src_image->height, (float*)src_image->data[i], dst_image1->width, (float*)dst_image1->data[i], (float*)dst_image2->data[i]);
+ break;
+ case IM_CFLOAT:
+ InterlaceSplit(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image1->width, (imcfloat*)dst_image1->data[i], (imcfloat*)dst_image2->data[i]);
+ break;
+ }
+ }
+}
diff --git a/src/process/im_histogram.cpp b/src/process/im_histogram.cpp
new file mode 100644
index 0000000..e6796fe
--- /dev/null
+++ b/src/process/im_histogram.cpp
@@ -0,0 +1,105 @@
+/** \file
+ * \brief Histogram Based Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_histogram.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+
+#include "im_process_pon.h"
+#include "im_process_ana.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+static void iExpandHistogram(const imImage* src_image, imImage* dst_image, int low_level, int high_level)
+{
+ int i, value;
+
+ imbyte re_map[256];
+ memset(re_map, 0, 256);
+
+ int range = high_level-low_level+1;
+ float factor = 256.0f / (float)range;
+
+ for (i = 0; i < 256; i++)
+ {
+ if (i < low_level)
+ re_map[i] = 0;
+ else if (i > high_level)
+ re_map[i] = 255;
+ else
+ {
+ value = imResample(i - low_level, factor);
+ re_map[i] = (imbyte)IM_BYTECROP(value);
+ }
+ }
+
+ imbyte* dst_map = (imbyte*)dst_image->data[0];
+ imbyte* src_map = (imbyte*)src_image->data[0];
+ int total_count = src_image->count*src_image->depth;
+ for (i = 0; i < total_count; i++)
+ dst_map[i] = re_map[src_map[i]];
+}
+
+void imProcessExpandHistogram(const imImage* src_image, imImage* dst_image, float percent)
+{
+ unsigned long histo[256];
+ imCalcGrayHistogram(src_image, histo, 0);
+
+ unsigned long acum, cut = (unsigned long)((src_image->count * percent) / 100.0f);
+ int low_level, high_level;
+
+ acum = 0;
+ for (low_level = 0; low_level < 256; low_level++)
+ {
+ acum += histo[low_level];
+ if (acum > cut)
+ break;
+ }
+
+ acum = 0;
+ for (high_level = 255; high_level > 0; high_level--)
+ {
+ acum += histo[high_level];
+ if (acum > cut)
+ break;
+ }
+
+ if (low_level >= high_level)
+ {
+ low_level = 0;
+ high_level = 255;
+ }
+
+ iExpandHistogram(src_image, dst_image, low_level, high_level);
+}
+
+void imProcessEqualizeHistogram(const imImage* src_image, imImage* dst_image)
+{
+ int i, value;
+
+ imbyte re_map[256];
+ memset(re_map, 0, 256);
+
+ unsigned long histo[256];
+ imCalcGrayHistogram(src_image, histo, 1);
+
+ float factor = 256.0f / (float)src_image->count;
+
+ for (i = 0; i < 256; i++)
+ {
+ value = imResample(histo[i], factor);
+ re_map[i] = (imbyte)IM_BYTECROP(value);
+ }
+
+ imbyte* dst_map = (imbyte*)dst_image->data[0];
+ imbyte* src_map = (imbyte*)src_image->data[0];
+ int total_count = src_image->count*src_image->depth;
+ for (i = 0; i < total_count; i++)
+ dst_map[i] = re_map[src_map[i]];
+}
diff --git a/src/process/im_houghline.cpp b/src/process/im_houghline.cpp
new file mode 100644
index 0000000..6ead982
--- /dev/null
+++ b/src/process/im_houghline.cpp
@@ -0,0 +1,435 @@
+/** \file
+ * \brief Hough Transform
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_houghline.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+#include <im.h>
+#include <im_util.h>
+#include <im_complex.h>
+#include <im_convert.h>
+#include <im_counter.h>
+
+#include "im_process_glo.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+
+#ifndef M_PI
+#define M_PI 3.14159265358979323846
+#endif
+
+static double *costab=NULL, *sintab=NULL;
+
+static int hgAbs(int x)
+{
+ return x < 0? -x: x;
+}
+
+typedef struct _point
+{
+ int rho, theta, count;
+} point;
+
+typedef struct _listnode
+{
+ struct _listnode *next;
+ point pt;
+} listnode;
+
+static listnode* listnew(point *pt)
+{
+ listnode* node = (listnode*)malloc(sizeof(listnode));
+ node->next = NULL;
+ node->pt = *pt;
+ return node;
+}
+
+static listnode* listadd(listnode* node, point *pt)
+{
+ node->next = listnew(pt);
+ return node->next;
+}
+
+/* minimum angle to match similar angles */
+#define THETA_DELTA1 0.05 /* radians */
+#define THETA_DELTA2 3 /* degrees */
+
+static int ptNear(point* pt1, point* pt2, int rho_delta)
+{
+ int theta_diff = hgAbs(pt1->theta - pt2->theta);
+ if ((hgAbs(pt1->rho - pt2->rho) < rho_delta && theta_diff < THETA_DELTA2) ||
+ (hgAbs(pt1->rho + pt2->rho) < rho_delta && 180-theta_diff < THETA_DELTA2))
+ {
+ if (pt2->count > pt1->count)
+ return 2; /* replace the line */
+ else
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static listnode* listadd_filtered(listnode* list, listnode* cur_node, point *pt, int rho_delta)
+{
+ int ret;
+ listnode* lt = list;
+ while (lt)
+ {
+ ret = ptNear(&lt->pt, pt, rho_delta);
+ if (ret)
+ {
+ if (ret == 2)
+ lt->pt = *pt; /* replace the line */
+ return cur_node;
+ }
+ lt = lt->next;
+ }
+
+ cur_node->next = listnew(pt);
+ return cur_node->next;
+}
+
+/*C* Initial version from XITE
+
+ houghLine
+ $Id: im_houghline.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ Copyright 1990, Blab, UiO
+ Image processing lab, Department of Informatics
+ University of Oslo
+ E-mail: blab@ifi.uio.no
+________________________________________________________________
+
+ houghLine - Hough transform for line detection
+
+ Description:
+ Performs a Hough transform to detect lines. Every band in the
+ input image 'inimage' is transformed to a two dimensional
+ Hough space, a (theta, rho) space.
+
+ After creating the transform, the Hough space may be searched
+ for local maxima. Within each band, only the largest local
+ maximum (maxima) within a 'ws'x'ws' area is registered.
+ Besides, only maxima with number of updates above a limit
+ given by the ul option are used.
+
+ updateLimit determines the minimum number of updates for a maximum
+ to be used. The minimum number is determined from 'updateLimit'
+ and the size of the hough space image:
+ | updateLimit * MAX(horizontal size, vertical size)
+ Default: 0.1.
+
+ All pixels above zero in the 'input' band are
+ transformed to (theta,rho) space in the 'output'
+ band. The 'input' band may have any size, while
+ the 'output' band currently must be at least
+ | xsize: 180
+ | ysize: 2 * sqrt(inputXsize*inputXsize +
+ | inputYsize*inputYsize) + 1
+
+ Notice that band x coordinates 1..180 correspond
+ to angles theta = 0 .. 179, and y coordinates
+ 1..YSIZE correspond to rho = -(ysize/2) .. ysize/2.
+
+ Restrictions:
+ 'input' must have pixel type imbyte.
+ 'output' must have pixel type int.
+
+ Author: Tor Lønnestad, BLAB, Ifi, UiO
+*/
+
+
+static int houghLine(const imImage* input, imImage* output, int counter)
+{
+ int ixsize, iysize, ixhalf, iyhalf, thetamax, x, y, rho, theta, rhomax;
+ imbyte *input_map = (imbyte*)input->data[0];
+ int *output_map = (int*)output->data[0];
+
+ ixsize = input->width;
+ iysize = input->height;
+ ixhalf = ixsize/2;
+ iyhalf = iysize/2;
+
+ thetamax = output->width; /* theta max = 180 */
+ rhomax = output->height/2; /* rho shift to 0, -rmax <= r <= +rmax */
+
+ costab = (double*)malloc(thetamax*sizeof(double));
+ sintab = (double*)malloc(thetamax*sizeof(double));
+
+ for (theta=0; theta < thetamax; theta++)
+ {
+ double th = (M_PI*theta)/thetamax;
+ costab[theta] = cos(th);
+ sintab[theta] = sin(th);
+ }
+
+ for (y=0; y < iysize; y++)
+ {
+ for (x=0; x < ixsize; x++)
+ {
+ if (input_map[y*ixsize + x])
+ {
+ for (theta=0; theta < thetamax; theta++)
+ {
+ rho = imRound((x-ixhalf)*costab[theta] + (y-iyhalf)*sintab[theta]);
+ if (rho > rhomax) continue;
+ if (rho < -rhomax) continue;
+ output_map[(rho+rhomax)*thetamax + theta]++;
+ }
+ }
+ }
+
+ if (!imCounterInc(counter))
+ {
+ free(costab); costab = NULL;
+ free(sintab); sintab = NULL;
+ return 0;
+ }
+ }
+
+ free(costab); costab = NULL;
+ free(sintab); sintab = NULL;
+
+ return 1;
+}
+
+static listnode* findMaxima(const imImage* hough_points, int *line_count, const imImage* hough)
+{
+ int x, y, xsize, ysize, rhomax, offset, rho_delta = 0;
+ listnode* maxima = NULL, *cur_node = NULL;
+ point pt;
+ imbyte *map = (imbyte*)hough_points->data[0];
+ int *hough_map = NULL;
+
+ xsize = hough_points->width; /* X = theta */
+ ysize = hough_points->height; /* Y = rho */
+ rhomax = ysize/2;
+
+ if (hough)
+ {
+ hough_map = (int*)hough->data[0];
+ rho_delta = (int)(rhomax*tan(THETA_DELTA1));
+ }
+
+ for (y=0; y < ysize; y++)
+ {
+ for (x=0; x < xsize; x++)
+ {
+ offset = y*xsize + x;
+
+ if (map[offset])
+ {
+ pt.theta = x;
+ pt.rho = y-rhomax;
+
+ if (!maxima)
+ {
+ cur_node = maxima = listnew(&pt);
+ (*line_count)++;
+ }
+ else
+ {
+ if (hough_map)
+ {
+ listnode* old_node = cur_node;
+ pt.count = hough_map[offset];
+ cur_node = listadd_filtered(maxima, cur_node, &pt, rho_delta);
+ if (cur_node != old_node)
+ (*line_count)++;
+ }
+ else
+ {
+ cur_node = listadd(cur_node, &pt);
+ (*line_count)++;
+ }
+ }
+ }
+ }
+ }
+
+ return maxima;
+}
+
+#define SWAPINT(a, b) {int t = a; a = b; b = t; }
+
+static void drawLine(imImage* image, int theta, int rho)
+{
+ int xsize, ysize, xstart, xstop, ystart, ystop, xhalf, yhalf;
+ float a, b;
+ imbyte *map = (imbyte*)image->data[0];
+
+ xsize = image->width;
+ ysize = image->height;
+ xhalf = xsize/2;
+ yhalf = ysize/2;
+
+ if (theta == 0) /* vertical line */
+ {
+ int y;
+ if (rho+xhalf < 0 || rho+xhalf > xsize-1) return;
+ for (y=0; y < ysize; y++)
+ map[y*xsize + rho+xhalf]=254;
+
+ return;
+ }
+
+ if (theta == 90) /* horizontal line */
+ {
+ int x;
+ if (rho+yhalf < 0 || rho+yhalf > ysize-1) return;
+ for (x=0; x < xsize; x++)
+ map[(rho+yhalf)*xsize + x]=254;
+
+ return;
+ }
+
+ a = (float)(-costab[theta]/sintab[theta]);
+ b = (float)((rho + xhalf*costab[theta] + yhalf*sintab[theta])/sintab[theta]);
+
+ {
+ int x[2];
+ int y[2];
+ int c = 0;
+ int y1 = imRound(b); /* x = 0 */
+ int y2 = imRound(a*(xsize-1)+b); /* x = xsize-1 */
+
+ int x1 = imRound(-b/a); /* y = 0 */
+ int x2 = imRound((ysize-1-b)/a); /* y = ysize-1 */
+
+ if (y1 >= 0 && y1 < ysize)
+ {
+ y[c] = y1;
+ x[c] = 0;
+ c++;
+ }
+
+ if (y2 >= 0 && y2 < ysize)
+ {
+ y[c] = y2;
+ x[c] = xsize-1;
+ c++;
+ }
+
+ if (c < 2 && x1 >= 0 && x1 < xsize)
+ {
+ x[c] = x1;
+ y[c] = 0;
+ c++;
+ }
+
+ if (c < 2 && x2 >= 0 && x2 < xsize)
+ {
+ x[c] = x2;
+ y[c] = ysize-1;
+ c++;
+ }
+
+ if (c < 2) return;
+
+ ystart = y[0];
+ xstart = x[0];
+ ystop = y[1];
+ xstop = x[1];
+ }
+
+ {
+ int x, y;
+ if (45 <= theta && theta <= 135)
+ {
+ if (xstart > xstop)
+ SWAPINT(xstart, xstop);
+
+ for (x=xstart; x <= xstop; x++)
+ {
+ y = imRound(a*x + b);
+ if (y < 0) continue;
+ if (y > ysize-1) continue;
+ map[y*xsize + x]=254;
+ }
+ }
+ else
+ {
+ if (ystart > ystop)
+ SWAPINT(ystart, ystop);
+
+ for (y=ystart; y <= ystop; y++)
+ {
+ x = imRound((y-b)/a);
+ if (x < 0) continue;
+ if (x > xsize-1) continue;
+ map[y*xsize + x]=254;
+ }
+ }
+ }
+}
+
+int imProcessHoughLines(const imImage* image, imImage *NewImage)
+{
+ int counter = imCounterBegin("Hough Line Transform");
+ imCounterTotal(counter, image->height, "Processing...");
+
+ int ret = houghLine(image, NewImage, counter);
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static void DrawPoints(imImage *image, listnode* maxima)
+{
+ listnode* cur_node;
+ while (maxima)
+ {
+ cur_node = maxima;
+ drawLine(image, cur_node->pt.theta, cur_node->pt.rho);
+ maxima = cur_node->next;
+ free(cur_node);
+ }
+}
+
+static void ReplaceColor(imImage* NewImage)
+{
+ int i;
+ imbyte* map = (imbyte*)NewImage->data[0];
+
+ NewImage->color_space = IM_MAP;
+ NewImage->palette[254] = imColorEncode(255, 0, 0);
+
+ for (i = 0; i < NewImage->count; i++)
+ {
+ if (map[i] == 254)
+ map[i] = 255;
+ }
+}
+
+int imProcessHoughLinesDraw(const imImage* original_image, const imImage *hough, const imImage *hough_points, imImage *NewImage)
+{
+ int theta, line_count = 0;
+
+ if (original_image != NewImage)
+ imImageCopyData(original_image, NewImage);
+
+ listnode* maxima = findMaxima(hough_points, &line_count, hough);
+
+ ReplaceColor(NewImage);
+
+ costab = (double*)malloc(180*sizeof(double));
+ sintab = (double*)malloc(180*sizeof(double));
+
+ for (theta=0; theta < 180; theta++)
+ {
+ double th = (M_PI*theta)/180.;
+ costab[theta] = cos(th);
+ sintab[theta] = sin(th);
+ }
+
+ DrawPoints(NewImage, maxima);
+
+ free(costab); costab = NULL;
+ free(sintab); sintab = NULL;
+
+ return line_count;
+}
+
diff --git a/src/process/im_kernel.cpp b/src/process/im_kernel.cpp
new file mode 100644
index 0000000..d5e976e
--- /dev/null
+++ b/src/process/im_kernel.cpp
@@ -0,0 +1,293 @@
+/** \file
+ * \brief Kernel Generators
+ * Creates several known kernels
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_kernel.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+#include "im.h"
+#include "im_util.h"
+#include "im_image.h"
+#include "im_kernel.h"
+
+#include <stdlib.h>
+#include <memory.h>
+#include <assert.h>
+#include <math.h>
+
+
+static imImage* iKernelCreate(int w, int h, int* data, char* desc)
+{
+ imImage* kernel = imImageCreate(w, h, IM_GRAY, IM_INT);
+ int* kernel_data = (int*)kernel->data[0];
+ memcpy(kernel_data, data, kernel->size);
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)desc);
+ return kernel;
+}
+
+imImage* imKernelSobel(void)
+{
+ int kernel_data[3*3] = {
+ -1, -2, -1,
+ 0, 0, 0,
+ 1, 2, 1
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Sobel");
+}
+
+imImage* imKernelPrewitt(void)
+{
+ int kernel_data[3*3] = {
+ -1, -1, -1,
+ 0, 0, 0,
+ 1, 1, 1
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Prewitt");
+}
+
+imImage* imKernelKirsh(void)
+{
+ int kernel_data[3*3] = {
+ -3, -3, -3,
+ -3, 0, -3,
+ 5, 5, 5
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Kirsh");
+}
+
+imImage* imKernelLaplacian4(void)
+{
+ int kernel_data[3*3] = {
+ 0, -1, 0,
+ -1, 4, -1,
+ 0, -1, 0
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Laplacian4");
+}
+
+imImage* imKernelLaplacian8(void)
+{
+ int kernel_data[3*3] = {
+ -1, -1, -1,
+ -1, 8, -1,
+ -1, -1, -1
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Laplacian8");
+}
+
+imImage* imKernelLaplacian5x5(void)
+{
+ int kernel_data[5*5] = {
+ 0, -1, -1, -1, 0,
+ -1, 0, 1, 0, -1,
+ -1, 1, 8, 1, -1,
+ -1, 0, 1, 0, -1,
+ 0, -1, -1, -1, 0
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "Laplacian5x5");
+}
+
+imImage* imKernelLaplacian7x7(void)
+{
+ int kernel_data[7*7] = {
+ -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, 48, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1
+ };
+
+ return iKernelCreate(7, 7, kernel_data, "Laplacian7x7");
+}
+
+imImage* imKernelGradian3x3(void)
+{
+ int kernel_data[3*3] = {
+ 0, -1, 0,
+ 0, 1, 0,
+ 0, 0, 0
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Gradian3x3");
+}
+
+imImage* imKernelGradian7x7(void)
+{
+ int kernel_data[7*7] = {
+ 0, -1, -1, 0, 1, 1, 0,
+ -1, -2, -2, 0, 2, 2, 1,
+ -1, -2, -3, 0, 3, 2, 1,
+ -1, -2, -3, 0, 3, 2, 1,
+ -1, -2, -3, 0, 3, 2, 1,
+ -1, -2, -2, 0, 2, 2, 1,
+ 0, -1, -1, 0, 1, 1, 0
+ };
+
+ return iKernelCreate(7, 7, kernel_data, "Gradian7x7");
+}
+
+imImage* imKernelSculpt(void)
+{
+ int kernel_data[3*3] = {
+ 0, 0, 1,
+ 0, 0, 0,
+ -1, 0, 0
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Sculpt");
+}
+
+imImage* imKernelMean3x3(void)
+{
+ int kernel_data[3*3] = {
+ 1, 1, 1,
+ 1, 1, 1,
+ 1, 1, 1
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Mean3x3");
+}
+
+imImage* imKernelMean5x5(void)
+{
+ int kernel_data[5*5] = {
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "Mean5x5");
+}
+
+imImage* imKernelCircularMean5x5(void)
+{
+ int kernel_data[5*5] = {
+ 0, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 0
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "CircularMean5x5");
+}
+
+imImage* imKernelMean7x7(void)
+{
+ int kernel_data[7*7] = {
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1
+ };
+
+ return iKernelCreate(7, 7, kernel_data, "Mean7x7");
+}
+
+imImage* imKernelCircularMean7x7(void)
+{
+ int kernel_data[7*7] = {
+ 0, 0, 1, 1, 1, 0, 0,
+ 0, 1, 1, 1, 1, 1, 0,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1,
+ 0, 1, 1, 1, 1, 1, 0,
+ 0, 0, 1, 1, 1, 0, 0
+ };
+
+ return iKernelCreate(7, 7, kernel_data, "CircularMean7x7");
+}
+
+imImage* imKernelGaussian3x3(void)
+{
+ int kernel_data[3*3] = {
+ 1, 2, 1,
+ 2, 4, 2,
+ 1, 2, 1
+ };
+
+ return iKernelCreate(3, 3, kernel_data, "Gaussian3x3");
+}
+
+imImage* imKernelGaussian5x5(void)
+{
+ int kernel_data[5*5] = {
+ 1, 4, 6, 4, 1,
+ 4, 16, 24, 16, 4,
+ 6, 24, 36, 24, 6,
+ 4, 16, 24, 16, 4,
+ 1, 4, 6, 4, 1
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "Gaussian5x5");
+}
+
+imImage* imKernelBarlett5x5(void)
+{
+ int kernel_data[5*5] = {
+ 1, 2, 3, 2, 1,
+ 2, 4, 6, 4, 2,
+ 3, 6, 9, 6, 3,
+ 2, 4, 6, 4, 2,
+ 1, 2, 3, 2, 1
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "Barlett5x5");
+}
+
+imImage* imKernelTopHat5x5(void)
+{
+ int kernel_data[5*5] = {
+ 0, -1, -1, -1, 0,
+ -1, -1, 3, -1, -1,
+ -1, 3, 4, 3, -1,
+ -1, -1, 3, -1, -1,
+ 0, -1, -1, -1, 0
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "TopHat5x5");
+}
+
+imImage* imKernelTopHat7x7(void)
+{
+ int kernel_data[7*7] = {
+ 0, 0, -1, -1, -1, 0, 0,
+ 0, -1, -1, -1, -1, -1, 0,
+ -1, -1, 3, 3, 3, -1, -1,
+ -1, -1, 3, 4, 3, -1, -1,
+ -1, -1, 3, 3, 3, -1, -1,
+ 0, -1, -1, -1, -1, -1, 0,
+ 0, 0, -1, -1, -1, 0, 0
+ };
+
+ return iKernelCreate(7, 7, kernel_data, "TopHat7x7");
+}
+
+imImage* imKernelEnhance(void)
+{
+ int kernel_data[5*5] = {
+ 0, -1, -2, -1, 0,
+ -1, -4, 0, -4, -1,
+ -2, 0, 40, 0, -2,
+ -1, -4, 0, -4, -1,
+ 0, -1, -2, -1, 0
+ };
+
+ return iKernelCreate(5, 5, kernel_data, "Enhance");
+}
+
diff --git a/src/process/im_logic.cpp b/src/process/im_logic.cpp
new file mode 100644
index 0000000..82e607d
--- /dev/null
+++ b/src/process/im_logic.cpp
@@ -0,0 +1,136 @@
+/** \file
+ * \brief Logical Arithmetic Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_logic.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+template <class T>
+static void DoBitwiseOp(T *map1, T *map2, T *map, int count, int op)
+{
+ int i;
+
+ switch(op)
+ {
+ case IM_BIT_AND:
+ for (i = 0; i < count; i++)
+ map[i] = map1[i] & map2[i];
+ break;
+ case IM_BIT_OR:
+ for (i = 0; i < count; i++)
+ map[i] = map1[i] | map2[i];
+ break;
+ case IM_BIT_XOR:
+ for (i = 0; i < count; i++)
+ map[i] = (T)~(map1[i] | map2[i]);
+ break;
+ }
+}
+
+void imProcessBitwiseOp(const imImage* src_image1, const imImage* src_image2, imImage* dst_image, int op)
+{
+ int count = src_image1->count*src_image1->depth;
+
+ switch(src_image1->data_type)
+ {
+ case IM_BYTE:
+ DoBitwiseOp((imbyte*)src_image1->data[0], (imbyte*)src_image2->data[0], (imbyte*)dst_image->data[0], count, op);
+ break;
+ case IM_USHORT:
+ DoBitwiseOp((imushort*)src_image1->data[0], (imushort*)src_image2->data[0], (imushort*)dst_image->data[0], count, op);
+ break;
+ case IM_INT:
+ DoBitwiseOp((int*)src_image1->data[0], (int*)src_image2->data[0], (int*)dst_image->data[0], count, op);
+ break;
+ }
+}
+
+template <class T>
+static void DoBitwiseNot(T *map1, T *map, int count)
+{
+ for (int i = 0; i < count; i++)
+ map[i] = ~map1[i];
+}
+
+static void DoBitwiseNotBin(imbyte *map1, imbyte *map, int count)
+{
+ for (int i = 0; i < count; i++)
+ map[i] = map1[i]? 0: 1;
+}
+
+void imProcessBitwiseNot(const imImage* src_image, imImage* dst_image)
+{
+ int count = src_image->count*src_image->depth;
+
+ if (dst_image->color_space == IM_BINARY)
+ {
+ DoBitwiseNotBin((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], count);
+ return;
+ }
+
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ DoBitwiseNot((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], count);
+ break;
+ case IM_USHORT:
+ DoBitwiseNot((imushort*)src_image->data[0], (imushort*)dst_image->data[0], count);
+ break;
+ case IM_INT:
+ DoBitwiseNot((int*)src_image->data[0], (int*)dst_image->data[0], count);
+ break;
+ }
+}
+
+void imProcessBitMask(const imImage* src_image, imImage* dst_image, unsigned char mask, int op)
+{
+ imbyte* src_map = (imbyte*)src_image->data[0];
+ imbyte* dst_map = (imbyte*)dst_image->data[0];
+ int i;
+ int count = dst_image->count * dst_image->depth;
+ switch(op)
+ {
+ case IM_BIT_AND:
+ for (i = 0; i < count; i++)
+ *dst_map++ = *src_map++ & mask;
+ break;
+ case IM_BIT_OR:
+ for (i = 0; i < count; i++)
+ *dst_map++ = *src_map++ | mask;
+ break;
+ case IM_BIT_XOR:
+ for (i = 0; i < count; i++)
+ *dst_map++ = (imbyte)~(*src_map++ | mask);
+ break;
+ }
+
+ if ((op == IM_BIT_XOR || op == IM_BIT_OR) && dst_image->color_space == IM_BINARY && mask > 1)
+ dst_image->color_space = IM_GRAY;
+}
+
+void imProcessBitPlane(const imImage* src_image, imImage* dst_image, int plane, int reset)
+{
+ imbyte mask = imbyte(0x01 << plane);
+ if (reset) mask = ~mask;
+ imbyte* src_map = (imbyte*)src_image->data[0];
+ imbyte* dst_map = (imbyte*)dst_image->data[0];
+ int count = dst_image->count * dst_image->depth;
+ for (int i = 0; i < count; i++)
+ {
+ if (reset)
+ *dst_map++ = *src_map & mask;
+ else
+ *dst_map++ = (*src_map & mask)? 1: 0;
+
+ src_map++;
+ }
+}
diff --git a/src/process/im_morphology_bin.cpp b/src/process/im_morphology_bin.cpp
new file mode 100644
index 0000000..9405ff6
--- /dev/null
+++ b/src/process/im_morphology_bin.cpp
@@ -0,0 +1,317 @@
+/** \file
+ * \brief Morphology Operations for Binary Images
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_morphology_bin.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_counter.h>
+
+#include "im_process_loc.h"
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+static int DoBinMorphConvolve(imbyte *map, imbyte* new_map, int width, int height, const imImage* kernel, int counter, int hit_value, int miss_value)
+{
+ int *kernel_line;
+ int offset, new_offset, i, j, x, y;
+ int kh, kw, kh2, kw2, hit;
+
+ kh = kernel->height;
+ kw = kernel->width;
+ kh2 = kernel->height/2;
+ kw2 = kernel->width/2;
+
+ int* kernel_data = (int*)kernel->data[0];
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ hit = 1;
+
+ for(y = -kh2; y <= kh2 && hit; y++)
+ {
+ kernel_line = kernel_data + (y+kh2)*kernel->width;
+
+ if ((j + y < 0) || // pass the bottom border
+ (j + y >= height)) // pass the top border
+ offset = -1;
+ else
+ offset = (j + y) * width;
+
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if ((offset == -1) ||
+ (i + x < 0) || // pass the left border
+ (i + x >= width)) // pass the right border
+ {
+ if(kernel_line[x+kw2] != -1 && kernel_line[x+kw2] != 0) // 0 extension beyond borders
+ hit = 0;
+ }
+ else
+ {
+ if(kernel_line[x+kw2] != -1 && kernel_line[x+kw2] != map[offset + (i + x)])
+ hit = 0;
+ }
+ }
+ }
+
+ new_map[new_offset + i] = (imbyte)(hit? hit_value: miss_value);
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+int imProcessBinMorphConvolve(const imImage* src_image, imImage* dst_image, const imImage *kernel, int hit_white, int iter)
+{
+ int j, ret = 0, hit_value, miss_value;
+ void *tmp = NULL;
+ int counter;
+
+ if (hit_white)
+ {
+ hit_value = 1;
+ miss_value = 0;
+ }
+ else
+ {
+ hit_value = 0;
+ miss_value = 1;
+ }
+
+ counter = imCounterBegin("Binary Morphological Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel, "Description", NULL, NULL);
+ if (!msg) msg = "Processing...";
+ imCounterTotal(counter, src_image->height*iter, msg);
+
+ if (iter > 1)
+ tmp = malloc(src_image->size);
+
+ for (j = 0; j < iter; j++)
+ {
+ if (j == 0)
+ ret = DoBinMorphConvolve((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], src_image->width, src_image->height, kernel, counter, hit_value, miss_value);
+ else
+ {
+ memcpy(tmp, dst_image->data[0], src_image->size);
+ ret = DoBinMorphConvolve((imbyte*)tmp, (imbyte*)dst_image->data[0], src_image->width, src_image->height, kernel, counter, hit_value, miss_value);
+ }
+
+ if (!ret)
+ break;
+ }
+
+ if (tmp) free(tmp);
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+int imProcessBinMorphErode(const imImage* src_image, imImage* dst_image, int kernel_size, int iter)
+{
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_INT);
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Erode");
+
+ int* kernel_data = (int*)kernel->data[0];
+ for(int i = 0; i < kernel->count; i++)
+ kernel_data[i] = 1;
+
+ int ret = imProcessBinMorphConvolve(src_image, dst_image, kernel, 1, iter);
+ imImageDestroy(kernel);
+ return ret;
+}
+
+int imProcessBinMorphDilate(const imImage* src_image, imImage* dst_image, int kernel_size, int iter)
+{
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_INT);
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Dilate");
+ // Kernel is all zeros
+ int ret = imProcessBinMorphConvolve(src_image, dst_image, kernel, 0, iter);
+ imImageDestroy(kernel);
+ return ret;
+}
+
+int imProcessBinMorphOpen(const imImage* src_image, imImage* dst_image, int kernel_size, int iter)
+{
+ imImage*temp = imImageClone(src_image);
+ if (!temp)
+ return 0;
+
+ if (!imProcessBinMorphErode(src_image, temp, kernel_size, iter)) {imImageDestroy(temp); return 0;}
+ if (!imProcessBinMorphDilate(temp, dst_image, kernel_size, iter)) {imImageDestroy(temp); return 0;}
+
+ imImageDestroy(temp);
+ return 1;
+}
+
+int imProcessBinMorphClose(const imImage* src_image, imImage* dst_image, int kernel_size, int iter)
+{
+ imImage*temp = imImageClone(src_image);
+ if (!temp)
+ return 0;
+
+ if (!imProcessBinMorphDilate(src_image, temp, kernel_size, iter)) {imImageDestroy(temp); return 0;}
+ if (!imProcessBinMorphErode(temp, dst_image, kernel_size, iter)) {imImageDestroy(temp); return 0;}
+
+ imImageDestroy(temp);
+ return 1;
+}
+
+int imProcessBinMorphOutline(const imImage* src_image, imImage* dst_image, int kernel_size, int iter)
+{
+ if (!imProcessBinMorphErode(src_image, dst_image, kernel_size, iter)) return 0;
+ imProcessArithmeticOp(src_image, dst_image, dst_image, IM_BIN_DIFF);
+ return 1;
+}
+
+/* Direction masks: */
+/* N S W E */
+static int masks[] = { 0200, 0002, 0040, 0010 };
+
+/* True if pixel neighbor map indicates the pixel is 8-simple and */
+/* not an end point and thus can be deleted. The neighborhood */
+/* map is defined as an integer of bits abcdefghi with a non-zero */
+/* bit representing a non-zero pixel. The bit assignment for the */
+/* neighborhood is: */
+/* */
+/* a b c */
+/* d e f */
+/* g h i */
+
+static unsigned char isdelete[512] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1
+};
+
+static void DoThinImage(imbyte *map, int xsize, int ysize)
+{
+ int x, y; /* Pixel location */
+ int i; /* Pass index */
+ int pc = 0; /* Pass count */
+ int count = 1; /* Deleted pixel count */
+ int p, q; /* Neighborhood maps of adjacent cells */
+ imbyte *qb; /* Neighborhood maps of previous scanline */
+ int m; /* Deletion direction mask */
+
+ qb = (imbyte *) malloc(xsize);
+ qb[xsize-1] = 0; /* Used for lower-right pixel */
+
+ while ( count )
+ {
+ /* Scan src_image while deletions */
+ pc++;
+ count = 0;
+
+ for ( i = 0 ; i < 4 ; i++ )
+ {
+ m = masks[i];
+
+ /* Build initial previous scan buffer. */
+
+ p = map[0] != 0;
+ for (x = 0 ; x < xsize-1 ; x++)
+ {
+ p = ((p<<1)&0006) | (map[x+1] != 0);
+ qb[x] = (imbyte)p;
+ }
+
+ /* Scan src_image for pixel deletion candidates. */
+
+ for ( y = 0 ; y < ysize-1 ; y++ )
+ {
+ q = qb[0];
+ p = ((q<<3)&0110) | (map[(y+1)*xsize] != 0);
+
+ for ( x = 0 ; x < xsize-1 ; x++ )
+ {
+ q = qb[x];
+ p = ((p<<1)&0666) | ((q<<3)&0110) | (map[(y+1)*xsize + x+1] != 0);
+ qb[x] = (imbyte)p;
+
+ if (((p&m) == 0) && isdelete[p] )
+ {
+ count++;
+ map[y*xsize + x] = 0;
+ }
+ }
+
+ /* Process right edge pixel. */
+
+ p = (p<<1)&0666;
+ if ( (p&m) == 0 && isdelete[p] )
+ {
+ count++;
+ map[y*xsize + xsize-1] = 0;
+ }
+ }
+
+ /* Process bottom scan line. */
+
+ for ( x = 0 ; x < xsize ; x++ )
+ {
+ q = qb[x];
+ p = ((p<<1)&0666) | ((q<<3)&0110);
+
+ if ( (p&m) == 0 && isdelete[p] )
+ {
+ count++;
+ map[(ysize-1)*xsize + x] = 0;
+ }
+ }
+ }
+ }
+
+ free (qb);
+}
+
+void imProcessBinMorphThin(const imImage* src_image, imImage* dst_image)
+{
+ imImageCopyData(src_image, dst_image);
+ DoThinImage((imbyte*)dst_image->data[0], dst_image->width, dst_image->height);
+}
diff --git a/src/process/im_morphology_gray.cpp b/src/process/im_morphology_gray.cpp
new file mode 100644
index 0000000..c3c9d45
--- /dev/null
+++ b/src/process/im_morphology_gray.cpp
@@ -0,0 +1,231 @@
+/** \file
+ * \brief Morphology Operations for Gray Images
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_morphology_gray.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_counter.h>
+#include <im_convert.h>
+
+#include "im_process_loc.h"
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+
+template <class T, class DT>
+static int DoGrayMorphConvolve(T *map, T* new_map, int width, int height, const imImage* kernel, int counter, int ismax, DT)
+{
+ DT value, *kernel_line, max = 0, min = 0;
+ int offset, new_offset, i, j, x, y, init;
+ int kh, kw, kh2, kw2;
+
+ kh = kernel->height;
+ kw = kernel->width;
+ kh2 = kernel->height/2;
+ kw2 = kernel->width/2;
+
+ DT* kernel_data = (DT*)kernel->data[0];
+
+ for(j = 0; j < height; j++)
+ {
+ new_offset = j * width;
+
+ for(i = 0; i < width; i++)
+ {
+ init = 0;
+
+ for(y = -kh2; y <= kh2; y++)
+ {
+ kernel_line = kernel_data + (y+kh2)*kw;
+
+ if ((j + y < 0) || // pass the bottom border
+ (j + y >= height)) // pass the top border
+ continue;
+ else
+ offset = (j + y) * width;
+
+ for(x = -kw2; x <= kw2; x++)
+ {
+ if (kernel_line[x+kw2] != -1)
+ {
+ if ((i + x < 0) || // pass the left border
+ (i + x >= width)) // pass the right border
+ continue;
+ else
+ value = kernel_line[x+kw2] + map[offset + (i + x)];
+
+ if (init == 0) // first time here for each pass
+ {
+ if (ismax)
+ max = value;
+ else
+ min = value;
+
+ init = 1;
+ }
+ else
+ {
+ if (ismax && value > max)
+ max = value;
+
+ if (!ismax && value < min)
+ min = value;
+ }
+ }
+ }
+ }
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ {
+ if (ismax)
+ new_map[new_offset + i] = (T)IM_BYTECROP(max);
+ else
+ new_map[new_offset + i] = (T)IM_BYTECROP(min);
+ }
+ else
+ {
+ if (ismax)
+ new_map[new_offset + i] = (T)max;
+ else
+ new_map[new_offset + i] = (T)min;
+ }
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+int imProcessGrayMorphConvolve(const imImage* src_image, imImage* dst_image, const imImage *kernel, int ismax)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin("Gray Morphological Convolution");
+ const char* msg = (const char*)imImageGetAttribute(kernel, "Description", NULL, NULL);
+ if (!msg) msg = "Processing...";
+ imCounterTotal(counter, src_image->depth*src_image->height, msg);
+
+ imImage* fkernel = NULL;
+
+ if (src_image->data_type == IM_FLOAT && kernel->data_type != IM_FLOAT)
+ {
+ fkernel = imImageCreate(kernel->width, kernel->height, IM_GRAY, IM_FLOAT);
+ imConvertDataType(kernel, fkernel, 0, 0, 0, IM_CAST_DIRECT);
+ kernel = fkernel;
+ }
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoGrayMorphConvolve((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], src_image->width, src_image->height, kernel, counter, ismax, (int)0);
+ break;
+ case IM_USHORT:
+ ret = DoGrayMorphConvolve((imushort*)src_image->data[i], (imushort*)dst_image->data[i], src_image->width, src_image->height, kernel, counter, ismax, (int)0);
+ break;
+ case IM_INT:
+ ret = DoGrayMorphConvolve((int*)src_image->data[i], (int*)dst_image->data[i], src_image->width, src_image->height, kernel, counter, ismax, (int)0);
+ break;
+ case IM_FLOAT:
+ ret = DoGrayMorphConvolve((float*)src_image->data[i], (float*)dst_image->data[i], src_image->width, src_image->height, kernel, counter, ismax, (float)0);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ if (fkernel) imImageDestroy(fkernel);
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+int imProcessGrayMorphErode(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_INT);
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Erode");
+ // Kernel is all zeros
+ int ret = imProcessGrayMorphConvolve(src_image, dst_image, kernel, 0);
+ imImageDestroy(kernel);
+ return ret;
+}
+
+int imProcessGrayMorphDilate(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ imImage* kernel = imImageCreate(kernel_size, kernel_size, IM_GRAY, IM_INT);
+ imImageSetAttribute(kernel, "Description", IM_BYTE, -1, (void*)"Dilate");
+ // Kernel is all zeros
+ int ret = imProcessGrayMorphConvolve(src_image, dst_image, kernel, 1);
+ imImageDestroy(kernel);
+ return ret;
+}
+
+int imProcessGrayMorphOpen(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ imImage*temp = imImageClone(src_image);
+ if (!temp)
+ return 0;
+
+ if (!imProcessGrayMorphErode(src_image, temp, kernel_size)) {imImageDestroy(temp); return 0;}
+ if (!imProcessGrayMorphDilate(temp, dst_image, kernel_size)) {imImageDestroy(temp); return 0;}
+
+ imImageDestroy(temp);
+ return 1;
+}
+
+int imProcessGrayMorphClose(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ imImage*temp = imImageClone(src_image);
+ if (!temp)
+ return 0;
+
+ if (!imProcessGrayMorphDilate(src_image, temp, kernel_size)) {imImageDestroy(temp); return 0;}
+ if (!imProcessGrayMorphErode(temp, dst_image, kernel_size)) {imImageDestroy(temp); return 0;}
+
+ imImageDestroy(temp);
+ return 1;
+}
+
+int imProcessGrayMorphTopHat(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ if (!imProcessGrayMorphOpen(src_image, dst_image, kernel_size)) return 0;
+ imProcessArithmeticOp(src_image, dst_image, dst_image, IM_BIN_DIFF);
+ return 1;
+}
+
+int imProcessGrayMorphWell(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ if (!imProcessGrayMorphClose(src_image, dst_image, kernel_size)) return 0;
+ imProcessArithmeticOp(src_image, dst_image, dst_image, IM_BIN_DIFF);
+ return 1;
+}
+
+int imProcessGrayMorphGradient(const imImage* src_image, imImage* dst_image, int kernel_size)
+{
+ imImage*temp = imImageClone(src_image);
+ if (!temp)
+ return 0;
+
+ if (!imProcessGrayMorphDilate(src_image, temp, kernel_size)) {imImageDestroy(temp); return 0;}
+ if (!imProcessGrayMorphErode(src_image, dst_image, kernel_size)) {imImageDestroy(temp); return 0;}
+
+ imProcessArithmeticOp(temp, dst_image, dst_image, IM_BIN_DIFF);
+
+ imImageDestroy(temp);
+ return 1;
+}
+
diff --git a/src/process/im_quantize.cpp b/src/process/im_quantize.cpp
new file mode 100644
index 0000000..9a65f4c
--- /dev/null
+++ b/src/process/im_quantize.cpp
@@ -0,0 +1,65 @@
+/** \file
+ * \brief Additional Image Quantization Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_quantize.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_palette.h>
+#include <im_math.h>
+
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+
+void imProcessQuantizeRGBUniform(const imImage* src_image, imImage* dst_image, int dither)
+{
+ imbyte *dst_map=(imbyte*)dst_image->data[0],
+ *red_map=(imbyte*)src_image->data[0],
+ *green_map=(imbyte*)src_image->data[1],
+ *blue_map=(imbyte*)src_image->data[2];
+
+ long *palette = imPaletteUniform();
+ imImageSetPalette(dst_image, palette, 256);
+
+ for (int y = 0; y < src_image->height; y++)
+ {
+ for (int x = 0; x < src_image->width; x++)
+ {
+ if (dither)
+ *dst_map++ = (imbyte)imPaletteUniformIndexHalftoned(imColorEncode(*red_map++, *green_map++, *blue_map++), x, y);
+ else
+ *dst_map++ = (imbyte)imPaletteUniformIndex(imColorEncode(*red_map++, *green_map++, *blue_map++));
+ }
+ }
+}
+
+void imProcessQuantizeGrayUniform(const imImage* src_image, imImage* dst_image, int grays)
+{
+ int i, value;
+
+ imbyte *dst_map=(imbyte*)dst_image->data[0],
+ *src_map=(imbyte*)src_image->data[0];
+
+ imbyte re_map[256];
+ memset(re_map, 0, 256);
+
+ float factor = (float)grays/256.0f;
+ float factor256 = 256.0f/(float)grays;
+
+ for (i = 0; i < 256; i++)
+ {
+ value = imResample(i, factor);
+ value = imResample(value, factor256);
+ re_map[i] = (imbyte)IM_BYTECROP(value);
+ }
+
+ int total_count = src_image->count*src_image->depth;
+ for (i = 0; i < total_count; i++)
+ dst_map[i] = re_map[src_map[i]];
+}
diff --git a/src/process/im_render.cpp b/src/process/im_render.cpp
new file mode 100644
index 0000000..f5d296f
--- /dev/null
+++ b/src/process/im_render.cpp
@@ -0,0 +1,532 @@
+/** \file
+ * \brief Synthetic Image Render
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_render.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_counter.h>
+#include <im_math.h>
+
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+#include <time.h>
+
+static float iGetFactor(int data_type)
+{
+ if (data_type == IM_BYTE)
+ return 255.0f;
+ else if (data_type == IM_INT || data_type == IM_USHORT)
+ return 65535.0f;
+ else
+ return 1.0f;
+}
+
+template <class T>
+static int DoRenderCondOp(T *map, int width, int height, int d, imRenderCondFunc render_func, float* param, int counter)
+{
+ int offset, cond = 1;
+ T Value;
+
+ for(int y = 0; y < height; y++)
+ {
+ offset = y * width;
+
+ for(int x = 0; x < width; x++)
+ {
+ Value = (T)(render_func(x, y, d, &cond, param));
+ if (cond) map[offset + x] = Value;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+int imProcessRenderCondOp(imImage* image, imRenderCondFunc render_func, char* render_name, float* param)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin(render_name);
+ imCounterTotal(counter, image->depth*image->height, "Rendering...");
+
+ for (int d = 0; d < image->depth; d++)
+ {
+ switch(image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoRenderCondOp((imbyte*)image->data[d], image->width, image->height, d, render_func, param, counter);
+ break;
+ case IM_USHORT:
+ ret = DoRenderCondOp((imushort*)image->data[d], image->width, image->height, d, render_func, param, counter);
+ break;
+ case IM_INT:
+ ret = DoRenderCondOp((int*)image->data[d], image->width, image->height, d, render_func, param, counter);
+ break;
+ case IM_FLOAT:
+ ret = DoRenderCondOp((float*)image->data[d], image->width, image->height, d, render_func, param, counter);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+template <class T>
+static int DoRenderOp(T *map, int width, int height, int d, imRenderFunc render_func, float* param, int counter, int plus)
+{
+ int offset;
+
+ for(int y = 0; y < height; y++)
+ {
+ offset = y * width;
+
+ for(int x = 0; x < width; x++)
+ {
+ if (plus)
+ {
+ int size_of = sizeof(imbyte);
+ float value = map[offset + x] + render_func(x, y, d, param);
+ if (sizeof(T) == size_of)
+ map[offset + x] = (T)IM_BYTECROP(value);
+ else
+ map[offset + x] = (T)value;
+
+ }
+ else
+ map[offset + x] = (T)render_func(x, y, d, param);
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+int imProcessRenderOp(imImage* image, imRenderFunc render_func, char* render_name, float* param, int plus)
+{
+ int ret = 0;
+
+ int counter = imCounterBegin(render_name);
+ imCounterTotal(counter, image->depth*image->height, "Rendering...");
+
+ for (int d = 0; d < image->depth; d++)
+ {
+ switch(image->data_type)
+ {
+ case IM_BYTE:
+ ret = DoRenderOp((imbyte*)image->data[d], image->width, image->height, d, render_func, param, counter, plus);
+ break;
+ case IM_USHORT:
+ ret = DoRenderOp((imushort*)image->data[d], image->width, image->height, d, render_func, param, counter, plus);
+ break;
+ case IM_INT:
+ ret = DoRenderOp((int*)image->data[d], image->width, image->height, d, render_func, param, counter, plus);
+ break;
+ case IM_FLOAT:
+ ret = DoRenderOp((float*)image->data[d], image->width, image->height, d, render_func, param, counter, plus);
+ break;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ imCounterEnd(counter);
+
+ return ret;
+}
+
+static float do_add_specklenoise(int, int, int, int *cond, float* param)
+{
+ float rnd = float(rand()) / RAND_MAX;
+ if (rnd < param[1])
+ {
+ *cond = 1;
+ return (rand() * param[0]) / RAND_MAX;
+ }
+ else
+ {
+ *cond = 0;
+ return 0;
+ }
+}
+
+int imProcessRenderAddSpeckleNoise(const imImage* src_image, imImage* dst_image, float percent)
+{
+ float param[2];
+ param[0] = iGetFactor(src_image->data_type);
+ param[1] = percent / 100.0f;
+ srand((unsigned)time(NULL));
+ imImageCopyData(src_image, dst_image);
+ return imProcessRenderCondOp(dst_image, do_add_specklenoise, "Add Speckle Noise", param);
+}
+
+static float do_add_gaussiannoise(int, int, int, float* param)
+{
+ float rnd, x1, x2;
+
+ do
+ {
+ x1 = float(rand()) / RAND_MAX; /* [0,1] */
+ x2 = float(rand()) / RAND_MAX; /* [0,1] */
+ x1 = 2*x1 - 1; /* [-1,1] */
+ x2 = 2*x2 - 1; /* [-1,1] */
+ rnd = x1*x1 + x2*x2;
+ } while( rnd >= 1 || rnd == 0);
+
+ rnd = (float)sqrt(-2 * log(rnd) / rnd) * x1;
+ return rnd * param[1] + param[0];
+}
+
+int imProcessRenderAddGaussianNoise(const imImage* src_image, imImage* dst_image, float mean, float stddev)
+{
+ float param[2];
+ param[0] = mean;
+ param[1] = stddev;
+ srand((unsigned)time(NULL));
+ imImageCopyData(src_image, dst_image);
+ return imProcessRenderOp(dst_image, do_add_gaussiannoise, "Add Gaussian Noise", param, 1);
+}
+
+static float do_add_uniformnoise(int, int, int, float* param)
+{
+ float rnd = float(rand()) / RAND_MAX;
+ rnd = 2*rnd - 1; /* [-1,1] */
+ return 1.7320508f * rnd * param[1] + param[0];
+}
+
+int imProcessRenderAddUniformNoise(const imImage* src_image, imImage* dst_image, float mean, float stddev)
+{
+ float param[2];
+ param[0] = mean;
+ param[1] = stddev;
+ srand((unsigned)time(NULL));
+ imImageCopyData(src_image, dst_image);
+ return imProcessRenderOp(dst_image, do_add_uniformnoise, "Add Uniform Noise", param, 1);
+}
+
+static float do_const(int, int, int d, float* param)
+{
+ return param[d];
+}
+
+int imProcessRenderConstant(imImage* image, float* value)
+{
+ return imProcessRenderOp(image, do_const, "Constant", value, 0);
+}
+
+static float do_noise(int, int, int, float* param)
+{
+ return (rand() * param[0]) / RAND_MAX;
+}
+
+int imProcessRenderRandomNoise(imImage* image)
+{
+ static float param[1];
+ param[0] = iGetFactor(image->data_type);
+ srand((unsigned)time(NULL));
+ return imProcessRenderOp(image, do_noise, "Random Noise", param, 0);
+}
+
+static float do_cosine(int x, int y, int, float* param)
+{
+ return float((cos(param[1]*(x-param[3])) * cos(param[2]*(y-param[4])) + param[5]) * param[0]);
+}
+
+int imProcessRenderCosine(imImage* image, float xperiod, float yperiod)
+{
+ float param[6];
+ param[0] = iGetFactor(image->data_type);
+
+ if (xperiod == 0.0f) param[1] = 0.0;
+ else param[1] = 2.0f * 3.1416f / xperiod;
+
+ if (yperiod == 0.0f) param[2] = 0.0;
+ else param[2] = 2.0f * 3.1416f / yperiod;
+
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+
+ if (image->data_type < IM_FLOAT)
+ param[0] = param[0] / 2.0f;
+
+ if (image->data_type == IM_BYTE)
+ param[5] = 1.0f;
+ else
+ param[5] = 0.0f;
+
+ return imProcessRenderOp(image, do_cosine, "Cosine", param, 0);
+}
+
+static float do_gaussian(int x, int y, int, float* param)
+{
+ int xd = x - (int)param[2];
+ int yd = y - (int)param[3];
+ xd *= xd;
+ yd *= yd;
+ return float(exp((xd + yd)*param[1])*param[0]);
+}
+
+int imProcessRenderGaussian(imImage* image, float stddev)
+{
+ float param[4];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = -1.0f / (2.0f * stddev * stddev);
+ param[2] = image->width/2.0f;
+ param[3] = image->height/2.0f;
+ return imProcessRenderOp(image, do_gaussian, "Gaussian", param, 0);
+}
+
+static float do_lapgauss(int x, int y, int, float* param)
+{
+ int xd = x - (int)param[2];
+ int yd = y - (int)param[3];
+ xd *= xd;
+ yd *= yd;
+ xd += yd;
+ return float((xd - param[4])*exp(xd*param[1])*param[0]);
+}
+
+int imProcessRenderLapOfGaussian(imImage* image, float stddev)
+{
+ float param[5];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = -1.0f / (2.0f * stddev * stddev);
+ param[2] = image->width/2.0f;
+ param[3] = image->height/2.0f;
+ param[4] = 2.0f * stddev * stddev;
+ param[0] /= param[4];
+ return imProcessRenderOp(image, do_lapgauss, "Laplacian of Gaussian", param, 0);
+}
+
+static inline float sinc(float x)
+{
+ if (x == 0.0f)
+ return 1.0f;
+ else
+ return float(sin(x)/x);
+}
+
+static float do_sinc(int x, int y, int, float* param)
+{
+ return float((sinc((x - param[3])*param[1])*sinc((y - param[4])*param[2]) + param[5])*param[0]);
+}
+
+int imProcessRenderSinc(imImage* image, float xperiod, float yperiod)
+{
+ float param[6];
+ param[0] = iGetFactor(image->data_type);
+
+ if (xperiod == 0.0f) param[1] = 0.0;
+ else param[1] = 2.0f * 3.1416f / xperiod;
+
+ if (yperiod == 0.0f) param[2] = 0.0;
+ else param[2] = 2.0f * 3.1416f / yperiod;
+
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+
+ if (image->data_type < IM_FLOAT)
+ param[0] = param[0] / 1.3f;
+
+ if (image->data_type == IM_BYTE)
+ param[5] = 0.3f;
+ else
+ param[5] = 0.0f;
+
+ return imProcessRenderOp(image, do_sinc, "Sinc", param, 0);
+}
+
+static float do_box(int x, int y, int, float* param)
+{
+ int xr = x - (int)param[3];
+ int yr = y - (int)param[4];
+ if (xr < -(int)param[1] || xr > (int)param[1] ||
+ yr < -(int)param[2] || yr > (int)param[2])
+ return 0;
+ else
+ return param[0];
+}
+
+int imProcessRenderBox(imImage* image, int width, int height)
+{
+ float param[5];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = width/2.0f;
+ param[2] = height/2.0f;
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+ return imProcessRenderOp(image, do_box, "Box", param, 0);
+}
+
+static float do_ramp(int x, int y, int, float* param)
+{
+ if (param[3])
+ {
+ if (y < param[1])
+ return 0;
+ if (y > param[2])
+ return 0;
+
+ return (y-param[1])*param[0];
+ }
+ else
+ {
+ if (x < param[1])
+ return 0;
+ if (x > param[2])
+ return 0;
+
+ return (x-param[1])*param[0];
+ }
+}
+
+int imProcessRenderRamp(imImage* image, int start, int end, int dir)
+{
+ float param[4];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = (float)start;
+ param[2] = (float)end;
+ param[3] = (float)dir;
+ param[0] /= float(end-start);
+ return imProcessRenderOp(image, do_ramp, "Ramp", param, 0);
+}
+
+static inline int Tent(int t, int T)
+{
+ if (t < 0)
+ return (t + T);
+ else
+ return (T - t);
+}
+
+static float do_tent(int x, int y, int, float* param)
+{
+ int xr = x - (int)param[3];
+ int yr = y - (int)param[4];
+ if (xr < -(int)param[1] || xr > (int)param[1] ||
+ yr < -(int)param[2] || yr > (int)param[2])
+ return 0;
+ else
+ return Tent(xr, (int)param[1]) * Tent(yr, (int)param[2]) * param[0];
+}
+
+int imProcessRenderTent(imImage* image, int width, int height)
+{
+ float param[5];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = width/2.0f;
+ param[2] = height/2.0f;
+ param[0] /= param[1]*param[2];
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+ return imProcessRenderOp(image, do_tent, "Tent", param, 0);
+}
+
+static float do_cone(int x, int y, int, float* param)
+{
+ int xr = x - (int)param[2];
+ int yr = y - (int)param[3];
+ int radius = imRound(sqrt((double)(xr*xr + yr*yr)));
+ if (radius > (int)param[1])
+ return 0;
+ else
+ return ((int)param[1] - radius)*param[0];
+}
+
+int imProcessRenderCone(imImage* image, int radius)
+{
+ float param[4];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = (float)radius;
+ param[0] /= param[1];
+ param[2] = image->width/2.0f;
+ param[3] = image->height/2.0f;
+ return imProcessRenderOp(image, do_cone, "Cone", param, 0);
+}
+
+static float do_wheel(int x, int y, int, float* param)
+{
+ int xr = x - (int)param[3];
+ int yr = y - (int)param[4];
+ int radius = imRound(sqrt((double)(xr*xr + yr*yr)));
+ if (radius < (int)param[1] || radius > (int)param[2])
+ return 0;
+ else
+ return param[0];
+}
+
+int imProcessRenderWheel(imImage* image, int int_radius, int ext_radius)
+{
+ float param[5];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = (float)int_radius;
+ param[2] = (float)ext_radius;
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+ return imProcessRenderOp(image, do_wheel, "Wheel", param, 0);
+}
+
+static float do_grid(int x, int y, int, float* param)
+{
+ int xr = x - (int)param[3];
+ int yr = y - (int)param[4];
+ if (xr % (int)param[1] == 0 && yr % (int)param[2] == 0)
+ return param[0];
+ else
+ return 0;
+}
+
+int imProcessRenderGrid(imImage* image, int x_space, int y_space)
+{
+ float param[5];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = (float)x_space;
+ param[2] = (float)y_space;
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+ return imProcessRenderOp(image, do_grid, "Grid", param, 0);
+}
+
+static float do_chessboard(int x, int y, int, float* param)
+{
+ int xr = x - (int)param[3];
+ int yr = y - (int)param[4];
+ int xp = xr % (int)param[1];
+ int yp = yr % (int)param[2];
+ int xc = (int)param[1]/2;
+ int yc = (int)param[2]/2;
+ if (xr < 0) xc = -xc;
+ if (yr < 0) yc = -yc;
+ if ((xp < xc && yp < yc) ||
+ (xp > xc && yp > yc))
+ return param[0];
+ else
+ return 0;
+}
+
+int imProcessRenderChessboard(imImage* image, int x_space, int y_space)
+{
+ float param[5];
+ param[0] = iGetFactor(image->data_type);
+ param[1] = (float)x_space*2;
+ param[2] = (float)y_space*2;
+ param[3] = image->width/2.0f;
+ param[4] = image->height/2.0f;
+ return imProcessRenderOp(image, do_chessboard, "Chessboard", param, 0);
+}
diff --git a/src/process/im_resize.cpp b/src/process/im_resize.cpp
new file mode 100644
index 0000000..ddf6e47
--- /dev/null
+++ b/src/process/im_resize.cpp
@@ -0,0 +1,332 @@
+/** \file
+ * \brief Image Resize
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_resize.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+#include <im_complex.h>
+#include <im_counter.h>
+
+#include "im_process_loc.h"
+
+#include <stdlib.h>
+#include <memory.h>
+
+
+static inline void iResizeInverse(int x, int y, float *xl, float *yl, float x_invfactor, float y_invfactor)
+{
+ *xl = (x + 0.5f) * x_invfactor;
+ *yl = (y + 0.5f) * y_invfactor;
+}
+
+template <class DT, class DTU>
+static int iResize(int src_width, int src_height, const DT *src_map,
+ int dst_width, int dst_height, DT *dst_map,
+ DTU Dummy, int order, int counter)
+{
+ float xl, yl;
+ float x_invfactor = float(src_width)/float(dst_width);
+ float y_invfactor = float(src_height)/float(dst_height);
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ for (int x = 0; x < dst_width; x++)
+ {
+ iResizeInverse(x, y, &xl, &yl, x_invfactor, y_invfactor);
+
+ // if inside the original image
+ if (xl > 0.0 && yl > 0.0 && xl < src_width && yl < src_height)
+ {
+ if (order == 1)
+ *dst_map = imBilinearInterpolation(src_width, src_height, src_map, xl, yl);
+ else if (order == 3)
+ *dst_map = imBicubicInterpolation(src_width, src_height, src_map, xl, yl, Dummy);
+ else
+ *dst_map = imZeroOrderInterpolation(src_width, src_height, src_map, xl, yl);
+ }
+
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+
+ return 1;
+}
+
+template <class DT, class DTU>
+static int iReduce(int src_width, int src_height, const DT *src_map,
+ int dst_width, int dst_height, DT *dst_map,
+ DTU Dummy, int order, int counter)
+{
+ float xl, yl;
+ float x_invfactor = float(src_width)/float(dst_width);
+ float y_invfactor = float(src_height)/float(dst_height);
+
+ iResizeInverse(1, 1, &xl, &yl, x_invfactor, y_invfactor);
+ float xl0 = xl, yl0 = yl;
+ iResizeInverse(2, 2, &xl, &yl, x_invfactor, y_invfactor);
+ float xl1 = xl, yl1 = yl;
+
+ float box_width = xl1 - xl0;
+ float box_height = yl1 - yl0;
+
+ for (int y = 0; y < dst_height; y++)
+ {
+ for (int x = 0; x < dst_width; x++)
+ {
+ iResizeInverse(x, y, &xl, &yl, x_invfactor, y_invfactor);
+
+ // if inside the original image
+ if (xl > 0.0 && yl > 0.0 && xl < src_width && yl < src_height)
+ {
+ if (order == 0)
+ *dst_map = imZeroOrderDecimation(src_width, src_height, src_map, xl, yl, box_width, box_height, Dummy);
+ else
+ *dst_map = imBilinearDecimation(src_width, src_height, src_map, xl, yl, box_width, box_height, Dummy);
+ }
+
+ dst_map++;
+ }
+
+ if (!imCounterInc(counter))
+ return 0;
+ }
+ return 1;
+}
+
+int imProcessReduce(const imImage* src_image, imImage* dst_image, int order)
+{
+ int ret = 0;
+ int counter = imCounterBegin("Reduce Size");
+ const char* int_msg = (order == 1)? "Bilinear Decimation": "Zero Order Decimation";
+ imCounterTotal(counter, src_image->depth*dst_image->height, int_msg);
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = iReduce(src_image->width, src_image->height, (const imbyte*)src_image->data[i],
+ dst_image->width, dst_image->height, (imbyte*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_USHORT:
+ ret = iReduce(src_image->width, src_image->height, (const imushort*)src_image->data[i],
+ dst_image->width, dst_image->height, (imushort*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_INT:
+ ret = iReduce(src_image->width, src_image->height, (const int*)src_image->data[i],
+ dst_image->width, dst_image->height, (int*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_FLOAT:
+ ret = iReduce(src_image->width, src_image->height, (const float*)src_image->data[i],
+ dst_image->width, dst_image->height, (float*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_CFLOAT:
+ ret = iReduce(src_image->width, src_image->height, (const imcfloat*)src_image->data[i],
+ dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i],
+ imcfloat(0,0), order, counter);
+ break;
+ }
+ }
+
+ imCounterEnd(counter);
+ return ret;
+}
+
+int imProcessResize(const imImage* src_image, imImage* dst_image, int order)
+{
+ int ret = 0;
+ int counter = imCounterBegin("Resize");
+ const char* int_msg = (order == 3)? "Bicubic Interpolation": (order == 1)? "Bilinear Interpolation": "Zero Order Interpolation";
+ imCounterTotal(counter, src_image->depth*dst_image->height, int_msg);
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ret = iResize(src_image->width, src_image->height, (const imbyte*)src_image->data[i],
+ dst_image->width, dst_image->height, (imbyte*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_USHORT:
+ ret = iResize(src_image->width, src_image->height, (const imushort*)src_image->data[i],
+ dst_image->width, dst_image->height, (imushort*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_INT:
+ ret = iResize(src_image->width, src_image->height, (const int*)src_image->data[i],
+ dst_image->width, dst_image->height, (int*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_FLOAT:
+ ret = iResize(src_image->width, src_image->height, (const float*)src_image->data[i],
+ dst_image->width, dst_image->height, (float*)dst_image->data[i],
+ float(0), order, counter);
+ break;
+ case IM_CFLOAT:
+ ret = iResize(src_image->width, src_image->height, (const imcfloat*)src_image->data[i],
+ dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i],
+ imcfloat(0,0), order, counter);
+ break;
+ }
+ }
+
+ imCounterEnd(counter);
+ return ret;
+}
+
+template <class DT>
+static void ReduceBy4(int src_width,
+ int src_height,
+ DT *src_map,
+ int dst_width,
+ int dst_height,
+ DT *dst_map)
+{
+ int x,y,yd,xd;
+ (void)dst_height;
+
+ // make an even size
+ int height = (src_height/2)*2;
+ int width = (src_width/2)*2;
+
+ for(y = 0 ; y < height ; y += 2)
+ {
+ yd = y/2;
+ for(x = 0 ; x < width ; x += 2)
+ {
+ xd = x/2;
+ dst_map[yd * dst_width + xd] = ((src_map[y * src_width + x] +
+ src_map[y * src_width + (x+1)] +
+ src_map[(y+1) * src_width + x] +
+ src_map[(y+1) * src_width + (x+1)])/4);
+ }
+ }
+}
+
+void imProcessReduceBy4(const imImage* src_image, imImage* dst_image)
+{
+ int i;
+
+ for (i = 0; i < src_image->depth; i++)
+ {
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ ReduceBy4(src_image->width, src_image->height, (imbyte*)src_image->data[i], dst_image->width, dst_image->height, (imbyte*)dst_image->data[i]);
+ break;
+ case IM_USHORT:
+ ReduceBy4(src_image->width, src_image->height, (imushort*)src_image->data[i], dst_image->width, dst_image->height, (imushort*)dst_image->data[i]);
+ break;
+ case IM_INT:
+ ReduceBy4(src_image->width, src_image->height, (int*)src_image->data[i], dst_image->width, dst_image->height, (int*)dst_image->data[i]);
+ break;
+ case IM_FLOAT:
+ ReduceBy4(src_image->width, src_image->height, (float*)src_image->data[i], dst_image->width, dst_image->height, (float*)dst_image->data[i]);
+ break;
+ case IM_CFLOAT:
+ ReduceBy4(src_image->width, src_image->height, (imcfloat*)src_image->data[i], dst_image->width, dst_image->height, (imcfloat*)dst_image->data[i]);
+ break;
+ }
+ }
+}
+
+void imProcessCrop(const imImage* src_image, imImage* dst_image, int xmin, int ymin)
+{
+ int type_size = imDataTypeSize(src_image->data_type);
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ imbyte *src_map = (imbyte*)src_image->data[i];
+ imbyte *dst_map = (imbyte*)dst_image->data[i];
+
+ for (int y = 0; y < dst_image->height; y++)
+ {
+ int src_offset = (y + ymin)*src_image->line_size + xmin*type_size;
+ int dst_offset = y*dst_image->line_size;
+
+ memcpy(&dst_map[dst_offset], &src_map[src_offset], dst_image->line_size);
+ }
+ }
+}
+
+void imProcessInsert(const imImage* src_image, const imImage* rgn_image, imImage* dst_image, int xmin, int ymin)
+{
+ int type_size = imDataTypeSize(src_image->data_type);
+ int dst_size1 = xmin*type_size;
+ int dst_size2 = src_image->line_size - (rgn_image->line_size + dst_size1);
+ int dst_offset2 = dst_size1+rgn_image->line_size;
+ int ymax = ymin+rgn_image->height-1;
+ int rgn_size = rgn_image->line_size;
+
+ if (dst_size2 < 0)
+ {
+ dst_size2 = 0;
+ rgn_size = src_image->line_size - dst_size1;
+ dst_offset2 = dst_size1+rgn_size;
+ }
+
+ if (ymax > src_image->height-1)
+ ymax = src_image->height-1;
+
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ imbyte *src_map = (imbyte*)src_image->data[i];
+ imbyte *rgn_map = (imbyte*)rgn_image->data[i];
+ imbyte *dst_map = (imbyte*)dst_image->data[i];
+
+ for (int y = 0; y < src_image->height; y++)
+ {
+ if (y < ymin || y > ymax)
+ {
+ memcpy(dst_map, src_map, src_image->line_size);
+ }
+ else
+ {
+ if (dst_size1)
+ memcpy(dst_map, src_map, dst_size1);
+
+ memcpy(dst_map + dst_size1, rgn_map, rgn_size);
+
+ if (dst_size2)
+ memcpy(dst_map + dst_offset2,
+ src_map + dst_offset2, dst_size2);
+
+ rgn_map += rgn_image->line_size;
+ }
+
+ src_map += src_image->line_size;
+ dst_map += dst_image->line_size;
+ }
+ }
+}
+
+void imProcessAddMargins(const imImage* src_image, imImage* dst_image, int xmin, int ymin)
+{
+ int type_size = imDataTypeSize(src_image->data_type);
+ for (int i = 0; i < src_image->depth; i++)
+ {
+ imbyte *dst_map = (imbyte*)dst_image->data[i];
+ imbyte *src_map = (imbyte*)src_image->data[i];
+
+ for (int y = 0; y < src_image->height; y++)
+ {
+ int src_offset = y*src_image->line_size;
+ int dst_offset = (y + ymin)*dst_image->line_size + xmin*type_size;
+
+ memcpy(&dst_map[dst_offset], &src_map[src_offset], src_image->line_size);
+ }
+ }
+}
+
diff --git a/src/process/im_statistics.cpp b/src/process/im_statistics.cpp
new file mode 100644
index 0000000..b9f086d
--- /dev/null
+++ b/src/process/im_statistics.cpp
@@ -0,0 +1,341 @@
+/** \file
+ * \brief Image Statistics Calculations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_statistics.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math_op.h>
+
+#include "im_process_ana.h"
+
+#include <stdlib.h>
+#include <memory.h>
+#include <math.h>
+
+static unsigned long count_map(const imImage* image)
+{
+ unsigned long histo[256];
+ int size = image->width * image->height;
+ imCalcHistogram((imbyte*)image->data[0], size, histo, 0);
+ unsigned long numcolor = 0;
+
+ for (int i = 0; i < 256; i++)
+ {
+ if(histo[i] != 0)
+ numcolor++;
+ }
+
+ return numcolor;
+}
+
+// will count also all the 3 components color spaces
+static unsigned long count_rgb(const imImage* image)
+{
+ imbyte *count = (imbyte*)calloc(sizeof(imbyte), 1 << 21 ); /* (2^24)/8=2^21 ~ 2Mb */
+ if (!count)
+ return (unsigned long)-1;
+
+ int size = image->width * image->height;
+ imbyte *red = (imbyte*)image->data[0];
+ imbyte *green = (imbyte*)image->data[1];
+ imbyte *blue = (imbyte*)image->data[2];
+
+ int index;
+ unsigned long numcolor = 0;
+
+ for(int i = 0; i < size; i++)
+ {
+ index = red[i] << 16 | green[i] << 8 | blue[i];
+
+ if(imDataBitGet(count, index) == 0)
+ numcolor++;
+
+ imDataBitSet(count, index, 1);
+ }
+
+ free(count);
+
+ return numcolor;
+}
+
+unsigned long imCalcCountColors(const imImage* image)
+{
+ if (imColorModeDepth(image->color_space) > 1)
+ return count_rgb(image);
+ else
+ return count_map(image);
+}
+
+void imCalcHistogram(const imbyte* map, int size, unsigned long* histo, int cumulative)
+{
+ int i;
+
+ memset(histo, 0, 256 * sizeof(unsigned long));
+
+ for (i = 0; i < size; i++)
+ histo[*map++]++;
+
+ if (cumulative)
+ {
+ /* make cumulative histogram */
+ for (i = 1; i < 256; i++)
+ histo[i] += histo[i-1];
+ }
+}
+
+void imCalcUShortHistogram(const imushort* map, int size, unsigned long* histo, int cumulative)
+{
+ int i;
+
+ memset(histo, 0, 65535 * sizeof(unsigned long));
+
+ for (i = 0; i < size; i++)
+ histo[*map++]++;
+
+ if (cumulative)
+ {
+ /* make cumulative histogram */
+ for (i = 1; i < 65535; i++)
+ histo[i] += histo[i-1];
+ }
+}
+
+void imCalcGrayHistogram(const imImage* image, unsigned long* histo, int cumulative)
+{
+ int i;
+
+ memset(histo, 0, 256 * sizeof(unsigned long));
+
+ if (image->color_space == IM_GRAY)
+ {
+ imbyte* map = (imbyte*)image->data[0];
+ for (i = 0; i < image->count; i++)
+ histo[*map++]++;
+ }
+ else if (image->color_space == IM_MAP || image->color_space == IM_BINARY)
+ {
+ imbyte* map = (imbyte*)image->data[0];
+ imbyte gray_map[256], r, g, b;
+
+ for (i = 0; i < image->palette_count; i++)
+ {
+ imColorDecode(&r, &g, &b, image->palette[i]);
+ gray_map[i] = (imbyte)((299*r + 587*g + 114*b) / 1000);
+ }
+
+ for (i = 0; i < image->count; i++)
+ {
+ int index = *map++;
+ histo[gray_map[index]]++;
+ }
+ }
+ else
+ {
+ imbyte gray;
+ imbyte* r = (imbyte*)image->data[0];
+ imbyte* g = (imbyte*)image->data[1];
+ imbyte* b = (imbyte*)image->data[2];
+ for (i = 0; i < image->count; i++)
+ {
+ gray = (imbyte)((299*(*r++) + 587*(*g++) + 114*(*b++)) / 1000);
+ histo[gray]++;
+ }
+ }
+
+ if (cumulative)
+ {
+ /* make cumulative histogram */
+ for (i = 1; i < 256; i++)
+ histo[i] += histo[i-1];
+ }
+}
+
+template <class T>
+static void DoStats(T* data, int count, imStats* stats)
+{
+ memset(stats, 0, sizeof(imStats));
+
+ stats->min = (float)data[0];
+ stats->max = (float)data[0];
+
+ for (int i = 0; i < count; i++)
+ {
+ if (data[i] < stats->min)
+ stats->min = (float)data[i];
+
+ if (data[i] > stats->max)
+ stats->max = (float)data[i];
+
+ if (data[i] > 0)
+ stats->positive++;
+
+ if (data[i] < 0)
+ stats->negative++;
+
+ if (data[i] == 0)
+ stats->zeros++;
+
+ stats->mean += (float)data[i];
+ stats->stddev += ((float)data[i])*((float)data[i]);
+ }
+
+ stats->mean /= float(count);
+ stats->stddev = (float)sqrt((stats->stddev - count * stats->mean*stats->mean)/(count-1.0));
+}
+
+void imCalcImageStatistics(const imImage* image, imStats* stats)
+{
+ int count = image->width * image->height;
+
+ for (int i = 0; i < image->depth; i++)
+ {
+ switch(image->data_type)
+ {
+ case IM_BYTE:
+ DoStats((imbyte*)image->data[i], count, &stats[i]);
+ break;
+ case IM_USHORT:
+ DoStats((imushort*)image->data[i], count, &stats[i]);
+ break;
+ case IM_INT:
+ DoStats((int*)image->data[i], count, &stats[i]);
+ break;
+ case IM_FLOAT:
+ DoStats((float*)image->data[i], count, &stats[i]);
+ break;
+ }
+ }
+}
+
+void imCalcHistogramStatistics(const imImage* image, imStats* stats)
+{
+ int image_size = image->width * image->height;
+ unsigned long histo[256];
+
+ for (int d = 0; d < image->depth; d++)
+ {
+ imCalcHistogram((imbyte*)image->data[d], image_size, histo, 0);
+ DoStats((unsigned long*)histo, 256, &stats[d]);
+ }
+}
+
+void imCalcHistoImageStatistics(const imImage* image, int* median, int* mode)
+{
+ unsigned long histo[256];
+
+ for (int d = 0; d < image->depth; d++)
+ {
+ int i;
+ imCalcHistogram((imbyte*)image->data[d], image->count, histo, 0);
+
+ unsigned long half = image->count/2;
+ unsigned long count = histo[0];
+ for (i = 1; i < 256; i++)
+ {
+ if (count > half)
+ {
+ median[d] = i-1;
+ break;
+ }
+
+ count += histo[i];
+ }
+
+ unsigned long max = histo[0];
+ for (i = 1; i < 256; i++)
+ {
+ if (max < histo[i])
+ max = histo[i];
+ }
+
+ int found_mode = 0;
+ for (i = 0; i < 256; i++)
+ {
+ if (histo[i] == max)
+ {
+ if (found_mode)
+ {
+ mode[d] = -1;
+ break;
+ }
+
+ mode[d] = i;
+ found_mode = 1;
+ }
+ }
+ }
+}
+
+float imCalcSNR(const imImage* image, const imImage* noise_image)
+{
+ imStats stats[3];
+ imCalcImageStatistics((imImage*)image, stats);
+
+ imStats noise_stats[3];
+ imCalcImageStatistics((imImage*)noise_image, noise_stats);
+
+ if (image->color_space == IM_RGB)
+ {
+ noise_stats[0].stddev += noise_stats[1].stddev;
+ noise_stats[0].stddev += noise_stats[2].stddev;
+ noise_stats[0].stddev /= 3;
+ stats[0].stddev += stats[1].stddev;
+ stats[0].stddev += stats[2].stddev;
+ stats[0].stddev /= 3;
+ }
+
+ if (noise_stats[0].stddev == 0)
+ return 0;
+
+ return float(20.*log10(stats[0].stddev / noise_stats[0].stddev));
+}
+
+template <class T>
+static float DoRMSOp(T *map1, T *map2, int count)
+{
+ float rmserror = 0.0f;
+ float diff;
+
+ for (int i = 0; i < count; i++)
+ {
+ diff = float(map1[i] - map2[i]);
+ rmserror += diff * diff;
+ }
+
+ return rmserror;
+}
+
+float imCalcRMSError(const imImage* image1, const imImage* image2)
+{
+ float rmserror = 0.0f;
+
+ int count = image1->count*image1->depth;
+
+ switch(image1->data_type)
+ {
+ case IM_BYTE:
+ rmserror = DoRMSOp((imbyte*)image1->data[0], (imbyte*)image2->data[0], count);
+ break;
+ case IM_USHORT:
+ rmserror = DoRMSOp((imushort*)image1->data[0], (imushort*)image2->data[0], count);
+ break;
+ case IM_INT:
+ rmserror = DoRMSOp((int*)image1->data[0], (int*)image2->data[0], count);
+ break;
+ case IM_FLOAT:
+ rmserror = DoRMSOp((float*)image1->data[0], (float*)image2->data[0], count);
+ break;
+ case IM_CFLOAT:
+ rmserror = DoRMSOp((float*)image1->data[0], (float*)image2->data[0], 2*count);
+ break;
+ }
+
+ rmserror = float(sqrt(rmserror / float((count * image1->depth))));
+
+ return rmserror;
+}
+
diff --git a/src/process/im_threshold.cpp b/src/process/im_threshold.cpp
new file mode 100644
index 0000000..4af72ee
--- /dev/null
+++ b/src/process/im_threshold.cpp
@@ -0,0 +1,391 @@
+/** \file
+ * \brief Threshold Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_threshold.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+
+#include "im_process_pon.h"
+#include "im_process_ana.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+
+void imProcessSliceThreshold(const imImage* src_image, imImage* dst_image, int start_level, int end_level)
+{
+ float params[3];
+ params[0] = (float)start_level;
+ params[1] = (float)end_level;
+ params[2] = (float)1; /* binarize 0-255 */
+ imProcessToneGamut(src_image, dst_image, IM_GAMUT_SLICE, params);
+ imImageMakeBinary(dst_image); /* this compensates the returned values in IM_GAMUT_SLICE */
+}
+
+void imProcessThresholdByDiff(const imImage* image1, const imImage* image2, imImage* NewImage)
+{
+ imbyte *src_map1 = (imbyte*)image1->data[0];
+ imbyte *src_map2 = (imbyte*)image2->data[0];
+ imbyte *dst_map = (imbyte*)NewImage->data[0];
+ int size = image1->count;
+
+ for (int i = 0; i < size; i++)
+ {
+ if (*src_map1++ <= *src_map2++)
+ *dst_map++ = 0;
+ else
+ *dst_map++ = 1;
+ }
+}
+
+template <class T>
+static void doThreshold(T *src_map, imbyte *dst_map, int count, int level, int value)
+{
+ for (int i = 0; i < count; i++)
+ {
+ if (*src_map++ <= level)
+ *dst_map++ = 0;
+ else
+ *dst_map++ = (imbyte)value;
+ }
+}
+
+void imProcessThreshold(const imImage* src_image, imImage* dst_image, int level, int value)
+{
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ doThreshold((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->count, level, value);
+ break;
+ case IM_USHORT:
+ doThreshold((imushort*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->count, level, value);
+ break;
+ case IM_INT:
+ doThreshold((int*)src_image->data[0], (imbyte*)dst_image->data[0],
+ src_image->count, level, value);
+ break;
+ }
+}
+
+static int compare_int(const void *elem1, const void *elem2)
+{
+ int* v1 = (int*)elem1;
+ int* v2 = (int*)elem2;
+
+ if (*v1 < *v2)
+ return -1;
+
+ if (*v1 > *v2)
+ return 1;
+
+ return 0;
+}
+
+static int thresUniErr(unsigned char* band, int width, int height)
+{
+ int x, y, i, bottom, top, ant2x2, maks1, maks2, maks4, t;
+ int xsize, ysize, offset1, offset2;
+ double a, b, c, phi;
+ int g[4], tab1[256], tab2[256], tab4[256];
+
+ memset(tab1, 0, sizeof(int)*256);
+ memset(tab2, 0, sizeof(int)*256);
+ memset(tab4, 0, sizeof(int)*256);
+
+ xsize = width;
+ ysize = height;
+
+ if (xsize%2 != 0)
+ xsize--;
+
+ if (ysize%2 != 0)
+ ysize--;
+
+ /* examine all 2x2 neighborhoods */
+
+ for (y=0; y<ysize; y+=2)
+ {
+ offset1 = y*width;
+ offset2 = (y+1)*width;
+
+ for (x=0; x<xsize; x+=2)
+ {
+ g[0] = band[offset1 + x];
+ g[1] = band[offset1 + x+1];
+ g[2] = band[offset2 + x];
+ g[3] = band[offset2 + x+1];
+
+ /* Sorting */
+ qsort(g, 4, sizeof(int), compare_int);
+
+ /* Accumulating */
+ tab1[g[0]] += 1;
+ tab1[g[1]] += 1;
+ tab1[g[2]] += 1;
+ tab1[g[3]] += 1;
+
+ tab2[g[0]] +=3;
+ tab2[g[1]] +=2;
+ tab2[g[2]] +=1;
+
+ tab4[g[0]] +=1;
+ }
+ }
+
+ /* Summing */
+ for (i=254; i>=0; i--)
+ {
+ tab1[i] += tab1[i+1];
+ tab2[i] += tab2[i+1];
+ tab4[i] += tab4[i+1];
+ }
+
+ /* Tables are ready, find threshold */
+ bottom = 0; top = 255;
+ ant2x2 = (xsize/2)*(ysize/2);
+ maks1 = tab1[0]; /* = ant2x2 * 4; */
+ maks2 = tab2[0]; /* = ant2x2 * 6; */
+ maks4 = tab4[0]; /* = ant2x2; */
+
+ /* binary search */
+ t = 0;
+ while (bottom != top-1)
+ {
+ t = (int) ((bottom+top)/2);
+
+ /* Calculate probabilities */
+ a = (double) tab1[t+1]/maks1;
+ b = (double) tab2[t+1]/maks2;
+ c = (double) tab4[t+1]/maks4;
+
+ phi = sqrt((b*b - c) / (a*a - b));
+
+ if (phi> 1)
+ bottom = t;
+ else
+ top = t;
+ }
+
+ return t;
+}
+
+int imProcessUniformErrThreshold(const imImage* image, imImage* NewImage)
+{
+ int level = thresUniErr((imbyte*)image->data[0], image->width, image->height);
+ imProcessThreshold(image, NewImage, level, 1);
+ return level;
+}
+
+static void do_dither_error(imbyte* data1, imbyte* data2, int size, int t, int value)
+{
+ int i, error;
+ float scale = (float)(t/(255.0-t));
+
+ error = 0; /* always in [-127,127] */
+
+ for (i = 0; i < size; i++)
+ {
+ if ((int)(*data1 + error) > t)
+ {
+ error -= (int)(((int)255 - (int)*data1++)*scale);
+ *data2++ = (imbyte)value;
+ }
+ else
+ {
+ error += (int)*data1++;
+ *data2++ = (imbyte)0;
+ }
+ }
+}
+
+void imProcessDifusionErrThreshold(const imImage* image, imImage* NewImage, int level)
+{
+ int value = image->depth > 1? 255: 1;
+ int size = image->width * image->height;
+ for (int i = 0; i < image->depth; i++)
+ {
+ do_dither_error((imbyte*)image->data[i], (imbyte*)NewImage->data[i], size, level, value);
+ }
+}
+
+int imProcessPercentThreshold(const imImage* image, imImage* NewImage, float percent)
+{
+ unsigned long histo[256], cut;
+
+ cut = (int)((image->width * image->height * percent)/100.);
+
+ imCalcHistogram((imbyte*)image->data[0], image->width * image->height, histo, 1);
+
+ int i;
+ for (i = 0; i < 256; i++)
+ {
+ if (histo[i] > cut)
+ break;
+ }
+
+ int level = (i==0? 0: i==256? 254: i-1);
+
+ imProcessThreshold(image, NewImage, level, 1);
+ return level;
+}
+
+static int MaximizeDiscriminantFunction(double * p)
+{
+ double mi_255 = 0;
+ int k;
+ for (k=0; k<256; k++)
+ mi_255 += k*p[k];
+
+ int index = 0;
+ double max = 0;
+ double mi_k = 0;
+ double w_k = 0;
+ double value;
+ for (k=0; k<256; k++)
+ {
+ mi_k += k*p[k];
+ w_k += p[k];
+ value = ((w_k == 0) || (w_k == 1))? -1 : ((mi_255*w_k - mi_k)*(mi_255*w_k - mi_k))/(w_k*(1-w_k));
+ if (value >= max)
+ {
+ index = k;
+ max = value;
+ }
+ }
+
+ return index;
+}
+
+static unsigned char Otsu(const imImage *image)
+{
+ unsigned long histo[256];
+ imCalcHistogram((imbyte*)image->data[0], image->count, histo, 0);
+
+ double totalPixels = image->count;
+ double p[256];
+ for (int i=0; i<256; i++)
+ p[i] = histo[i]/totalPixels;
+
+ return (unsigned char)MaximizeDiscriminantFunction(p);
+}
+
+int imProcessOtsuThreshold(const imImage* image, imImage* NewImage)
+{
+ int level = Otsu(image);
+ imProcessThreshold(image, NewImage, level, 1);
+ return level;
+}
+
+int imProcessMinMaxThreshold(const imImage* image, imImage* NewImage)
+{
+ imStats stats;
+ imCalcImageStatistics(image, &stats);
+ int level = (int)((stats.max - stats.min)/2.0f);
+ imProcessThreshold(image, NewImage, level, 1);
+ return level;
+}
+
+void imProcessHysteresisThresEstimate(const imImage* image, int *low_thres, int *high_thres)
+{
+ unsigned long hist[256];
+ imCalcHistogram((imbyte*)image->data[0], image->count, hist, 0);
+
+ /* The high threshold should be > 80 or 90% of the pixels */
+ unsigned long cut = (int)(0.1*image->count);
+
+ int k = 255;
+ unsigned long count = hist[255];
+ while (count < cut)
+ {
+ k--;
+ count += hist[k];
+ }
+ *high_thres = k;
+
+ k=0;
+ while (hist[k]==0) k++;
+
+ *low_thres = (int)((*high_thres + k)/2.0) + k;
+}
+
+void imProcessHysteresisThreshold(const imImage* image, imImage* NewImage, int low_thres, int high_thres)
+{
+ imbyte *src_map = (imbyte*)image->data[0];
+ imbyte *dst_map = (imbyte*)NewImage->data[0];
+ int i, j, size = image->count;
+
+ for (i = 0; i < size; i++)
+ {
+ if (*src_map > high_thres)
+ *dst_map++ = 1;
+ else if (*src_map > low_thres)
+ *dst_map++ = 2; // mark for future replace
+ else
+ *dst_map++ = 0;
+
+ src_map++;
+ }
+
+ // now loop multiple times until there is no "2"s or no one was changed
+ dst_map = (imbyte*)NewImage->data[0];
+ int changed = 1;
+ while (changed)
+ {
+ changed = 0;
+ for (j=1; j<image->height-1; j++)
+ {
+ for (i=1; i<image->width-1; i++)
+ {
+ int offset = i+j*image->width;
+ if (dst_map[offset] == 2)
+ {
+ // if there is an edge neighbor mark this as edge too
+ if (dst_map[offset+1] == 1 || dst_map[offset-1] == 1 ||
+ dst_map[offset+image->width] == 1 || dst_map[offset-image->width] == 1 ||
+ dst_map[offset+image->width-1] == 1 || dst_map[offset+image->width+1] == 1 ||
+ dst_map[offset-image->width-1] == 1 || dst_map[offset-image->width+1] == 1)
+ {
+ dst_map[offset] = 1;
+ changed = 1;
+ }
+ }
+ }
+ }
+ }
+
+ // Clear the remaining "2"s
+ dst_map = (imbyte*)NewImage->data[0];
+ for (i = 0; i < size; i++)
+ {
+ if (*dst_map == 2)
+ *dst_map = 0;
+ dst_map++;
+ }
+}
+
+void imProcessLocalMaxThresEstimate(const imImage* image, int *thres)
+{
+ unsigned long hist[256];
+ imCalcHistogram((imbyte*)image->data[0], image->count, hist, 0);
+
+ int high_count = 0;
+ int index = 255;
+ while (high_count < 10 && index > 0)
+ {
+ if (hist[index] != 0)
+ high_count++;
+
+ index--;
+ }
+ *thres = index+1;
+}
+
diff --git a/src/process/im_tonegamut.cpp b/src/process/im_tonegamut.cpp
new file mode 100644
index 0000000..cf63350
--- /dev/null
+++ b/src/process/im_tonegamut.cpp
@@ -0,0 +1,322 @@
+/** \file
+ * \brief Tone Gamut Operations
+ *
+ * See Copyright Notice in im_lib.h
+ * $Id: im_tonegamut.cpp,v 1.1 2008/10/17 06:16:33 scuri Exp $
+ */
+
+
+#include <im.h>
+#include <im_util.h>
+#include <im_math.h>
+
+#include "im_process_pon.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <memory.h>
+#include <string.h>
+#include <math.h>
+
+
+template <class T>
+static inline T line_op(const T& v, const T& min, const T& max, const float& a, const float& b)
+{
+ float r = v * a + b;
+ if (r > (float)max) return max;
+ if (r < (float)min) return min;
+ return (T)r;
+}
+
+template <class T>
+static inline T normal_op(const T& v, const T& min, const T& range)
+{
+ return (T)(float(v - min) / float(range));
+}
+
+template <class T>
+static inline T zerostart_op(const T& v, const T& min)
+{
+ return (T)(v - min);
+}
+
+template <class T>
+static inline float invert_op(const T& v, const T& min, const T& range)
+{
+ return 1.0f - float(v - min) / float(range);
+}
+
+template <class T>
+static inline T solarize_op(const T& v, const T& level, const float& A, const float& B)
+{
+ if (v > level)
+ return (T)(v * A + B);
+ else
+ return v;
+}
+
+template <class T>
+static inline T slice_op(const T& v, const T& min, const T& max, const T& start, const T& end, int bin)
+{
+ if (v < start || v > end)
+ return min;
+ else
+ {
+ if (bin)
+ return max;
+ else
+ return v;
+ }
+}
+
+template <class T>
+static inline T tonecrop_op(const T& v, const T& start, const T& end)
+{
+ if (v < start)
+ return start;
+ if (v > end)
+ return end;
+ else
+ return v;
+}
+
+template <class T>
+static inline T expand_op(const T& v, const T& min, const T& max, const T& start, const float& norm)
+{
+ float r = (v - start)*norm + min;
+ if (r > (float)max) return max;
+ if (r < (float)min) return min;
+ return (T)r;
+}
+
+template <class T>
+static inline float norm_pow_op(const T& v, const T& min, const T& range, const float& gamma)
+{
+ return (float)pow(float(v - min) / float(range), gamma);
+}
+
+template <class T>
+static inline float norm_log_op(const T& v, const T& min, const T& range, const float& norm, const float& K)
+{
+ return (float)(log(K * float(v - min) / float(range) + 1) / norm);
+}
+
+template <class T>
+static inline float norm_exp_op(const T& v, const T& min, const T& range, const float& norm, const float& K)
+{
+ return (float)((exp(K * float(v - min) / float(range)) - 1) / norm);
+}
+
+template <class T>
+static void DoNormalizedUnaryOp(T *map, T *new_map, int count, int op, float *args)
+{
+ int i;
+ T min, max, range;
+
+ int size_of = sizeof(imbyte);
+ if (sizeof(T) == size_of)
+ {
+ min = 0;
+ max = 255;
+ }
+ else
+ {
+ imMinMax(map, count, min, max);
+
+ if (min == max)
+ {
+ max = min + 1;
+
+ if (min != 0)
+ min = min - 1;
+ }
+ }
+
+ range = max-min;
+
+ switch(op)
+ {
+ case IM_GAMUT_NORMALIZE:
+ {
+ if (min >= 0 && max <= 1)
+ {
+ for (i = 0; i < count; i++)
+ new_map[i] = (T)map[i];
+ }
+ else
+ {
+ for (i = 0; i < count; i++)
+ new_map[i] = normal_op(map[i], min, range);
+ }
+ break;
+ }
+ case IM_GAMUT_INVERT:
+ for (i = 0; i < count; i++)
+ new_map[i] = (T)(invert_op(map[i], min, range)*range + min);
+ break;
+ case IM_GAMUT_ZEROSTART:
+ for (i = 0; i < count; i++)
+ new_map[i] = (T)zerostart_op(map[i], min);
+ break;
+ case IM_GAMUT_SOLARIZE:
+ {
+ T level = (T)(((100 - args[0]) * range) / 100.0f + min);
+ float A = float(level - min) / float(level - max);
+ float B = float(level * range) / float(max - level);
+ for (i = 0; i < count; i++)
+ new_map[i] = solarize_op(map[i], level, A, B);
+ break;
+ }
+ case IM_GAMUT_POW:
+ for (i = 0; i < count; i++)
+ new_map[i] = (T)(norm_pow_op(map[i], min, range, args[0])*range + min);
+ break;
+ case IM_GAMUT_LOG:
+ {
+ float norm = float(log(args[0] + 1));
+ for (i = 0; i < count; i++)
+ new_map[i] = (T)(norm_log_op(map[i], min, range, norm, args[0])*range + min);
+ break;
+ }
+ case IM_GAMUT_EXP:
+ {
+ float norm = float(exp(args[0]) - 1);
+ for (i = 0; i < count; i++)
+ new_map[i] = (T)(norm_exp_op(map[i], min, range, norm, args[0])*range + min);
+ break;
+ }
+ case IM_GAMUT_SLICE:
+ {
+ if (args[0] > args[1]) { float tmp = args[1]; args[1] = args[0]; args[0] = tmp; }
+ if (args[1] > max) args[1] = (float)max;
+ if (args[0] < min) args[0] = (float)min;
+ for (i = 0; i < count; i++)
+ new_map[i] = slice_op(map[i], min, max, (T)args[0], (T)args[1], (int)args[2]);
+ break;
+ }
+ case IM_GAMUT_CROP:
+ {
+ if (args[0] > args[1]) { float tmp = args[1]; args[1] = args[0]; args[0] = tmp; }
+ if (args[1] > max) args[1] = (float)max;
+ if (args[0] < min) args[0] = (float)min;
+ for (i = 0; i < count; i++)
+ new_map[i] = tonecrop_op(map[i], (T)args[0], (T)args[1]);
+ break;
+ }
+ case IM_GAMUT_EXPAND:
+ {
+ if (args[0] > args[1]) { float tmp = args[1]; args[1] = args[0]; args[0] = tmp; }
+ if (args[1] > max) args[1] = (float)max;
+ if (args[0] < min) args[0] = (float)min;
+ float norm = float(max - min)/(args[1] - args[0]);
+ for (i = 0; i < count; i++)
+ new_map[i] = expand_op(map[i], min, max, (T)args[0], norm);
+ break;
+ }
+ case IM_GAMUT_BRIGHTCONT:
+ {
+ float bs = (args[0] * range) / 100.0f;
+ float a = (float)tan((45+args[1]*0.449999)/57.2957795);
+ float b = bs + (float)range*(1.0f - a)/2.0f;
+ for (i = 0; i < count; i++)
+ new_map[i] = line_op(map[i], min, max, a, b);
+ break;
+ }
+ }
+}
+
+void imProcessToneGamut(const imImage* src_image, imImage* dst_image, int op, float *args)
+{
+ int count = src_image->count*src_image->depth;
+
+ switch(src_image->data_type)
+ {
+ case IM_BYTE:
+ DoNormalizedUnaryOp((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], count, op, args);
+ break;
+ case IM_USHORT:
+ DoNormalizedUnaryOp((imushort*)src_image->data[0], (imushort*)dst_image->data[0], count, op, args);
+ break;
+ case IM_INT:
+ DoNormalizedUnaryOp((int*)src_image->data[0], (int*)dst_image->data[0], count, op, args);
+ break;
+ case IM_FLOAT:
+ DoNormalizedUnaryOp((float*)src_image->data[0], (float*)dst_image->data[0], count, op, args);
+ break;
+ }
+}
+
+void imProcessUnNormalize(const imImage* image, imImage* NewImage)
+{
+ int count = image->count*image->depth;
+
+ float* map = (float*)image->data[0];
+ imbyte* new_map = (imbyte*)NewImage->data[0];
+
+ for (int i = 0; i < count; i++)
+ {
+ if (map[i] > 1)
+ new_map[i] = (imbyte)255;
+ else if (map[i] < 0)
+ new_map[i] = (imbyte)0;
+ else
+ new_map[i] = (imbyte)(map[i]*255);
+ }
+}
+
+template <class T>
+static void DoDirectConv(T* map, imbyte* new_map, int count)
+{
+ for (int i = 0; i < count; i++)
+ {
+ if (map[i] > 255)
+ new_map[i] = (imbyte)255;
+ else if (map[i] < 0)
+ new_map[i] = (imbyte)0;
+ else
+ new_map[i] = (imbyte)(map[i]);
+ }
+}
+
+void imProcessDirectConv(const imImage* image, imImage* NewImage)
+{
+ int count = image->count*image->depth;
+
+ switch(image->data_type)
+ {
+ case IM_USHORT:
+ DoDirectConv((imushort*)image->data[0], (imbyte*)NewImage->data[0], count);
+ break;
+ case IM_INT:
+ DoDirectConv((int*)image->data[0], (imbyte*)NewImage->data[0], count);
+ break;
+ case IM_FLOAT:
+ DoDirectConv((float*)image->data[0], (imbyte*)NewImage->data[0], count);
+ break;
+ }
+}
+
+void imProcessNegative(const imImage* src_image, imImage* dst_image)
+{
+ if (src_image->color_space == IM_MAP)
+ {
+ unsigned char r, g, b;
+ for (int i = 0; i < src_image->palette_count; i++)
+ {
+ imColorDecode(&r, &g, &b, src_image->palette[i]);
+ r = ~r; g = ~g; b = ~b;
+ dst_image->palette[i] = imColorEncode(r, g, b);
+ }
+
+ imImageCopyData(src_image, dst_image);
+ }
+ else if (src_image->color_space == IM_BINARY)
+ {
+ imbyte* map1 = (imbyte*)src_image->data[0];
+ imbyte* map = (imbyte*)dst_image->data[0];
+ for (int i = 0; i < src_image->count; i++)
+ map[i] = map1[i]? 0: 1;
+ }
+ else
+ imProcessToneGamut(src_image, dst_image, IM_GAMUT_INVERT, NULL);
+}