图像基本处理算法的简单实现(一)

 图像基本处理算法的简单实现(二)

 图像基本处理算法的简单实现(三)
 
书内叙述的细化算法:
  1. /** 
  2.  * 对二值化Bitmap进行细化运算后返回 
  3.  * 
  4.  * 采用“精通Visual.Cpp数字图像处理典型算法及实现(第2版)”内叙述的细化算法 
  5.  * 
  6.  * JNIEnv*  jni环境(jni必要参数) 
  7.  * jobject  java对象(jni必要参数) 
  8.  * jintArray    Bitmap所有像素值 
  9.  * int  Bitmap宽度 
  10.  * int  Bitmap高度 
  11.  */ 
  12. JNIEXPORT jintArray JNICALL Java_org_join_image_util_JoinImage_thinning2( 
  13.         JNIEnv* env, jobject obj, jintArray buf, int w, int h) { 
  14.     LOGE("==thinning2=="); 
  15.  
  16.     jint * cbuf; 
  17.     cbuf = (*env)->GetIntArrayElements(env, buf, 0); // 获取int数组元素 
  18.  
  19.     int black = 0xFF000000; // 不透明黑色 
  20.  
  21.     unsigned char foreground = 0xFF; // 前景灰度值:255(白) 
  22.     unsigned char background = 0; // 背景灰度值:0(黑) 
  23.  
  24.     jboolean modified = 1; // 设置脏标记:true 
  25.     unsigned char count; // 计数器 
  26.     unsigned char mark[w][h]; // 可删除标记 
  27.  
  28.     int i, j, m, n; // 循环标记 
  29.     unsigned char gray; // 灰度值 
  30.     unsigned char grays[5][5]; // 5×5相邻区域像素值 
  31.     jint *p; // 指向源图像像素的指针 
  32.  
  33.     /* 
  34.      * 一次迭代操作(直到没有点再满足标记条件) 
  35.      * 
  36.      * 8-领域示意图: 
  37.      * 
  38.      * P3   P2  P9 
  39.      * P4   P1  P8 
  40.      * P5   P6  P7 
  41.      * 
  42.      * 删除条件: 
  43.      * 
  44.      * (1.1) 2<=NZ(p1)<=6 
  45.      * (1.2) Zo(p1)=1 
  46.      * (1.3) p2*p4*p8=0或者Zo(p2)!=1 
  47.      * (1.4) p2*p4*p6=0或者Zo(p4)!=1 
  48.      * 
  49.      * NZ(p1):p1的非零邻点的个数 
  50.      * Zo(p1):以p2 ,p3 ,…… ,p9为序时这些点的值从0到1变化的次数 
  51.      */ 
  52.     while (modified) { 
  53.         modified = 0; // 设置脏标记:false 
  54.         memset(mark, 0, sizeof(mark)); // 重置删除标记为false 
  55.  
  56.         // 由于使用5×5的结构元素,防止越界,不处理上下左右四边两层像素 
  57.         for (i = 2; i < h - 2; i++) { 
  58.             for (j = 2; j < w - 2; j++) { 
  59.  
  60.                 p = cbuf + w * i + j; // 指向源图像i行j列 
  61.                 gray = (*p) & 0xFF; // 获得灰度值 
  62.  
  63.                 // 如果当前点为背景灰度值则跳过 
  64.                 if (gray == background) { 
  65.                     continue
  66.                 } 
  67.  
  68.                 // 获得当前点相邻的5×5区域内像素值(前景用1代表,背景用0代表) 
  69.                 for (m = -2; m <= 2; m++) { 
  70.                     for (n = -2; n <= 2; n++) { 
  71.                         // 前景色灰度值为255,所以直接除255即可 
  72.                         grays[m + 2][n + 2] = ((*(p + w * m + n)) & 0xFF) / 255; 
  73.                     } 
  74.                 } 
  75.  
  76.                 // 判断条件(1.1) 2<=NZ(p1)<=6 
  77.                 count = grays[1][1] + grays[1][2] + grays[1][3] + grays[2][1] 
  78.                         + grays[2][3] + grays[3][1] + grays[3][2] + grays[3][3]; 
  79.                 if (2 <= count && count <= 6) { 
  80.                 } else { 
  81.                     continue// 条件(1.1)不成立,跳出循环 
  82.                 } 
  83.  
  84.                 // 计算Zo(p1):四周像素由0变1的次数 
  85.                 count = 0; // 重置计数器 
  86.                 if (grays[1][2] < grays[1][1]) 
  87.                     count++; // p2->p3 
  88.                 if (grays[1][1] < grays[2][1]) 
  89.                     count++; // p3->p4 
  90.                 if (grays[2][1] < grays[3][1]) 
  91.                     count++; // p4->p5 
  92.                 if (grays[3][1] < grays[3][2]) 
  93.                     count++; // p5->p6 
  94.                 if (grays[3][2] < grays[3][3]) 
  95.                     count++; // p6->p7 
  96.                 if (grays[3][3] < grays[2][3]) 
  97.                     count++; // p7->p8 
  98.                 if (grays[2][3] < grays[1][3]) 
  99.                     count++; // p8->p9 
  100.                 if (grays[1][3] < grays[1][2]) 
  101.                     count++; // p9->p2 
  102.  
  103.                 // 判断条件(1.2) Zo(p1)=1 
  104.                 if (1 == count) { 
  105.                 } else { 
  106.                     continue// 条件(1.2)不成立,跳出循环 
  107.                 } 
  108.  
  109.                 // 判断条件(1.3) p2*p4*p8=0或者Zo(p2)!=1 
  110.                 if (grays[1][2] * grays[2][1] * grays[2][3] == 0) { 
  111.                 } else { 
  112.                     // 计算Zo(p2):四周像素由0变1的次数 
  113.                     count = 0; 
  114.                     if (grays[0][2] < grays[0][1]) 
  115.                         count++; 
  116.                     if (grays[0][1] < grays[1][1]) 
  117.                         count++; 
  118.                     if (grays[1][1] < grays[2][1]) 
  119.                         count++; 
  120.                     if (grays[2][1] < grays[2][2]) 
  121.                         count++; 
  122.                     if (grays[2][2] < grays[2][3]) 
  123.                         count++; 
  124.                     if (grays[2][3] < grays[1][3]) 
  125.                         count++; 
  126.                     if (grays[1][3] < grays[0][3]) 
  127.                         count++; 
  128.                     if (grays[0][3] < grays[0][2]) 
  129.                         count++; 
  130.  
  131.                     if (count != 1) { 
  132.                     } else { 
  133.                         continue// 条件(1.3)不成立,跳出循环 
  134.                     } 
  135.                 } 
  136.  
  137.                 // 判断条件(1.4) p2*p4*p6=0或者Zo(p4)!=1 
  138.                 if (grays[1][2] * grays[2][1] * grays[3][2] == 0) { 
  139.                 } else { 
  140.                     // 计算Zo(p4):四周像素由0变1的次数 
  141.                     count = 0; 
  142.                     if (grays[1][1] < grays[1][0]) 
  143.                         count++; 
  144.                     if (grays[1][0] < grays[2][0]) 
  145.                         count++; 
  146.                     if (grays[2][0] < grays[3][0]) 
  147.                         count++; 
  148.                     if (grays[3][0] < grays[3][1]) 
  149.                         count++; 
  150.                     if (grays[3][1] < grays[3][2]) 
  151.                         count++; 
  152.                     if (grays[3][2] < grays[2][2]) 
  153.                         count++; 
  154.                     if (grays[2][2] < grays[1][2]) 
  155.                         count++; 
  156.                     if (grays[1][2] < grays[1][1]) 
  157.                         count++; 
  158.  
  159.                     if (count != 1) { 
  160.                     } else { 
  161.                         continue// 条件(1.4)不成立,跳出循环 
  162.                     } 
  163.                 } 
  164.  
  165.                 /* 
  166.                  * 四条件都成立时 
  167.                  */ 
  168.                 mark[j][i] = 1; // 删除标记为true 
  169.                 modified = 1; // 脏标记为true 
  170.             } 
  171.         } 
  172.  
  173.         // 由删除标记去除 
  174.         if (modified) { 
  175.             for (i = 2; i < h - 2; i++) { 
  176.                 for (j = 2; j < w - 2; j++) { 
  177.                     // 如果删除标记为true 
  178.                     if (1 == mark[j][i]) { 
  179.                         cbuf[w * i + j] = black; // 修改成背景色(黑) 
  180.                     } 
  181.                 } 
  182.             } 
  183.         } 
  184.  
  185.     } 
  186.  
  187.     int size = w * h; 
  188.     jintArray result = (*env)->NewIntArray(env, size); // 新建一个jintArray 
  189.     (*env)->SetIntArrayRegion(env, result, 0, size, cbuf); // 将cbuf转存入result 
  190.     (*env)->ReleaseIntArrayElements(env, buf, cbuf, 0); // 释放int数组元素 
  191.     return result; 
 
6)欧拉数
         欧拉数被定义为连接体数与其中的孔洞数之差,用公式表述:E=C-H。由此,我们可以求得孔洞数H=C-E(其实一样的,只是判断时方便理解==)。
         实现为八邻域的的(公式亦看实现代码注释里^^)。主要注意方式1,二值化图像周围一圈需要是背景色。详细如下:
  1. /** 
  2.  * 以该方式求欧拉数,二值化图像周围一圈需要是背景色 
  3.  * 
  4.  * 如果二值化图像切割到边缘(周围一圈有前景色) 
  5.  * 这时两侧和下边被包在前景色内的背景色都会算作孔洞,如下: 
  6.  * 
  7.  * 0 1 1 0      \亦表示0,用于区分 
  8.  * 1 \ \ 1 
  9.  * \ \ 1 \ 
  10.  * \ 1 \ \ 
  11.  * 1 1 1 1 
  12.  * 1 1 \ 1 
  13.  * 
  14.  * 右斜杠部分会被视作是个孔洞(也就是欧拉数非预期值) 
  15.  * 欧拉数=-2;孔洞数=1-(-2)=3 
  16.  */ 
 
