基于opencv的数学形态学的操作,涉及数学形态学细化、击中击不中变换


#include"cmath"

#include"ctime"
#include"iostream"
#include
#include"cstring"
using namespace std;


#define HIT 1  
#define MISS 0  


using 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 8  
const 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 8  
const 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 for (int j = 0; 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 for (int j = 0; 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 for (int j = 0; 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 for (int j = 0; 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("E:\\二值像素图像保存\\8_3\\aa\\(2)_3_取.bmp", CV_LOAD_IMAGE_UNCHANGED);// 从文件中加载原图   

//======
int height1 = srcImage->height;
int width1 = srcImage->width;
CvSize cvSize;
cvSize.height = height1;
cvSize.width = width1;
IplImage *srcImage2 = cvCreateImage(cvSize,IPL_DEPTH_8U,3);
//==============================================================
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(srcImage2, i, j);
s = CV_RGB(0, 255, 0);
cvSet2D(srcImage2, i, j, s);
}
}
}


cvReleaseImage(&skeletonImage);
cvReleaseImage(&srcImage);
cvShowImage("Skeleton", srcImage2);
cvSaveImage("E:\\二值像素图像保存\\8_3\\aa\\11_17\\(1)_1_lenna.bmp", srcImage2);
cvWaitKey(0);
cvDestroyWindow("Skeleton");


cvReleaseImage(&srcImage2);

}


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 element  
IplImage *srcImage = cvLoadImage("E:\\二值像素图像保存\\8_3\\aa\\(2)_3_取.bmp", CV_LOAD_IMAGE_UNCHANGED);// 从文件中加载原图    
int height1 = srcImage->height;
int width1 = srcImage->width;
CvSize cvSize;
cvSize.height = height1;
cvSize.width = width1;
IplImage *srcImage2 = cvCreateImage(cvSize, IPL_DEPTH_8U, 3);


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);
cvReleaseImage(&srcImage);


//把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(srcImage2, i, j);
s = CV_RGB(0, 255, 0);
cvSet2D(srcImage2, i, j, s);
}
}
}


cvReleaseImage(&thinImage);


cvShowImage("Thin", srcImage2);
cvSaveImage("E:\\二值像素图像保存\\8_3\\aa\\(2)_1_lenna.bmp", srcImage2);
cvWaitKey(0);
cvDestroyWindow("Thin");


cvReleaseImage(&srcImage2);
}


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 element  
IplImage *srcImage = cvLoadImage("E:\\二值像素图像保存\\8_3\\aa\\(2)_3_取.bmp", CV_LOAD_IMAGE_UNCHANGED);// 从文件中加载原图    

int height1 = srcImage->height;
int width1 = srcImage->width;
CvSize cvSize;
cvSize.height = height1;
cvSize.width = width1;
IplImage *srcImage2 = cvCreateImage(cvSize, IPL_DEPTH_8U, 3);

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);


cvReleaseImage(&srcImage);
//把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(srcImage2, i, j);
s = CV_RGB(0, 255, 0);
cvSet2D(srcImage2, i, j, s);
}
}
}


cvReleaseImage(&thickenImage);


cvShowImage("Thicken", srcImage2);
cvSaveImage("E:\\二值像素图像保存\\8_3\\aa\\(3)_1_lenna.bmp", srcImage2);
cvWaitKey(0);
cvDestroyWindow("Thicken");


cvReleaseImage(&srcImage2);


}


int main()
{
job1();
job2();
job3();
return 0;

}


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

你可能感兴趣的:(opencv,图像处理,vs2015)