#include"cmath"
#include"ctime"#include"iostream"
#include<opencv2\opencv.hpp>
#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<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("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