本节主要涉及到图像的领域、算术操作以及如何操作图像感兴趣的区域。
一:邻域操作
以下例子主要对图像进行锐化。基于拉普拉斯算子<后面讨论>。这幅图像的边缘部分将得到放大,细节部分将更加的锐利。计算方式为:
sharpened_pixel = 5*current – left – right –up – down.
Code:
#include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <opencv2\core\core.hpp> #include <iostream> using namespace std; using namespace cv; void sharpen(const Mat &image, Mat &result) { const int nChannels = image.channels(); for(int j = 1; j < image.rows - 1; j++) { const uchar *previous = image.ptr<const uchar>(j-1); // 上一行 const uchar *current = image.ptr<const uchar>(j); // 此行 const uchar *next = image.ptr<const uchar>(j+1); //下一行 uchar *output = result.ptr<uchar>(j); for(int i = 1; i < image.cols - 1; i++) { if(image.channels() == 1) // 黑白图像 { output[i] = saturate_cast<uchar>(5*current[i] - current[i-1] - current[i+1] - previous[i] - next[i]); } else if(image.channels() == 3) // 彩色图像 { *(output + i*image.elemSize()) =saturate_cast<uchar>(5 * *(current + i*image.elemSize()) - *(current + (i-1)*image.elemSize())- *(current + (i+1)*image.elemSize()) - *(previous + i*image.elemSize())- *(next + i*image.elemSize())); *(output + i*image.elemSize() + 1) = saturate_cast<uchar>(5 * *(current + i*image.elemSize() + 1) - *(current + (i-1)*image.elemSize() + 1)- *(current + (i+1)*image.elemSize() + 1) - *(previous + i*image.elemSize() + 1)- *(next + i*image.elemSize() + 1)); *(output + i*image.elemSize() + 2) = saturate_cast<uchar>(5 * *(current + i*image.elemSize() + 2) - *(current + (i-1)*image.elemSize() + 2)- *(current + (i+1)*image.elemSize() + 2) - *(previous + i*image.elemSize() + 2)- *(next + i*image.elemSize() + 2)); } } /*for(int i = nChannels; i < nChannels * (image.cols - 1); ++i) { *output ++ = saturate_cast<uchar>(5*current[i] - current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]); }*/ } result.row(0).setTo(Scalar(0)); result.row(result.rows - 1).setTo(Scalar(0)); result.col(0).setTo(Scalar(0,0,0)); result.col(result.cols - 1).setTo(Scalar(0)); return; } void sharpen2D(const Mat &image, Mat &result) { Mat kernel(3, 3, CV_32FC1, Scalar(0)); kernel.at<float>(1,1) = 5.0; kernel.at<float>(0, 1) = -1.0; kernel.at<float>(1, 0) = -1.0; kernel.at<float>(1, 2) = -1.0; kernel.at<float>(2,1) = -1.0; /*也可以这样定义,这是opencv中简单初始化只有几个像素的mat类型*/ //Mat kernel = (Mat_<float>(3,3) << 0, -1.0, 0.0, -1.0, 5.0, -1.0, 0, -1.0, 0); filter2D(image, result,image.depth(), kernel); return; } int main() { Mat image = imread("F:\\huangrong.jpg", 0); Mat result; cout << image.depth() << endl; result.create(Size(image.cols, image.rows), image.type()); sharpen(image, result); //sharpen2D(image, result); namedWindow("image"); imshow("image", image); namedWindow("result"); imshow("result", result); waitKey(0); return 0; }
Explaination:
(1) saturate_cast<uchar>主要是类型检查来保证转换的安全性。如对于计算得到的结果不在0~255范围内,则进行截断。通常做法是将负值截断为0,将大于255的截断为255.
(2) Matkernel = (Mat_<float>(3,3) << 0, -1.0, 0.0, -1.0, 5.0, -1.0, 0,-1.0, 0);
//opencv中简单初始化只有几个像素的mat类型
(3) result.row(0).setTo(Scalar(0));
//可以将矩阵的第0行所有像素全部设置为0
(4)opencv中自带了filter2D这个函数可以进行领域操作,其结果是一样的,在某些情况下回更高效点。
Result:
二:算术操作
Code:
#include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <iostream> using namespace std; using namespace cv; Mat addWeight(Mat &image, Mat &image2) { Mat result; //addWeighted(image, 0.5, image2, 0.5, 0.0, result); result = image*0.5 + image2*0.5 + 0.3; // 大多数算术函数在opencv2中都有对应的重载操作符 return result; } Mat addWeight2(Mat &image, Mat &image3) { Mat result; vector<Mat> planes; split(image, planes); /// 将一个彩色三通道图像分解为三个单通道图像 planes[0] += image3; merge(planes, result); /// 将三个单通道图像合并为一个彩色三通道图像 return result; } int main() { Mat image = imread("F:\\huangrong.jpg", 1); if(!image.data){ cout << "fail to load image" << endl; return 0; } Mat image2 =Mat::zeros(image.rows, image.cols, image.type()); //Mat image2(image.rows, image.cols, image.type(), Scalar(255, 255, 255)); Mat result; result = addWeight(image, image2); Mat result2; Mat image3 = Mat::zeros(image.rows, image.cols, CV_8UC1); result2 = addWeight2(image, image3); namedWindow("image"); imshow("image", image); namedWindow("image2"); imshow("image2", image2); namedWindow("result"); imshow("result", result); namedWindow("result2"); imshow("result2", result2); waitKey(0); return 0; }
Explaination:
(1) 以上实现的是两幅图像相加,即可用opencv自带的函数addWeighted,也可用重载的运算符+。其它如&,|,^,~等都被重载了。除了加法,还有其它运算,如矩阵求逆m1.inv(),装置m1.t(),矩阵行列式m1.determinant()等
(2) split(image, planes);和merger(planes,image); 分别是将一个彩色三通道图像分解为三个单通道图像和将三个单通道图像合并为一个彩色三通道图像
Result:
三:定义感兴趣区域
Code:
#include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\imgproc\imgproc.hpp> #include <iostream> using namespace std; using namespace cv; int main() { Mat image = imread("F:\\huangrong.jpg", 1); if(!image.data){ cout << "fail to load image" << endl; return 0; } Mat tongtong = imread("F:\\tt.png", 1); //Mat imageROI = image(Rect(140,20,tongtong.cols, tongtong.rows)); //Mat imageROI(image, Rect(140,20,tongtong.cols, tongtong.rows));//第二种方式 Mat imageROI = image(Range(20, 20 + tongtong.rows),Range(140, 140+tongtong.cols)); // 第三种方式 tongtong.copyTo(imageROI); namedWindow("imageROI"); imshow("imageROI", imageROI); namedWindow("image"); imshow("image",image); waitKey(0); return 0; }
Explaination:
(1) 三种方式:
a) MatimageROI = image(Rect(140,20,tongtong.cols, tongtong.rows));
b) MatimageROI(image, Rect(140,20,tongtong.cols, tongtong.rows));
c) MatimageROI = image(Range(20, 20 + tongtong.rows),Range(140, 140+tongtong.cols)); // 其中Range是指从起始索引到终止索引(不包含终止索引)的一段连续序列。
(2)其中需要注意的是:ROI和原始图像共享数据缓冲区,对ROI的任何变换都会影响到图像对应的区域。
作者:小村长 出处:http://blog.csdn.net/lu597203933 欢迎转载或分享,但请务必声明文章出处。 (新浪微博:小村长zack, 欢迎交流!)