方式1(广泛应用,包括MATLAB):
  1. /** 
  2.  * 求二值图像欧拉数(二值化图像周围一圈需要是背景色) 
  3.  */ 
  4. int euler(jint *color, int w, int h) { 
  5.     /** 
  6.      * E(8) = (S1-S3-2X) / 4(8-连通) 
  7.      * 
  8.      * S1、S3 和X代表二值图像 中具有下列模式的2×2方块个数 
  9.      * 
  10.      * S1:1个1,3个0的模式 
  11.      * S3:3个1,1个0的模式 
  12.      * X:2个1在一条对角线,2个0在另一条对角线上的模式 
  13.      */ 
  14.     int count_s1 = 0, count_s3 = 0, count_x = 0; // 模式计数 
  15.     int count_1; // 1的计数 
  16.     int gray[2][2]; // 2×2方块颜色值 
  17.     int white = 0xFFFFFFFF; // 不透明白色(前景色) 
  18.  
  19.     int i, j, m, n; 
  20.     // 防止越界,最右和最下两行像素不处理 
  21.     for (i = 0; i < h - 1; i++) { 
  22.  
  23.         for (j = 0; j < w - 1; j++) { 
  24.  
  25.             // 计数置0 
  26.             count_1 = 0; 
  27.  
  28.             // 获取2×2方块灰度值 
  29.             for (m = 0; m < 2; m++) { 
  30.                 for (n = 0; n < 2; n++) { 
  31.                     gray[m][n] = *(color + w * i + j + w * m + n); 
  32.  
  33.                     // 判断是否为前景色 
  34.                     if (white == gray[m][n]) { 
  35.                         count_1++; 
  36.                     } 
  37.                 } 
  38.             } 
  39.  
  40.             // 判断是否为S1模式 
  41.             if (1 == count_1) { 
  42.                 count_s1++; 
  43.             } 
  44.  
  45.             // 判断是否为S3模式 
  46.             else if (3 == count_1) { 
  47.                 count_s3++; 
  48.             } 
  49.  
  50.             // 判断是否为X模式 
  51.             else if (2 == count_1) { 
  52.                 // 判断对角线是否同背景色 
  53.                 if (gray[0][0] == gray[1][1]) { 
  54.                     count_x++; 
  55.                 } 
  56.             } 
  57.         } 
  58.     } 
  59.  
  60. //  LOGE("s1=%d;s2=%d;x=%d", count_s1, count_s3, count_x); 
  61.  
  62.     // 返回欧拉数 
  63.     return (count_s1 - count_s3 - 2 * count_x) / 4; 
 
方式2(图段方式):
  1. /** 
  2.  * 定义图段结构 
  3.  */ 
  4. struct scope { 
  5.     int start; // 开始索引 
  6.     int end; // 结束索引 
  7. //    int length; // end - start + 1 
  8. }; 
  9.  
  10. /** 
  11.  * 求某行[start, end]间图段个数 
  12.  */ 
  13. int countScope(jint *line, int start, int end) { 
  14.  
  15.     int white = 0xFFFFFFFF; // 不透明白色(前景色) 
  16.     unsigned char tag = 0; // 变换标记 
  17.  
  18.     int i, count = 0; 
  19.     for (i = start; i <= end; i++) { 
  20.         if (*(line + i) == white) { // 如果是前景色 
  21.             if (tag == 0) { // 如果之前是背景色 
  22.                 tag = 1; // 设置为前景色标记 
  23.                 count++; // 交点+1 
  24.             } 
  25.         } else { // 否则背景色 
  26.             if (tag == 1) { // 如果之前是前景色 
  27.                 tag = 0; // 设置为背景色标记 
  28.             } 
  29.         } 
  30.     } 
  31.  
  32.     return count; 
  33.  
  34. /** 
  35.  * 求二值图像欧拉数(由图段和相领数计算) 
  36.  */ 
  37. int euler2(jint *color, int w, int h) { 
  38.  
  39.     int white = 0xFFFFFFFF; // 不透明白色(前景色) 
  40.     unsigned char tag; // 变换标记 
  41.  
  42.     /** 
  43.      * E = ∑[i=1->I] ∑[n=0->N(i)] (1- Vm) 
  44.      * 
  45.      * I:图像的行数 
  46.      * N(i):图像第i行内的图段个数 
  47.      * Vm:图像第i行,第n个图段所对应的相邻数 
  48.      */ 
  49.     jint *line, *frontLine; // 定义行指针 
  50.     line = color; // 指向首地址 
  51.     int scopeNum, scopeNeighbor; // 图段计数,相邻图段数 
  52.  
  53.     int eulerNum = 0; // 欧拉数 
  54.     int count; // 计数 
  55.  
  56.     int i, j, k, m, n; 
  57.     for (i = 0; i <= h - 1; i++) { 
  58.         scopeNum = countScope(line, 0, w - 1); // 计算该行图段数 
  59.         struct scope sco[scopeNum]; // 定义相应图段结构体数组 
  60.         count = 0; // 计数置0 
  61.         tag = 0; // 标记置0 
  62.  
  63.         // 该行图段赋值 
  64.         for (j = 0; j <= w - 1; j++) { 
  65.             if (*(line + j) == white) { // 如果是前景色 
  66.                 if (tag == 0) { // 如果之前是背景色 
  67.                     tag = 1; // 设置为前景色标记 
  68.                     sco[count].start = j; // 该行对应图段起始索引 
  69.                 } 
  70.                 // 如果该前景色已是最后一位 
  71.                 if (j == w - 1) { 
  72.                     sco[count].end = j; // 该行对应图段结束索引 
  73.                 } 
  74.             } else { // 否则背景色 
  75.                 if (tag == 1) { // 如果之前是前景色 
  76.                     tag = 0; // 设置为背景色标记 
  77.                     sco[count].end = j - 1; // 该行对应图段结束索引 
  78.                     count++; // 计数+1 
  79.                 } 
  80.             } 
  81.         } 
  82.  
  83.         // 该行各图段贡献值 
  84.         for (k = 0; k <= scopeNum - 1; k++) { 
  85.             /* 求Vm:图段f(i,j:j+K-1)的相邻数 
  86.              * 
  87.              * 4-连通:f(i-1,j:j+K-1) 该图段范围上一行内图段数 
  88.              * 8-连通:f(i-1,j-1:j+K) 该图段范围上一行左右各+1像素内图段数 
  89.              */ 
  90.             if (i == 0) { // 如果是第一行 
  91.                 scopeNeighbor = 0; // 图段相邻数 
  92.             } else { 
  93.                 // 图段相邻数(8-连通) 
  94.                 frontLine = line - w; // 前一行指针 
  95.                 m = (sco[k].start - 1 <= 0) ? 0 : sco[k].start - 1; // 开始位置 
  96.                 n = (sco[k].end + 1 >= w - 1) ? w - 1 : sco[k].end + 1; // 结束位置 
  97.                 scopeNeighbor = countScope(frontLine, m, n); 
  98.             } 
  99. //          LOGE( "第%d行第%d个图段相邻数:%d", i + 1, k + 1, scopeNeighbor); 
  100.             eulerNum = eulerNum + 1 - scopeNeighbor; 
  101.         } 
  102.  
  103.         line = line + w; // 指向下一行 
  104.     } 
  105.     return eulerNum; 
 
         方式2从下载的论文文献里看到的^^。
 
三、后记
         好吧,小弟承认基本也就只知道这么多了T^T。
 
         中间再加一下分割,用过线数啥的方法,简单识别识别数字还是可以的==。
 
         推荐阅读
         1、精通Visual.Cpp数字图像处理典型算法及实现 张宏林
         2、【课件】数字图像处理和分析技术(清华大学) 章毓晋
 
         ps:其实这些算法的话,在matlab调用一个函数就可以了==。可以Google搜索“MATLAB 数值计算 方法”类似的关键字。