【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
- 【openCV】二值图像基础操作
- opencv对二值图像进行颜色反色操作
- OpenCV学习笔记一:图像基础操作
- 【opencv】二值图像细化
- 【OpenCV图像处理】二、图像的二值化操作
- OPENCV图像处理基础(二)感兴趣区域ROI
- OpenCV(二)如何对图像的像素进行操作
- opencv入门笔记之二 操作图像像素点
- OpenCV 学习笔记 ----图像的基本操作(二)
- opencv学习(二十八)之基本图像阈值操作threshold
- opencv 学习第二天 图片图像的基础操作
- opencv基础操作及图像的卷积与滤波
- OpenCV学习笔记6-图像的基础操作
- OpenCV-Python——图像的基础操作
- Python-OpenCV 处理图像(二)(三):滤镜和图像运算 图像像素点操作
- opencv实现二值图像的细化
- Opencv获取二值图像轮廓
- OpenCV计算二值图像周长面积
- Struts2.x 学习笔记 之 通过超链接动态加载国际化资源文件
- ELM极限学习机源码
- java socket网络编程
- javascript中的几个假值
- OC基础(五)
- 【openCV】二值图像基础操作
- wxWidgets动态显示控件的注意点
- C++虚函数的缺点
- 浮点数表示方法
- test 3 Problem A: [noip2016十连测第三场]平均数 (实数二分+排序+树状数组)
- 动态添加 v4 fragment
- iOS地图导航的那些坑
- 剑指offer(一)
- php 扩展开发(一)