【openCV】二值图像基础操作

来源:互联网 发布:g s迭代法 c语言 编辑:程序博客网 时间:2024/04/29 18:41

实现了二值图像的Thin,Thicken和提取骨架的操作。

#include "opencv2/opencv.hpp"#define HIT 1#define MISS 0using namespace cv;using namespace std;const int dir[9][2] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,0},{0,1},{1,-1},{1,0},{1,1}};//定义了skeleton和convex hull操作用到的各八个structuring element//structuring element可根据不同需要改变,这里只是一种#define ELE_STR_SIZE 3#define SKELETON_STR_ELE_NUM 8const unsigned char skeleton_str_ele[SKELETON_STR_ELE_NUM][ELE_STR_SIZE][ELE_STR_SIZE] = {{{0,0,0},{2,1,2},{1,1,1}},{{2,0,0},{1,1,0},{2,1,2}},{{1,2,0},{1,1,0},{1,2,0}},{{2,1,2},{1,1,0},{2,0,0}},{{1,1,1},{2,1,2},{0,0,0}},{{2,1,2},{0,1,1},{0,0,2}},{{0,2,1},{0,1,1},{0,2,1}},{{0,0,2},{0,1,1},{2,1,2}}};#define CONVEX_HULL_STR_ELE_NUM 8const unsigned char convex_hull_str_ele[CONVEX_HULL_STR_ELE_NUM][ELE_STR_SIZE][ELE_STR_SIZE] = {{{1,1,2},{1,0,2},{1,2,0}},{{2,1,1},{2,0,1},{0,2,1}},{{1,1,1},{2,0,1},{0,2,2}},{{0,2,2},{2,0,1},{1,1,1}},{{0,2,1},{2,0,1},{2,1,1}},{{1,2,0},{1,0,2},{1,1,2}},{{2,2,0},{1,0,2},{1,1,1}},{{1,1,1},{1,0,2},{2,2,0}}};unsigned char hit_and_miss(unsigned char src[][ELE_STR_SIZE],const unsigned char str_ele[][ELE_STR_SIZE]){//对一块区域的hit_and_miss判定for (int i = 0;i < ELE_STR_SIZE;i++){for (int j = 0;j < ELE_STR_SIZE;j++){if (str_ele[i][j] == 0 || str_ele[i][j] == 1){if (str_ele[i][j] != src[i][j]) return MISS;}}}return HIT;}void hit_and_miss(IplImage *src,IplImage **dst,const unsigned char str_ele[][ELE_STR_SIZE]){//对一个二值图像的hit_and_miss操作unsigned char matrix[ELE_STR_SIZE][ELE_STR_SIZE];int i1,j1,nx,ny;CvSize size = cvGetSize(src);if ((*dst) != NULL) cvReleaseImage(dst); (*dst) = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);cvZero(*dst);for (int i = 0;i<size.height;i++){for (int j = 0;j<size.width;j++){i1 = 0;j1 = 0;for (int k = 0;k < 9;k++){//把自身及相邻共九个格子放入一个矩阵中nx = i + dir[k][0];ny = j + dir[k][1];if (nx<0 || nx>=size.height || ny<0 || ny>=size.width) break;//超出边界,无需判断matrix[i1][j1] = CV_IMAGE_ELEM(src,uchar,nx,ny);++j1;if (j1 == ELE_STR_SIZE){++i1;j1 = 0;}}if (i1 != ELE_STR_SIZE){CV_IMAGE_ELEM(*dst,uchar,i,j) = 0;}else{CV_IMAGE_ELEM(*dst,uchar,i,j) = hit_and_miss(matrix,str_ele);}}} }void biImageSubstract(IplImage *src1,IplImage *src2,IplImage **dst){//二值图像减法操作CvSize size1 = cvGetSize(src1);CvSize size2 = cvGetSize(src2);if ((*dst) != NULL) cvReleaseImage(dst);if ((size1.height != size2.height) || (size1.width != size2.width)){(*dst) = NULL;//大小不同则直接退出return;}//产生一张用于保存结果的空图(*dst) = cvCreateImage(cvGetSize(src1), IPL_DEPTH_8U, 1);cvZero(*dst);//按减法即为交补集的定义计算for (int i = 0;i<size1.height;i++){for (int j = 0;j<size1.width;j++){CV_IMAGE_ELEM(*dst,uchar,i,j) = CV_IMAGE_ELEM(src1,uchar,i,j) & (CV_IMAGE_ELEM(src2,uchar,i,j)^1);}} }void biImageUnion(IplImage *src1,IplImage *src2,IplImage **dst){//二值图像的并操作CvSize size1 = cvGetSize(src1);CvSize size2 = cvGetSize(src2);if ((*dst) != NULL) cvReleaseImage(dst);if ((size1.height != size2.height) || (size1.width != size2.width)){(*dst) = NULL;//大小不同则直接退出return;}//产生一张用于保存结果的空图(*dst) = cvCreateImage(cvGetSize(src1), IPL_DEPTH_8U, 1);cvZero(*dst);for (int i = 0;i<size1.height;i++){for (int j = 0;j<size1.width;j++){CV_IMAGE_ELEM(*dst,uchar,i,j) = CV_IMAGE_ELEM(src1,uchar,i,j) | CV_IMAGE_ELEM(src2,uchar,i,j);}} }bool equals(IplImage *src1,IplImage *src2){//判定两个二值图像是否相同CvSize size1 = cvGetSize(src1);CvSize size2 = cvGetSize(src2);if ((size1.height != size2.height) || (size1.width != size2.width)) return false; //大小不同则直接退出for (int i = 0;i<size1.height;i++){for (int j = 0;j<size1.width;j++){if (CV_IMAGE_ELEM(src1,uchar,i,j)!=CV_IMAGE_ELEM(src2,uchar,i,j)) return false; //判定到不同则直接退出}} return true;}void thicken(IplImage *src,const unsigned char str_ele[][ELE_STR_SIZE],IplImage**dst){//二值图像thicken操作IplImage *hitnmiss = NULL;//产生一张用于保存结果的空图if ((*dst) != NULL) cvReleaseImage(dst);(*dst) = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);cvZero(*dst);//按定义执行thicken操作hit_and_miss(src,&hitnmiss,str_ele);biImageUnion(src,hitnmiss,dst);cvReleaseImage(&hitnmiss);}void thin(IplImage *src,const unsigned char str_ele[][ELE_STR_SIZE],IplImage**dst){//二值图像thin操作IplImage *hitnmiss = NULL;//产生一张用于保存结果的空图if ((*dst) != NULL) cvReleaseImage(dst);(*dst) = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);cvZero(*dst);//按定义执行thin操作hit_and_miss(src,&hitnmiss,str_ele);biImageSubstract(src,hitnmiss,dst);cvReleaseImage(&hitnmiss);}void skeleton(IplImage *src,IplImage **dst){//二值图像提取骨架操作bool flag = true;IplImage *cp_src = NULL;IplImage *tmp = NULL;//拷贝原图cp_src = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);cvCopy(src,cp_src,NULL);//用给出的八个structuring element对原图不断做thin操作直至图片没有变化while (flag){flag = false;for (int i = 0;i < SKELETON_STR_ELE_NUM;i++){thin(cp_src,skeleton_str_ele[i],&tmp);if (!flag && !equals(cp_src,tmp)) flag = true;cvReleaseImage(&cp_src);cp_src = tmp;    tmp = NULL;}}if ((*dst) != NULL) cvReleaseImage(dst);(*dst) = cp_src;}void getBinaryImage(IplImage *src,IplImage **dst){IplImage *grayImage = NULL; // 转为灰度图 grayImage =  cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);  cvCvtColor(src, grayImage, CV_BGR2GRAY);  //创建二值图if ((*dst) != NULL) cvReleaseImage(dst);(*dst) = cvCreateImage(cvGetSize(grayImage), IPL_DEPTH_8U, 1);cvZero(*dst);//清零 cvThreshold(grayImage,*dst,128,1,CV_THRESH_BINARY_INV);cvReleaseImage(&grayImage);}void reverseBinaryImage(IplImage *src){//将一个二值图像反转CvSize size = cvGetSize(src);for (int i = 0;i < size.height;i++){for (int j = 0;j < size.width;j++){CV_IMAGE_ELEM(src,uchar,i,j) ^= 1;}}}void job1(){//提取土豆骨架//以二值图片src为输入,以提取到的skeleton以二值图片输出到dst中。//做法是在每次迭代中不断用八个Structuring Element去对原图做Thin操作,//直到某次迭代中图片不再有变化。IplImage *srcImage = cvLoadImage("potota.jpg", CV_LOAD_IMAGE_UNCHANGED);// 从文件中加载原图  IplImage *binaryImage = NULL; IplImage *skeletonImage = NULL;     if(srcImage == NULL)     {//如果读入图像失败         fprintf(stderr, "Can not load image\n");         return;     } getBinaryImage(srcImage,&binaryImage); reverseBinaryImage(binaryImage);//因为原土豆图黑底白色,将前后反转  skeleton(binaryImage,&skeletonImage);//提取骨架  cvReleaseImage(&binaryImage); //把提取到的skeleton用绿色标注在原图上 CvSize size =cvGetSize(skeletonImage); for (int i = 0;i < size.height;i++){ for (int j = 0;j < size.width;j++){ if (CV_IMAGE_ELEM(skeletonImage,uchar,i,j) == 1){CvScalar s = cvGet2D(srcImage,i,j);s = CV_RGB(0,255,0);cvSet2D(srcImage,i,j,s); } } } cvReleaseImage(&skeletonImage); cvShowImage("Skeleton",srcImage); cvWaitKey(0); cvDestroyWindow("Skeleton");  cvReleaseImage(&srcImage);}void job2(){//做一次thin操作//以二值图片src和结构元素str_ele作为输入,把操作结果以二值图片输出到dst中。//做法是先将原图和结果元素做一次Hit-and-Miss操作,再用原图减去操作结果。unsigned char str_ele[3][3] = {{2,1,2},{1,1,1},{2,1,2}};//操作用到的structuring elementIplImage *srcImage = cvLoadImage("potota.jpg", CV_LOAD_IMAGE_UNCHANGED);// 从文件中加载原图  IplImage *binaryImage = NULL; IplImage *thinImage = NULL;     if(srcImage == NULL)     {//如果读入图像失败         fprintf(stderr, "Can not load image\n");         return;     } getBinaryImage(srcImage,&binaryImage); reverseBinaryImage(binaryImage);//因为原土豆图黑底白色,将前后反转  thin(binaryImage,str_ele,&thinImage);//thin操作  cvReleaseImage(&binaryImage); //把thin操作的结果用绿色标注在原图上 CvSize size =cvGetSize(thinImage); for (int i = 0;i < size.height;i++){ for (int j = 0;j < size.width;j++){ if (CV_IMAGE_ELEM(thinImage,uchar,i,j) == 1){CvScalar s = cvGet2D(srcImage,i,j);s = CV_RGB(0,255,0);cvSet2D(srcImage,i,j,s); } } } cvReleaseImage(&thinImage); cvShowImage("Thin",srcImage); cvWaitKey(0); cvDestroyWindow("Thin");  cvReleaseImage(&srcImage);}void job3(){//做一次thicken操作//以二值图片src和结构元素str_ele作为输入,把操作结果以二值图片输出到dst中。//做法是先将原图和结果元素做一次Hit-and-Miss操作,再用原图并上操作结果。unsigned char str_ele[3][3] = {{2,1,2},{1,1,1},{2,1,2}};//操作用到的structuring elementIplImage *srcImage = cvLoadImage("potota.jpg", CV_LOAD_IMAGE_UNCHANGED);// 从文件中加载原图  IplImage *binaryImage = NULL; IplImage *thickenImage = NULL;     if(srcImage == NULL)     {//如果读入图像失败         fprintf(stderr, "Can not load image\n");         return;     } getBinaryImage(srcImage,&binaryImage); reverseBinaryImage(binaryImage);//因为原土豆图黑底白色,将前后反转  thicken(binaryImage,str_ele,&thickenImage);//thicken操作  cvReleaseImage(&binaryImage); //把thicken操作的结果用绿色标注在原图上 CvSize size =cvGetSize(thickenImage); for (int i = 0;i < size.height;i++){ for (int j = 0;j < size.width;j++){ if (CV_IMAGE_ELEM(thickenImage,uchar,i,j) == 1){CvScalar s = cvGet2D(srcImage,i,j);s = CV_RGB(0,255,0);cvSet2D(srcImage,i,j,s); } } } cvReleaseImage(&thickenImage); cvShowImage("Thicken",srcImage); cvWaitKey(0); cvDestroyWindow("Thicken");  cvReleaseImage(&srcImage);}int main(){job1();    job2();job3();return 0;}


原图:


Skeleton效果:


Thin效果:


Thicken效果:


Thin和Thick使用的element::


提取skeleton使用的elemeng(每四个方向只画一个):



相关操作的概念见:http://homepages.inf.ed.ac.uk/rbf/HIPR2/morops.htm


0 0