学校把很多考试都放在暑假考了,我们专业有6科,分布在一个月内。又要备赛数学建模,快乐暑假已经被榨干了... ...
所以只能利用碎片时间更一篇上次给大家介绍的《视觉图像处理100问》了,因为有原作者写好的代码,所以比较节省时间。关于这个具体资料看上篇文章:
【资源分享1】日本同行整理的视觉处理100问
最近在筹划一篇详解分水岭算法的文章,大家等等吧~
问题一:通道交换
这道题如果用opencv的cvtColor函数写很简单,cvtColor函数可以在绝大部分格式之间转换,具体见下图(截自毛星云《opencv3编程入门》):
我们基于像素操作,自定义一个函数channel_swap()来实现BGR->RGB转换的功能:
代码语言:javascript复制// 【1】通道转换
cv::Mat channel_swap(cv::Mat img) {
// get height and width
int width = img.cols;
int height = img.rows;
// prepare output
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC3);
// each y, x
for (int y = 0; y < height; y ) {
for (int x = 0; x < width; x ) {
// R -> B
out.at<cv::Vec3b>(y, x)[0] = img.at<cv::Vec3b>(y, x)[2];
// B -> R
out.at<cv::Vec3b>(y, x)[2] = img.at<cv::Vec3b>(y, x)[0];
// G -> G
out.at<cv::Vec3b>(y, x)[1] = img.at<cv::Vec3b>(y, x)[1];
}
}
return out;
}
程序很简单,就是逐像素主通道进行变换就可以了。
对比opencv的API:cvtColor和自定义函数的运行效果:
问题二:图像转灰度图
RGB转灰度图就是根据上图公式,同样可以根据像素操作来实现:
代码语言:javascript复制//【2】BGR -> Gray
cv::Mat BGR2GRAY(cv::Mat img) {
// get height and width
int width = img.cols;
int height = img.rows;
// prepare output
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);
// each y, x
for (int y = 0; y < height; y ) {
for (int x = 0; x < width; x ) {
// BGR -> Gray
out.at<uchar>(y, x) = 0.2126 * (float)img.at<cv::Vec3b>(y, x)[2]
0.7152 * (float)img.at<cv::Vec3b>(y, x)[1]
0.0722 * (float)img.at<cv::Vec3b>(y, x)[0];
}
}
return out;
}
问题三:图像二值化(THresholding)
二值化原理很简单了,利用像素操作加if判断就可以实现:
代码语言:javascript复制// Gray -> Binary
cv::Mat Binarize(cv::Mat gray, int th){
int width = gray.cols;
int height = gray.rows;
// prepare output
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);
// each y, x
for (int y = 0; y < height; y ){
for (int x = 0; x < width; x ){
// Binarize
if (gray.at<uchar>(y, x) > th){
out.at<uchar>(y, x) = 255;
} else {
out.at<uchar>(y, x) = 0;
}
}
}
return out;
}
下面对比一下opencv的Threshold()函数和我们自定义函数Binarize()函数:
代码语言:javascript复制int main()
{
Mat srcImage, grayImage,dstImage;
srcImage = imread("御坂美琴/1.png");//读取图像
imshow("原图BGR", srcImage);
cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);//转灰度
threshold(grayImage, dstImage, 125, 255, THRESH_BINARY);
imshow("threshold函数阈值化", dstImage);
Mat out = Binarize(grayImage,125);
imshow("自定义函数阈值化", out);//自定义函数
waitKey();
return 0;
}
问题四:大津二值化算法(Otsu's Method)
代码语言:javascript复制// Gray -> Binary 输入图像为灰度图
cv::Mat Binarize_Otsu(cv::Mat gray){
int width = gray.cols;
int height = gray.rows;
// determine threshold
double w0 = 0, w1 = 0; //被阈值 t分开的两个类中的像素数占总像素数的比率(满足相加等于1 )
double m0 = 0, m1 = 0; //是这两个类的像素值的平均值
double max_sb = 0, sb = 0;
int th = 0;
int val;
// 遍历阈值t的可能值寻求最大值
for (int t = 0; t < 255; t ){
w0 = 0;
w1 = 0;
m0 = 0;
m1 = 0;
for (int y = 0; y < height; y ){ //遍历图像像素点
for (int x = 0; x < width; x ){
val = (int)(gray.at<uchar>(y, x)); //获取该点的灰度值
if (val < t){
w0 ;
m0 = val;
} else {
w1 ;
m1 = val;
}
}
}
m0 /= w0;//计算大于,小于阈值t的两类像素的像素值的平均值m0 ,m1
m1 /= w1;
w0 /= (height * width);//计算大于,小于阈值t的两类像素的个数占总个数的比例w0,w1
w1 /= (height * width);
sb = w0 * w1 * pow((m0 - m1), 2); //计算sb的平方
if(sb > max_sb){ //更新最大值
max_sb = sb;
th = t;
}
}
std::cout << "threshold:" << th << std::endl;//打印阈值
//获取阈值后,对图像进行阈值化
// prepare output
cv::Mat out = cv::Mat::zeros(height, width, CV_8UC1);
// each y, x
for (int y = 0; y < height; y ){
for (int x = 0; x < width; x ){
// Binarize
if (gray.at<uchar>(y, x) > th){
out.at<uchar>(y, x) = 255;
} else {
out.at<uchar>(y, x) = 0;
}
}
}
return out;
}
处理效果: