diff options
Diffstat (limited to 'src/process')
-rw-r--r-- | src/process/im_analyze.cpp | 1262 | ||||
-rw-r--r-- | src/process/im_arithmetic_bin.cpp | 503 | ||||
-rw-r--r-- | src/process/im_arithmetic_un.cpp | 210 | ||||
-rw-r--r-- | src/process/im_canny.cpp | 254 | ||||
-rw-r--r-- | src/process/im_color.cpp | 255 | ||||
-rw-r--r-- | src/process/im_convolve.cpp | 1512 | ||||
-rw-r--r-- | src/process/im_convolve_rank.cpp | 701 | ||||
-rw-r--r-- | src/process/im_distance.cpp | 512 | ||||
-rw-r--r-- | src/process/im_effects.cpp | 86 | ||||
-rw-r--r-- | src/process/im_fft.cpp | 218 | ||||
-rw-r--r-- | src/process/im_geometric.cpp | 724 | ||||
-rw-r--r-- | src/process/im_histogram.cpp | 105 | ||||
-rw-r--r-- | src/process/im_houghline.cpp | 435 | ||||
-rw-r--r-- | src/process/im_kernel.cpp | 293 | ||||
-rw-r--r-- | src/process/im_logic.cpp | 136 | ||||
-rw-r--r-- | src/process/im_morphology_bin.cpp | 317 | ||||
-rw-r--r-- | src/process/im_morphology_gray.cpp | 231 | ||||
-rw-r--r-- | src/process/im_quantize.cpp | 65 | ||||
-rw-r--r-- | src/process/im_render.cpp | 532 | ||||
-rw-r--r-- | src/process/im_resize.cpp | 332 | ||||
-rw-r--r-- | src/process/im_statistics.cpp | 341 | ||||
-rw-r--r-- | src/process/im_threshold.cpp | 391 | ||||
-rw-r--r-- | src/process/im_tonegamut.cpp | 322 |
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 ®ion_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(<->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); +} |