file-type

VC++实现索引表细化算法处理BMP图像

RAR文件

4星 · 超过85%的资源 | 下载需积分: 9 | 3.6MB | 更新于2025-06-07 | 115 浏览量 | 22 下载量 举报 收藏
download 立即下载
在详细介绍基于索引表的细化算法程序源码之前,首先需要了解细化算法的概念、BMP图像格式、以及如何在VC++环境下处理图像数据。 细化算法是一种用于图像处理的算法,其目的是减少图像中的笔画宽度,只保留图像中心线的像素,用于优化图像数据的存储、传输或是为了满足特定视觉效果的需求。细化算法在诸如手写识别、光学字符识别(OCR)、模式识别等领域有着广泛的应用。 BMP图像格式是一种常用的图像文件格式,由微软公司开发,用于存储数字图像。BMP格式可以存储二值图像、灰度图像、256色图像、真彩色图像等。在该程序中建议使用256色BMP图像,这是因为256色图像颜色层次较为简单,便于进行算法处理,同时避免了真彩色图像的复杂性。在细化操作中,通常会将图像转化为二值图像,即只包含两种颜色:白色作为背景色,黑色作为线数据色。 VC++(Visual C++)是微软公司推出的一款集成开发环境(IDE),支持多种编程语言,其中C和C++是最常用的。VC++广泛应用于Windows平台下的软件开发,包括应用程序、系统软件、游戏以及驱动程序等。在图像处理方面,VC++可以调用各种图像处理库来实现对图像的读取、编辑和保存等功能。 在本程序源码中,算法基于索引表进行细化操作。索引表是一种数据结构,用于记录和查询数据的索引,通常用于快速访问数据。在图像处理中,索引表可以用来快速定位像素点的特定属性或者关联信息,如像素的值、颜色索引等。在此算法中,索引表可能用于存储图像数据的某些特征信息,或是为细化操作提供快速查找的依据。 程序的具体工作流程可能如下: 1. 读取BMP文件:首先需要在VC++环境中打开BMP图像文件。由于BMP是位图格式,每一行像素数据直接对应于磁盘文件中的字节序列,可以通过文件I/O操作读取到内存中。 2. 图像转换为二值图像:由于数据必须使用二值图像数据,算法可能包含了将读入的256色BMP图像转换成二值图像的过程。这通常通过阈值化方法实现,即设定一个阈值,高于阈值的像素设置为黑色,低于阈值的设置为白色。 3. 基于索引表的细化:在二值化后,算法运用索引表对图像进行细化。细化算法通过遍历图像中的每个像素,并根据索引表中的规则或查找表(LUT)来决定哪些像素是必要的,哪些可以被“去除”以达到减少线宽的目的。 4. 输出细化结果:算法执行完所有像素点的处理之后,会输出细化后的图像。该图像将只包含原先线条的中心线,背景仍然是白色。 对于不支持PNG、JPG图像的说明,这可能是由于PNG和JPG格式采用了无损和有损压缩技术,它们的图像数据处理过程与BMP格式不同,引入了复杂的数据结构和压缩算法,这可能超出了本程序算法处理的范畴。 总结以上知识点,本程序源码展示了一种图像处理技术,即通过VC++环境下的编程,读取和处理BMP格式图像,执行基于索引表的细化算法来减少图像中线条的宽度,保留线的核心部分,以达到精简数据的目的。需要注意的是,该程序只适用于256色的BMP图像,并且不支持对常见的PNG、JPG格式进行操作。在实际应用中,这种细化处理可以提高图像识别的效率或为特定图像处理任务提供优化。

相关推荐

