原理
- OpenCV官网教程
- 通过使用不同形状的核,得到不同的结果
- 提取横线:
经过先腐蚀,再膨胀,得到直线。
- 提取竖线:
经过先腐蚀,再膨胀,得到竖线。
代码
#include <opencv2/opencv.hpp>
#include <iostream>
using namespace cv;
using namespace std;
void show_wait_destroy(const char* winname, cv::Mat image)
{
static uchar i=0;
imshow(winname,image);
cv::moveWindow(winname,0,(++i)*(200));
}
int main(void)
{
Mat src = imread("../res/src.png");
if(src.empty())
{
cout << "load the image failed" << endl;
}
Mat gray;
if(src.channels() == 3)
{
cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);
}
else
{
gray = src;
}
//转化成二值图像
Mat bw;
cv::adaptiveThreshold(~gray,bw,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,15,-2);
show_wait_destroy("binary",bw);
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
//提取横线
int horizontal_size=horizontal.cols/30;
Mat horizontalStructure = cv::getStructuringElement(cv::MORPH_RECT,Size(horizontal_size,1));
cv::erode(horizontal,horizontal,horizontalStructure);
cv::dilate(horizontal,horizontal,horizontalStructure);
show_wait_destroy("horizontal",horizontal);
//提取竖线
int vertical_size = vertical.rows/30;
Mat verticalStructure = getStructuringElement(MORPH_RECT,Size(1,vertical_size));
erode(vertical,vertical,verticalStructure);
dilate(vertical,vertical,verticalStructure);
bitwise_not(vertical,vertical);
show_wait_destroy("vertical",vertical);
//提取边界
Mat edges;
cv::adaptiveThreshold(vertical,edges,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,3,-2);
show_wait_destroy("vertical_1",edges);
//膨胀边界
Mat kernel = Mat::ones(2,2,CV_8UC1);
dilate(edges,edges,kernel);
show_wait_destroy("vertical_2",edges);
//平滑vertical
Mat smooth;
vertical.copyTo(smooth);
blur(smooth,smooth,Size(2,2));
//将平滑之后的边界,覆盖到vertical,实现对vertical平滑边界的效果
smooth.copyTo(vertical,edges);
show_wait_destroy("final",vertical);
waitKey(0);
return 0;
};
原图
结果:
horizontal:
vertical:
源码分析:
Mat src = imread("../res/src.png");
if(src.empty())
{
cout << "load the image failed" << endl;
}
Mat gray;
if(src.channels() == 3)
{
cv::cvtColor(src,gray,cv::COLOR_BGR2GRAY);
}
else
{
gray = src;
}
imshow("src",src);
读取原图,再转化成灰度图像
Mat bw;
cv::adaptiveThreshold(~gray,bw,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,15,-2);
show_wait_destroy("binary",bw);
将灰度图像转成二值图像,使用的是adaptiveThreshold函数,各个参数的综合意义:如果原图锚点的的值大于领域(核的边长15)的平均值,则取255,否则取0。
Mat horizontal = bw.clone();
Mat vertical = bw.clone();
int horizontal_size=horizontal.cols/30;
Mat horizontalStructure = cv::getStructuringElement(cv::MORPH_RECT,Size(horizontal_size,1));
cv::erode(horizontal,horizontal,horizontalStructure);
cv::dilate(horizontal,horizontal,horizontalStructure);
show_wait_destroy("horizontal",horizontal);
创建一个核大小:(1, cols/30), 用其进行对原图先腐蚀再膨胀,得到结果:
int vertical_size = vertical.rows/30;
Mat verticalStructure = getStructuringElement(MORPH_RECT,Size(1,vertical_size));
erode(vertical,vertical,verticalStructure);
dilate(vertical,vertical,verticalStructure);
bitwise_not(vertical,vertical);
show_wait_destroy("vertical",vertical);
创建一个核大小:(vertical.rows/30 , 1), 用其进行对原图先腐蚀再膨胀,然后取反得到结果:
Mat edges;
cv::adaptiveThreshold(vertical,edges,255,cv::ADAPTIVE_THRESH_MEAN_C,cv::THRESH_BINARY,3,-2);
show_wait_destroy("vertical_1",edges);
因为领域大小选择了3,当锚点在白色区域的时候,src=领域均值的,所以赋值为0,所以只有在黑白交接处 src>均值,赋值为1,最后得到的结果就是提取边界。
Mat kernel = Mat::ones(2,2,CV_8UC1);
dilate(edges,edges,kernel);
show_wait_destroy("vertical_2",edges);
对边界进行膨胀
Mat smooth;
vertical.copyTo(smooth);
blur(smooth,smooth,Size(2,2));
smooth.copyTo(vertical,edges);
show_wait_destroy("final",vertical);
先将原始的vertical 进行均值平滑,再将结果以edges为mask,复制到vertical。(等价于只将平滑后的边界覆盖到vertical,实现平滑边界的效果)
官方API
(1)
- void cv::adaptiveThreshold(
InputArray src, // 输入图像
OutputArray dst, //输出图像
double maxValue, //对于二值图像转化的目标值
int adaptiveMethod, //自适应阈值算法
int thresholdType, //阈值的类型
int blockSize, //用来计算像素阈值的领域的大小(3,5,7等)
double C //偏移常数C,最终得到的阈值-C。
)
参数 adaptiveMethod:自适应阈值算法
算法 |
---|
ADAPTIVE_THRESH_MEAN_C |
ADAPTIVE_THRESH_GAUSSIAN_C |
参数 thresholdType:阈值类型
常用类型 |
---|
THRESH_BINARY |
THRESH_BINARY_INV |
(2)
- void cv::Mat::copyTo (
OutputArray m, //目的图像
InputArray mask //和this大小一样的掩膜,掩膜不为0的区域将this相应的区域复制到目的图像中,为0区域:目的图像还是原来的值
) const