第一步:读取图片
Mat img = imread("../img/cards.png", CV_LOAD_IMAGE_COLOR);
if(img.empty())
return -1;
namedWindow( "input", CV_WINDOW_AUTOSIZE );
imshow("input", img);
所读取的图片如下:
第二步:将图片的白色的背景变为黑色的背景
for(int i = 0; i < img.rows; i++){
for(int j = 0; j < img.cols; j++){
if(img.at(i,j) == Vec3b(255,255,255)){
img.at(i,j)[0] = 0;
img.at(i,j)[1] = 0;
img.at(i,j)[2] = 0;
}
}
}
结果如下:
第四步:为了使图像的边界更加清晰,使用filter2d和拉普拉斯算子来得到图像的边界
Mat filter2d_img;
Mat kernel = (Mat_(3,3) << 1,1,1,1,-8,1,1,1,1);
filter2D(img,filter2d_img,CV_32F,kernel);
filter2d_img.convertTo(filter2d_img,CV_8UC3);
imshow("filter2d_img",filter2d_img);
filter2d_img图片如下:
第五步:将源图像减去filter_2d的图像可以得到边界更加清晰的图像
Mat result_img;
result_img = img - filter2d_img;
imshow("result_img",result_img);
第六步:将图像转换为二值化图像
首先将图像转化为灰度图像,然后对图像使用自适应阈值变为二值化图像
Mat gray;
cvtColor(result_img,gray,CV_BGR2GRAY);
imshow("gray",gray);
// 我们使用自适应阈值,将图片变为黑白图像
Mat binary_img;
threshold(gray,binary_img,0,255,THRESH_BINARY | THRESH_OTSU);
imshow("binary",binary_img);
二值化图像的结果:
第七步:确定背景区域
对图片进行膨胀,膨胀会消除黑色的噪音。膨胀后仍为黑色的区域,即为背景区域。
//确定图像的背景区域
Mat sure_bg;
Mat kernel1 = getStructuringElement(MORPH_RECT,Size(9,9),Point(-1,-1));
dilate(binary_img,sure_bg,kernel1,Point(-1,-1),3);
sure_bg.convertTo(sure_bg,CV_8UC1);
imwrite("sure_bg.jpg",sure_bg);
背景区域的图片如下所示:
第八步:确定前景区域
首先对二值图像进行距离变换,距离大于0.4的均认为是前景图像
//确定前景区域
Mat sure_fg;
//距离变换:计算源像素的每个像素到最近的零像素的距离
Mat dist_img;
distanceTransform(binary_img,dist_img,DIST_L1,3,5);
//我们将距离变换的图像进行归一化
normalize(dist_img,dist_img,0,1,NORM_MINMAX);
//距离大于0.4的皆为前景
threshold(dist_img,sure_fg,0.4,1,THRESH_BINARY);
normalize(sure_fg,sure_fg,0,255,NORM_MINMAX);
sure_fg.convertTo(sure_fg,CV_8UC1);
imwrite("sure_fg.jpg",sure_fg);
前景区域的图片如下
第九步:背景区域减去前景区域,即我们不确定的区域,该区域中包含了图像分割的边界点
Mat unknown;
unknown = sure_bg-sure_fg;
imwrite("unknown.jpg",unknown);
第十步:我们对这些区域进行标记,其中不确定区域标记为0,背景区域标记为1。前景区域借助于connectedComponents函数进行标记,该函数会将前景区域从1开始进行标记
我们对前景区域从1开始标记
//前景对象标记是从1开始的整数
Mat marker1;
connectedComponents(sure_fg,marker1);
然后我们将标记的marker1每个值都加上1。相当于前景区域从2开始标记,除了前景区域的其他区域的值均为1。
Mat markers = Mat::ones(marker1.size(),marker1.type());
markers = marker1 +markers;
markers.convertTo(markers,CV_8UC1);
我们将未知区域标记为0
//我们将图像中的未知区域标记为0
for(int row = 0; row < unknown.rows; row++){
for(int col = 0; col < unknown.cols; col++){
uchar c = unknown.at(row,col);
if(c == 255)
markers.at(row,col) = 0;
}
}
normalize(markers,markers,0,255,NORM_MINMAX);
imwrite("markers.jpg",markers);
//实施分水岭算法,标签图像将会被修改便捷区域的标记为-1
markers.convertTo(markers,CV_32SC1);
watershed(img,markers);
Mat mark = Mat::zeros(markers.size(),CV_8UC1);
markers.convertTo(mark,CV_8UC1);
// bitwise_not(mark,mark);
imshow("water_img",mark);
实施分水岭算法之后mark结果如下所示:
最后一步:根据mark,对图片进行着色
vector pixes;
vector colors;
RNG rng;
Mat final_rst(mark.size(),CV_8UC3);
for(int i = 0; i< markers.rows; i++){
for(int j = 0;j< markers.cols;j++){
uchar c = mark.at(i,j);
bool target = false;
for(int k = 0; k(i,j) = colors[k];
target = true;
break;
}
}
if(target == false){
pixes.push_back(c);
int r = rng.uniform(0,255);
int g = rng.uniform(0,255);
int b = rng.uniform(0,255);
Vec3b color = Vec3b((uchar)b,(uchar)g,(uchar)r);
final_rst.at(i,j) = color;
colors.push_back(color);
}
}
}