filetype
// XiHua.cpp : 定义控制台应用程序的入口点。 // //基于索引表的图像细化算法 //#include "stdafx.h" #include "highgui.h" #include "cv.h"//包含cvCreateStructuringElementEx();颜色空间转换函数cvCvtColor()需要 //#include "cxcore.h" //#pragma comment(lib, "opencv_core249.lib")//opencv_core249.lib取代cv.lib等---(不懂以后再一一对应) //#pragma comment(lib,"opencv_highgui249.lib") //#pragma comment(lib,"opencv_imgproc249.lib") #include <stdlib.h> #include <string.h> //基于索引表的细化细化算法 //功能:对图象进行细化 //参数:lpDIBBits:代表图象的一维数组 // lWidth:图象高度 // lHeight:图象宽度 // 代表图象的一维数组 图象宽度 图象高度 bool ThiningDIBSkeleton(unsigned char* imagedata, int lWidth, int lHeight) { /* unsigned char* imagedata; imagedata = new uchar[sizeof(char) * src->width * src->height](); */ /* deletemark[256]为前人据8领域总结的是否可以被删除的256种情况 不可以删除用0来表示,能被删除的用1来表示 */ unsigned char deletemark[256] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 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, 1, 0, 0, 0, 1, 0, 1, 1, 1, 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, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0 };//索引表 //循环变量 //long i; //long j; unsigned char p0, p1, p2, p3, p4, p5, p6, p7; unsigned char *pmid, *vergeMark; // pmid 用来指向二值图像 vergeMark用来指向存放是否为边缘 unsigned char sum; bool bStart = true; //动态创建一个长度为sizeof(char) * src->width * src->height的数组 //并且初始化把数组元素都设置为0 //长度与imagedata长度一样 long lLength; lLength = lWidth * lHeight; //数组长度 unsigned char *pTemp = new uchar[sizeof(unsigned char) * lLength](); // P0 P1 P2 // P7 P3 // P6 P5 P4 while (bStart) { bStart = false; //每次循环pTemp所有元素都置0 memset(pTemp, 0, lLength);//初始化pTemp指向的lLength空间全部初始化为0 /*---------------首先求边缘点----------------*/ pmid = (unsigned char *)imagedata + lWidth + 1; // pmid 用来指向二值图像矩阵的第二行 +1 表示不考虑图像第一行 vergeMark = (unsigned char *)pTemp + lWidth + 1; //pmidtemp用来指向存放是否为边缘 如果是边缘点 则将其设为1 for (int i = 1; i < lHeight - 1; i++) //lHeight -1表示不考虑图像最后一行 第一列 { for (int j = 1; j < lWidth - 1; j++) //lWidth - 1表示不考虑图像最后一列 { //图像已经是01化,如果是0我们不考虑 if (*pmid == 0) { pmid++; vergeMark++; continue; } //如果是1,是我们考虑的点 我们需要对周围8个进行判断是否边缘 p0 = *(pmid - lWidth - 1); p1 = *(pmid - lWidth); // P0 P1 P2 p2 = *(pmid + 1 - lWidth); // P7 P3 p3 = *(pmid + 1); // P6 P5 P4 p4 = *(pmid + lWidth + 1); p5 = *(pmid + lWidth); p6 = *(pmid + lWidth - 1); p7 = *(pmid - 1); //p0--到---p7的值不是0就是1 sum = p0 & p1 & p2 & p3 & p4 & p5 & p6 & p7;//如果是边缘,肯定周围的P0P1P2P3P4P5P6P7 中一定至少有一个为0 if (sum == 0) { *vergeMark = 1;// 表记边缘 } pmid++; vergeMark++; } pmid++;//跳过图像最后一列,不考虑 vergeMark++; pmid++;//跳过图像第一列,不考虑 vergeMark++; } /*---------------沿着边缘现在开始删除----------------*/ pmid = (unsigned char *)imagedata + lWidth + 1; vergeMark = (unsigned char *)pTemp + lWidth + 1; for (long i = 1; i < lHeight - 1; i++) // 不考虑图像第一行 第一列 最后一行 最后一列 { for (long j = 1; j < lWidth - 1; j++) { //*vergeMark=0表示这个点不是边缘,即不进行删除 if (*vergeMark == 0) { pmid++; vergeMark++; continue; } //如果是1,是我们考虑的点 我们需要对周围8个进行判断 //判断一个点是否能去掉, 要根据它的八个相邻点的情况来判断 p0 = *(pmid - lWidth - 1); p1 = *(pmid - lWidth); // P0 P1 P2 p2 = *(pmid - lWidth + 1 ); // P7 P3 p3 = *(pmid + 1); // P6 P5 P4 p4 = *(pmid + lWidth + 1); p5 = *(pmid + lWidth); p6 = *(pmid + lWidth - 1); p7 = *(pmid - 1); /*根据它的八个相邻点的情况形成的索引表进行删除操作 * * 经过预处理后得到待细化的图像是0、1二值图像。 * 像素值为1的是需要细化的部分,像素值为0的是背景区域。 * * 基于索引表的算法就是"依据一定的判断依据",所做出的一张表, * 因为一个像素的8个邻域,我们可以用8位二进制表示,共有256中可能情况, * 因此,索引表的大小一般为256。 * 根据索引值对应表中的位置存0或1, * 当前像素不能删除存为0, * 可以删除存为1。 * * 然后根据要细化的点的八个邻域的在索引表中情况查询, * 若表中元素是1,则删除该点(改为背景) * 若是0则保留。 * * * ---------------------------------------- * |p7 | p6 | p5 | p4 | p3 | p2 | p1 | p0 |---索引值对应表中的位置-----》共有256中可能情况 * ---------------------------------------- (位置1)0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 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, 1, 0, 0, 0, 1, 0, 1, 1, 1, 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, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0(位置256) */ //此时p0--到---p7的值不是0就是1 p1 *= 2; p2 *= 4; p3 *= 8; p4 *= 16; p5 *= 32; p6 *= 64; p7 *= 128; sum = p0 | p1 | p2 | p3 | p4 | p5 | p6 | p7; // sum = p0 + p1 + p2 + p3 + p4 + p5 + p6 + p7; if (deletemark[sum] == 1)//可以删除 { *pmid = 0; bStart = true; // 表明本次扫描进行了细化 } pmid++; vergeMark++; } pmid++;//跳过图像最后一列,不考虑 vergeMark++; pmid++;//跳过图像第一列,不考虑 vergeMark++; } } //循环结束说明没有可以继续细化的点了,则为细化结束 delete[]pTemp; return true; } int main(int argc, char* argv[]) { //加载原图像 IplImage* src = cvLoadImage("F:\\zhengning\\QtOpencv\\Images\\xihua.PNG", 0); //---------------------------预处理二值化------------------------------------ //对原图像进行二值化 阈值 cvThreshold(src, src, 100, 255, CV_THRESH_BINARY); /* 图像二值化就是将图像上的像素点的灰度值设置为0或255, 也就是将整个图像呈现出明显的黑白效果 CV_THRESH_BINARY 意思为: if src(x,y) > 100 src(x,y)=255 else src(x,y)=-0 原图像由0到255---->0或255 */ cvNamedWindow("s", 0); cvShowImage("s", src); //-----------------------------预处理0/1化------------------------------------- /* 将imagedata指向的与原图像大小一下的数组空间进行0或1赋值 二值图有原来的0或255----->------0或1 */ //动态创建一个长度为sizeof(char) * src->width * src->height的字符数组 //并且初始化把数组元素都设置为0 unsigned char* imagedata; imagedata = new uchar[sizeof(char) * src->width * src->height](); int x, y; for (y = 0; y<src->height; y++) { /* widthStep表示存储一行像素需要的字节数 偏移到第y行 */ unsigned char* ptr = (unsigned char*)(src->imageData + y*src->widthStep); for (x = 0; x<src->width; x++) { /* 判断第y行第x列的元素值是否大于0 大于0时将数组对应的位置元素设置为1 不大于0则设置为0 imagedata数组里面不是0就时1 */ imagedata[y*src->width + x] = ptr[x] > 0 ? 1 : 0; //经过预处理后得到待细化的图像是0、1二值图像。 } } //----------------------------细化----------------------------------------- //经过预处理后得到待细化的图像是0、1二值图像。 //细化一次,和细化多次,由于索引表不变,每次细化效果都一样,想采用多次细化无法消除毛刺 ThiningDIBSkeleton(imagedata, src->width, src->height); //--------------------对细化后的0/1图二值化(0/255)----------------------------- /* 将ptr指向的与原图像大小一下的数组空间进行0或255赋值 */ for (y = 0; y<src->height; y++) { /* widthStep表示存储一行像素需要的字节数 偏移到第y行 */ unsigned char* ptr = (unsigned char*)(src->imageData + y*src->widthStep); for (x = 0; x<src->width; x++) { /* 判断第y行第x列的元素值是否大于0 大于0时将数组对应的位置元素设置为1 不大于0则设置为0 ptr指向的空间的值不是0就是255 也就是细化之后再次变为二值图 */ ptr[x] = imagedata[y*src->width + x]>0 ? 255 : 0; } } //------------------------------------------------------------------------- cvNamedWindow("src", 0); cvShowImage("src", src); cvWaitKey(0); delete[]imagedata; return 0; }
filetype
edisonqkj
  • 粉丝: 0
上传资源 快速赚钱