diff options
Diffstat (limited to 'im/src/process')
23 files changed, 9934 insertions, 0 deletions
| diff --git a/im/src/process/im_analyze.cpp b/im/src/process/im_analyze.cpp new file mode 100755 index 0000000..6fa9405 --- /dev/null +++ b/im/src/process/im_analyze.cpp @@ -0,0 +1,1268 @@ +/** \file + * \brief Image Analysis + * + * See Copyright Notice in im_lib.h + * $Id: im_analyze.cpp,v 1.2 2009/09/28 20:19:09 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 imProcessRemoveByArea(const imImage* image, imImage* NewImage, int connect, int start_size, int end_size, int inside) +{ +  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; +  } + +  if (end_size == 0) +    end_size = image->width*image->height; + +  int outside=0; +  if (!inside) outside = 1; + +  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 || area > end_size) +        *img_data = (imbyte)outside; +      else +        *img_data = (imbyte)inside; +    } +    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/im/src/process/im_arithmetic_bin.cpp b/im/src/process/im_arithmetic_bin.cpp new file mode 100755 index 0000000..494b6c0 --- /dev/null +++ b/im/src/process/im_arithmetic_bin.cpp @@ -0,0 +1,587 @@ +/** \file + * \brief Binary Arithmetic Operations + * + * See Copyright Notice in im_lib.h + * $Id: im_arithmetic_bin.cpp,v 1.2 2009/10/01 02:56:58 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 DoBinaryOpByte(imbyte *map1, imbyte *map2, imbyte *map, int count, int op) +{ +  int i; + +  switch(op) +  { +  case IM_BIN_ADD: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(add_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_SUB: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(sub_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_MUL: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(mul_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_DIV: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(div_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_DIFF: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(diff_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_MIN: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(min_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_MAX: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(max_op((int)map1[i], (int)map2[i])); +    break; +  case IM_BIN_POW: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(pow_op((int)map1[i], (int)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 +        DoBinaryOpByte((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; +  } +} + +template <class T1>  +static void DoBinaryConstOpByte(T1 *map1, int value, imbyte *map, int count, int op) +{ +  int i; + +  switch(op) +  { +  case IM_BIN_ADD: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(add_op((int)map1[i], value)); +    break; +  case IM_BIN_SUB: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(sub_op((int)map1[i], value)); +    break; +  case IM_BIN_MUL: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(mul_op((int)map1[i], value)); +    break; +  case IM_BIN_DIV: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(div_op((int)map1[i], value)); +    break; +  case IM_BIN_DIFF: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(diff_op((int)map1[i], value)); +    break; +  case IM_BIN_MIN: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(min_op((int)map1[i], value)); +    break; +  case IM_BIN_MAX: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(max_op((int)map1[i], value)); +    break; +  case IM_BIN_POW: +    for (i = 0; i < count; i++) +      map[i] = (imbyte)crop_byte(pow_op((int)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 +        DoBinaryConstOpByte((imbyte*)src_image1->data[i], (int)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) +        DoBinaryConstOpByte((imushort*)src_image1->data[i], (int)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) +        DoBinaryConstOpByte((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]; +    imProcessArithmeticOp(image, acum_image, acum_image, IM_BIN_ADD);  /* acum_image += image */ + +  } + +  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 +    imProcessArithmeticOp(aux_image, dst_image, dst_image, IM_BIN_ADD); +  } + +  // 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/im/src/process/im_arithmetic_un.cpp b/im/src/process/im_arithmetic_un.cpp new file mode 100755 index 0000000..e4dba8a --- /dev/null +++ b/im/src/process/im_arithmetic_un.cpp @@ -0,0 +1,256 @@ +/** \file + * \brief Unary Arithmetic Operations + * + * See Copyright Notice in im_lib.h + * $Id: im_arithmetic_un.cpp,v 1.2 2009/10/01 02:56:58 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_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] = sqrt_op((T2)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; +  } +} + +template <class T1>  +static void DoUnaryOpByte(T1 *map, imbyte *new_map, int count, int op) +{ +  int i; + +  switch(op) +  { +  case IM_UN_ABS: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(abs_op((int)map[i])); +    break; +  case IM_UN_INV: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(inv_op((int)map[i]));   /* will always be 0 */ +    break; +  case IM_UN_EQL: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte((int)map[i]); +    break; +  case IM_UN_LESS: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(less_op((int)map[i])); +    break; +  case IM_UN_SQR: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(sqr_op((int)map[i])); +    break; +  case IM_UN_SQRT: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(sqrt_op((int)map[i])); +    break; +  case IM_UN_LOG: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(log_op((int)map[i])); +    break; +  case IM_UN_SIN: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(sin_op((int)map[i])); +    break; +  case IM_UN_COS: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(cos_op((int)map[i])); +    break; +  case IM_UN_EXP: +    for (i = 0; i < count; i++) +      new_map[i] = (imbyte)crop_byte(exp_op((int)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 +      DoUnaryOpByte((imbyte*)src_image->data[0], (imbyte*)dst_image->data[0], total_count, op); +    break;                                                                                 +  case IM_USHORT: +    if (dst_image->data_type == IM_BYTE) +      DoUnaryOpByte((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) +      DoUnaryOpByte((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/im/src/process/im_canny.cpp b/im/src/process/im_canny.cpp new file mode 100755 index 0000000..d749fc0 --- /dev/null +++ b/im/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/im/src/process/im_color.cpp b/im/src/process/im_color.cpp new file mode 100755 index 0000000..b27d4b3 --- /dev/null +++ b/im/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/im/src/process/im_convolve.cpp b/im/src/process/im_convolve.cpp new file mode 100755 index 0000000..a88a25c --- /dev/null +++ b/im/src/process/im_convolve.cpp @@ -0,0 +1,1594 @@ +/** \file + * \brief Convolution Operations + * + * See Copyright Notice in im_lib.h + * $Id: im_convolve.cpp,v 1.2 2009/10/01 02:56:58 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; +} + +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; +} + +template <class T1, class T2>  +static void DoSharpOp(T1 *src_map, T1 *dst_map, int count, float amount, T2 threshold, int gauss) +{ +  int i; +  T1 min, max; + +  int size_of = sizeof(imbyte); +  if (sizeof(T1) == size_of) +  { +    min = 0; +    max = 255; +  } +  else +  { +    imMinMax(src_map, count, min, max); + +    if (min == max) +    { +      max = min + 1; + +      if (min != 0) +        min = min - 1; +    } +  } + +  for (i = 0; i < count; i++) +  { +    T2 diff; +     +    if (gauss) +      diff = 20*(src_map[i] - dst_map[i]);  /* dst_map contains a gaussian filter of the source image, must compensate for small edge values */ +    else +      diff = dst_map[i];  /* dst_map contains a laplacian filter of the source image */ + +    if (threshold && abs_op(2*diff) < threshold) +      diff = 0; + +    T2 value = (T2)(src_map[i] + amount*diff); +    if (value < min) +      value = min; +    else if (value > max) +      value = max; + +    dst_map[i] = (T1)value; +  } +} + +static void doSharp(const imImage* src_image, imImage* dst_image, float amount, float threshold, int gauss) +{ +  int count = src_image->count; + +  for (int i = 0; i < src_image->depth; i++) +  { +    switch(src_image->data_type) +    { +    case IM_BYTE: +      DoSharpOp((imbyte*)src_image->data[i], (imbyte*)dst_image->data[i], count, amount, (int)threshold, gauss); +      break; +    case IM_USHORT: +      DoSharpOp((imushort*)src_image->data[i], (imushort*)dst_image->data[i], count, amount, (int)threshold, gauss); +      break; +    case IM_INT: +      DoSharpOp((int*)src_image->data[i], (int*)dst_image->data[i], count, amount, (int)threshold, gauss); +      break; +    case IM_FLOAT: +      DoSharpOp((float*)src_image->data[i], (float*)dst_image->data[i], count, amount, (float)threshold, gauss); +      break; +    } +  } +} + +int imProcessUnsharp(const imImage* src_image, imImage* dst_image, float stddev, float amount, float threshold) +{ +  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*)"Unsharp"); +  imProcessRenderGaussian(kernel, stddev); + +  int ret = imProcessConvolveSep(src_image, dst_image, kernel); +  doSharp(src_image, dst_image, amount, threshold, 1); + +  imImageDestroy(kernel); + +  return ret; +} + +int imProcessSharp(const imImage* src_image, imImage* dst_image, float amount, float threshold) +{ +  imImage* kernel = imKernelLaplacian8(); +  if (!kernel) +    return 0; + +  int ret = imProcessConvolve(src_image, dst_image, kernel); +  doSharp(src_image, dst_image, amount, threshold, 0); + +  imImageDestroy(kernel); + +  return ret; +} + +static int iProcessCheckKernelType(const imImage* kernel) +{ +  if (kernel->data_type == IM_INT) +  { +    int* kernel_data = (int*)kernel->data[0]; +    for (int i = 0; i < kernel->count; i++) +    { +      if (kernel_data[i] < 0)   /* if there are negative values, assume kernel is an edge detector */ +        return 0; +    } +  } +  else if (kernel->data_type == IM_FLOAT) +  { +    float* kernel_data = (float*)kernel->data[0]; +    for (int i = 0; i < kernel->count; i++) +    { +      if (kernel_data[i] < 0)   /* if there are negative values, assume kernel is an edge detector */ +        return 0; +    } +  } +  return 1;  /* default is kernel is a smooth filter */ +} + +int imProcessSharpKernel(const imImage* src_image, const imImage* kernel, imImage* dst_image, float amount, float threshold) +{ +  int ret = imProcessConvolve(src_image, dst_image, kernel); +  doSharp(src_image, dst_image, amount, threshold, iProcessCheckKernelType(kernel)); +  return ret; +} + diff --git a/im/src/process/im_convolve_rank.cpp b/im/src/process/im_convolve_rank.cpp new file mode 100755 index 0000000..5488a78 --- /dev/null +++ b/im/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/im/src/process/im_distance.cpp b/im/src/process/im_distance.cpp new file mode 100755 index 0000000..019356d --- /dev/null +++ b/im/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/im/src/process/im_effects.cpp b/im/src/process/im_effects.cpp new file mode 100755 index 0000000..7f65ce6 --- /dev/null +++ b/im/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/im/src/process/im_fft.cpp b/im/src/process/im_fft.cpp new file mode 100755 index 0000000..2a36880 --- /dev/null +++ b/im/src/process/im_fft.cpp @@ -0,0 +1,198 @@ +/** \file + * \brief Fast Fourier Transform using FFTW library + * + * See Copyright Notice in im_lib.h + * $Id: im_fft.cpp,v 1.2 2009/08/20 12:37:11 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/im/src/process/im_geometric.cpp b/im/src/process/im_geometric.cpp new file mode 100755 index 0000000..a0b5129 --- /dev/null +++ b/im/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/im/src/process/im_histogram.cpp b/im/src/process/im_histogram.cpp new file mode 100755 index 0000000..e6796fe --- /dev/null +++ b/im/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/im/src/process/im_houghline.cpp b/im/src/process/im_houghline.cpp new file mode 100755 index 0000000..6ead982 --- /dev/null +++ b/im/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/im/src/process/im_kernel.cpp b/im/src/process/im_kernel.cpp new file mode 100755 index 0000000..d5e976e --- /dev/null +++ b/im/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/im/src/process/im_logic.cpp b/im/src/process/im_logic.cpp new file mode 100755 index 0000000..82e607d --- /dev/null +++ b/im/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/im/src/process/im_morphology_bin.cpp b/im/src/process/im_morphology_bin.cpp new file mode 100755 index 0000000..9405ff6 --- /dev/null +++ b/im/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/im/src/process/im_morphology_gray.cpp b/im/src/process/im_morphology_gray.cpp new file mode 100755 index 0000000..c3c9d45 --- /dev/null +++ b/im/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/im/src/process/im_quantize.cpp b/im/src/process/im_quantize.cpp new file mode 100755 index 0000000..de78cf4 --- /dev/null +++ b/im/src/process/im_quantize.cpp @@ -0,0 +1,64 @@ +/** \file + * \brief Additional Image Quantization Operations + * + * See Copyright Notice in im_lib.h + * $Id: im_quantize.cpp,v 1.2 2009/09/25 18:40:32 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]; + +  imImageSetPalette(dst_image, imPaletteUniform(), 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/im/src/process/im_render.cpp b/im/src/process/im_render.cpp new file mode 100755 index 0000000..f5d296f --- /dev/null +++ b/im/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/im/src/process/im_resize.cpp b/im/src/process/im_resize.cpp new file mode 100755 index 0000000..ddf6e47 --- /dev/null +++ b/im/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/im/src/process/im_statistics.cpp b/im/src/process/im_statistics.cpp new file mode 100755 index 0000000..b9f086d --- /dev/null +++ b/im/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/im/src/process/im_threshold.cpp b/im/src/process/im_threshold.cpp new file mode 100755 index 0000000..4af72ee --- /dev/null +++ b/im/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/im/src/process/im_tonegamut.cpp b/im/src/process/im_tonegamut.cpp new file mode 100755 index 0000000..cf63350 --- /dev/null +++ b/im/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); +} | 
