图像基本处理算法的简单实现(三)
2014-01-09 14:24
519 查看
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://vaero.blog.51cto.com/4350852/823004
图像基本处理算法的简单实现(一)
图像基本处理算法的简单实现(二)
图像基本处理算法的简单实现(三)
书内叙述的细化算法:
6)欧拉数
欧拉数被定义为连接体数与其中的孔洞数之差,用公式表述:E=C-H。由此,我们可以求得孔洞数H=C-E(其实一样的,只是判断时方便理解==)。
实现为八邻域的的(公式亦看实现代码注释里^^)。主要注意方式1,二值化图像周围一圈需要是背景色。详细如下:
方式1(广泛应用,包括MATLAB):
方式2(图段方式):
方式2从下载的论文文献里看到的^^。
三、后记
好吧,小弟承认基本也就只知道这么多了T^T。
中间再加一下分割,用过线数啥的方法,简单识别识别数字还是可以的==。
推荐阅读
1、精通Visual.Cpp数字图像处理典型算法及实现 张宏林
2、【课件】数字图像处理和分析技术(清华大学) 章毓晋
ps:其实这些算法的话,在matlab调用一个函数就可以了==。可以Google搜索“MATLAB 数值计算 方法”类似的关键字。
图像基本处理算法的简单实现(一)
图像基本处理算法的简单实现(二)
图像基本处理算法的简单实现(三)
书内叙述的细化算法:
/** * 对二值化Bitmap进行细化运算后返回 * * 采用“精通Visual.Cpp数字图像处理典型算法及实现(第2版)”内叙述的细化算法 * * JNIEnv* jni环境(jni必要参数) * jobject java对象(jni必要参数) * jintArray Bitmap所有像素值 * int Bitmap宽度 * int Bitmap高度 */ JNIEXPORT jintArray JNICALL Java_org_join_image_util_JoinImage_thinning2( JNIEnv* env, jobject obj, jintArray buf, int w, int h) { LOGE("==thinning2=="); jint * cbuf; cbuf = (*env)->GetIntArrayElements(env, buf, 0); // 获取int数组元素 int black = 0xFF000000; // 不透明黑色 unsigned char foreground = 0xFF; // 前景灰度值:255(白) unsigned char background = 0; // 背景灰度值:0(黑) jboolean modified = 1; // 设置脏标记:true unsigned char count; // 计数器 unsigned char mark[w][h]; // 可删除标记 int i, j, m, n; // 循环标记 unsigned char gray; // 灰度值 unsigned char grays[5][5]; // 5×5相邻区域像素值 jint *p; // 指向源图像像素的指针 /* * 一次迭代操作(直到没有点再满足标记条件) * * 8-领域示意图: * * P3 P2 P9 * P4 P1 P8 * P5 P6 P7 * * 删除条件: * * (1.1) 2<=NZ(p1)<=6 * (1.2) Zo(p1)=1 * (1.3) p2*p4*p8=0或者Zo(p2)!=1 * (1.4) p2*p4*p6=0或者Zo(p4)!=1 * * NZ(p1):p1的非零邻点的个数 * Zo(p1):以p2 ,p3 ,…… ,p9为序时这些点的值从0到1变化的次数 */ while (modified) { modified = 0; // 设置脏标记:false memset(mark, 0, sizeof(mark)); // 重置删除标记为false // 由于使用5×5的结构元素,防止越界,不处理上下左右四边两层像素 for (i = 2; i < h - 2; i++) { for (j = 2; j < w - 2; j++) { p = cbuf + w * i + j; // 指向源图像i行j列 gray = (*p) & 0xFF; // 获得灰度值 // 如果当前点为背景灰度值则跳过 if (gray == background) { continue; } // 获得当前点相邻的5×5区域内像素值(前景用1代表,背景用0代表) for (m = -2; m <= 2; m++) { for (n = -2; n <= 2; n++) { // 前景色灰度值为255,所以直接除255即可 grays[m + 2][n + 2] = ((*(p + w * m + n)) & 0xFF) / 255; } } // 判断条件(1.1) 2<=NZ(p1)<=6 count = grays[1][1] + grays[1][2] + grays[1][3] + grays[2][1] + grays[2][3] + grays[3][1] + grays[3][2] + grays[3][3]; if (2 <= count && count <= 6) { } else { continue; // 条件(1.1)不成立,跳出循环 } // 计算Zo(p1):四周像素由0变1的次数 count = 0; // 重置计数器 if (grays[1][2] < grays[1][1]) count++; // p2->p3 if (grays[1][1] < grays[2][1]) count++; // p3->p4 if (grays[2][1] < grays[3][1]) count++; // p4->p5 if (grays[3][1] < grays[3][2]) count++; // p5->p6 if (grays[3][2] < grays[3][3]) count++; // p6->p7 if (grays[3][3] < grays[2][3]) count++; // p7->p8 if (grays[2][3] < grays[1][3]) count++; // p8->p9 if (grays[1][3] < grays[1][2]) count++; // p9->p2 // 判断条件(1.2) Zo(p1)=1 if (1 == count) { } else { continue; // 条件(1.2)不成立,跳出循环 } // 判断条件(1.3) p2*p4*p8=0或者Zo(p2)!=1 if (grays[1][2] * grays[2][1] * grays[2][3] == 0) { } else { // 计算Zo(p2):四周像素由0变1的次数 count = 0; if (grays[0][2] < grays[0][1]) count++; if (grays[0][1] < grays[1][1]) count++; if (grays[1][1] < grays[2][1]) count++; if (grays[2][1] < grays[2][2]) count++; if (grays[2][2] < grays[2][3]) count++; if (grays[2][3] < grays[1][3]) count++; if (grays[1][3] < grays[0][3]) count++; if (grays[0][3] < grays[0][2]) count++; if (count != 1) { } else { continue; // 条件(1.3)不成立,跳出循环 } } // 判断条件(1.4) p2*p4*p6=0或者Zo(p4)!=1 if (grays[1][2] * grays[2][1] * grays[3][2] == 0) { } else { // 计算Zo(p4):四周像素由0变1的次数 count = 0; if (grays[1][1] < grays[1][0]) count++; if (grays[1][0] < grays[2][0]) count++; if (grays[2][0] < grays[3][0]) count++; if (grays[3][0] < grays[3][1]) count++; if (grays[3][1] < grays[3][2]) count++; if (grays[3][2] < grays[2][2]) count++; if (grays[2][2] < grays[1][2]) count++; if (grays[1][2] < grays[1][1]) count++; if (count != 1) { } else { continue; // 条件(1.4)不成立,跳出循环 } } /* * 四条件都成立时 */ mark[j][i] = 1; // 删除标记为true modified = 1; // 脏标记为true } } // 由删除标记去除 if (modified) { for (i = 2; i < h - 2; i++) { for (j = 2; j < w - 2; j++) { // 如果删除标记为true if (1 == mark[j][i]) { cbuf[w * i + j] = black; // 修改成背景色(黑) } } } } } int size = w * h; jintArray result = (*env)->NewIntArray(env, size); // 新建一个jintArray (*env)->SetIntArrayRegion(env, result, 0, size, cbuf); // 将cbuf转存入result (*env)->ReleaseIntArrayElements(env, buf, cbuf, 0); // 释放int数组元素 return result; }
6)欧拉数
欧拉数被定义为连接体数与其中的孔洞数之差,用公式表述:E=C-H。由此,我们可以求得孔洞数H=C-E(其实一样的,只是判断时方便理解==)。
实现为八邻域的的(公式亦看实现代码注释里^^)。主要注意方式1,二值化图像周围一圈需要是背景色。详细如下:
/** * 以该方式求欧拉数,二值化图像周围一圈需要是背景色 * * 如果二值化图像切割到边缘(周围一圈有前景色) * 这时两侧和下边被包在前景色内的背景色都会算作孔洞,如下: * * 0 1 1 0 \亦表示0,用于区分 * 1 \ \ 1 * \ \ 1 \ * \ 1 \ \ * 1 1 1 1 * 1 1 \ 1 * * 右斜杠部分会被视作是个孔洞(也就是欧拉数非预期值) * 欧拉数=-2;孔洞数=1-(-2)=3 */
方式1(广泛应用,包括MATLAB):
/** * 求二值图像欧拉数(二值化图像周围一圈需要是背景色) */ int euler(jint *color, int w, int h) { /** * E(8) = (S1-S3-2X) / 4(8-连通) * * S1、S3 和X代表二值图像 中具有下列模式的2×2方块个数 * * S1:1个1,3个0的模式 * S3:3个1,1个0的模式 * X:2个1在一条对角线,2个0在另一条对角线上的模式 */ int count_s1 = 0, count_s3 = 0, count_x = 0; // 模式计数 int count_1; // 1的计数 int gray[2][2]; // 2×2方块颜色值 int white = 0xFFFFFFFF; // 不透明白色(前景色) int i, j, m, n; // 防止越界,最右和最下两行像素不处理 for (i = 0; i < h - 1; i++) { for (j = 0; j < w - 1; j++) { // 计数置0 count_1 = 0; // 获取2×2方块灰度值 for (m = 0; m < 2; m++) { for (n = 0; n < 2; n++) { gray[m] = *(color + w * i + j + w * m + n); // 判断是否为前景色 if (white == gray[m] ) { count_1++; } } } // 判断是否为S1模式 if (1 == count_1) { count_s1++; } // 判断是否为S3模式 else if (3 == count_1) { count_s3++; } // 判断是否为X模式 else if (2 == count_1) { // 判断对角线是否同背景色 if (gray[0][0] == gray[1][1]) { count_x++; } } } } // LOGE("s1=%d;s2=%d;x=%d", count_s1, count_s3, count_x); // 返回欧拉数 return (count_s1 - count_s3 - 2 * count_x) / 4; }
方式2(图段方式):
/** * 定义图段结构 */ struct scope { int start; // 开始索引 int end; // 结束索引 // int length; // end - start + 1 }; /** * 求某行[start, end]间图段个数 */ int countScope(jint *line, int start, int end) { int white = 0xFFFFFFFF; // 不透明白色(前景色) unsigned char tag = 0; // 变换标记 int i, count = 0; for (i = start; i <= end; i++) { if (*(line + i) == white) { // 如果是前景色 if (tag == 0) { // 如果之前是背景色 tag = 1; // 设置为前景色标记 count++; // 交点+1 } } else { // 否则背景色 if (tag == 1) { // 如果之前是前景色 tag = 0; // 设置为背景色标记 } } } return count; } /** * 求二值图像欧拉数(由图段和相领数计算) */ int euler2(jint *color, int w, int h) { int white = 0xFFFFFFFF; // 不透明白色(前景色) unsigned char tag; // 变换标记 /** * E = ∑[i=1->I] ∑[n=0->N(i)] (1- Vm) * * I:图像的行数 * N(i):图像第i行内的图段个数 * Vm:图像第i行,第n个图段所对应的相邻数 */ jint *line, *frontLine; // 定义行指针 line = color; // 指向首地址 int scopeNum, scopeNeighbor; // 图段计数,相邻图段数 int eulerNum = 0; // 欧拉数 int count; // 计数 int i, j, k, m, n; for (i = 0; i <= h - 1; i++) { scopeNum = countScope(line, 0, w - 1); // 计算该行图段数 struct scope sco[scopeNum]; // 定义相应图段结构体数组 count = 0; // 计数置0 tag = 0; // 标记置0 // 该行图段赋值 for (j = 0; j <= w - 1; j++) { if (*(line + j) == white) { // 如果是前景色 if (tag == 0) { // 如果之前是背景色 tag = 1; // 设置为前景色标记 sco[count].start = j; // 该行对应图段起始索引 } // 如果该前景色已是最后一位 if (j == w - 1) { sco[count].end = j; // 该行对应图段结束索引 } } else { // 否则背景色 if (tag == 1) { // 如果之前是前景色 tag = 0; // 设置为背景色标记 sco[count].end = j - 1; // 该行对应图段结束索引 count++; // 计数+1 } } } // 该行各图段贡献值 for (k = 0; k <= scopeNum - 1; k++) { /* 求Vm:图段f(i,j:j+K-1)的相邻数 * * 4-连通:f(i-1,j:j+K-1) 该图段范围上一行内图段数 * 8-连通:f(i-1,j-1:j+K) 该图段范围上一行左右各+1像素内图段数 */ if (i == 0) { // 如果是第一行 scopeNeighbor = 0; // 图段相邻数 } else { // 图段相邻数(8-连通) frontLine = line - w; // 前一行指针 m = (sco[k].start - 1 <= 0) ? 0 : sco[k].start - 1; // 开始位置 n = (sco[k].end + 1 >= w - 1) ? w - 1 : sco[k].end + 1; // 结束位置 scopeNeighbor = countScope(frontLine, m, n); } // LOGE( "第%d行第%d个图段相邻数:%d", i + 1, k + 1, scopeNeighbor); eulerNum = eulerNum + 1 - scopeNeighbor; } line = line + w; // 指向下一行 } return eulerNum; }
方式2从下载的论文文献里看到的^^。
三、后记
好吧,小弟承认基本也就只知道这么多了T^T。
中间再加一下分割,用过线数啥的方法,简单识别识别数字还是可以的==。
推荐阅读
1、精通Visual.Cpp数字图像处理典型算法及实现 张宏林
2、【课件】数字图像处理和分析技术(清华大学) 章毓晋
ps:其实这些算法的话,在matlab调用一个函数就可以了==。可以Google搜索“MATLAB 数值计算 方法”类似的关键字。
相关文章推荐
- Linux VIM增加代码浏览功能
- unity鼠标拾取网格顶点
- 团队管理要师出有名
- poj 2376 Cleaning Shifts
- 双击单元格修改内容
- 商业银行把握机会,成就卓越绩效的建议
- Python学习笔记[1]
- java的System.getProperty()方法可以获取的值
- 图像基本处理算法的简单实现(二)
- Android中Path类的lineTo方法和quadTo方法画线的区别
- 基于分布式流计算平台(storm)的CGI采集与清理系统
- In-Game HD Video Capture using Real-Time YUYV-DXT Compression
- 浙大ZOJ 1011 NTA问题解决
- lwqq
- ServletContextListener使用详解
- 图像基本处理算法的简单实现(一)
- 串口编程利器:Pcomm lite 串口库介绍
- A general iterated shinkage algorithm for non-convex sparse coding.
- netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}' 详解
- Review Board,时区,HTTP