先看代码:
<span style="font-size:24px;">#include <opencv2\core\core.hpp> #include <opencv2\highgui\highgui.hpp> #include <opencv2\opencv.hpp> #include <iostream> using namespace cv; using namespace std; typedef unsigned char byte; void gradientGray(Mat &src, Mat &mag); int main(){ Mat src = imread("1.jpg", 0);//以灰度形式读取图片 Mat dst; gradientGray(src, dst); imwrite("2.jpg",dst); //保存文件,梯度图 imshow("src", src); imshow("dst", dst); waitKey(0); } void gradientGray(Mat &src, Mat &mag) { const int H = src.rows, W = src.cols; Mat Ix(H, W, CV_32S), Iy(H, W, CV_32S); //因为计算出的梯度值可能有正有负,且值也可能会很大,故数据类型为整形 // 求水平方向梯度,处理左右边缘像素 for (int y = 0; y < H; y++){ Ix.at<int>(y, 0) = abs(src.at<byte>(y, 1) - src.at<byte>(y, 0)) * 2; for (int x = 1; x < W - 1; x++) Ix.at<int>(y, x) = abs(src.at<byte>(y, x + 1) - src.at<byte>(y, x - 1)); Ix.at<int>(y, W - 1) = abs(src.at<byte>(y, W - 1) - src.at<byte>(y, W - 2))*2; } // 求垂直方向梯度,处理左右边缘像素 for (int x = 0; x < W; x++) { Iy.at<int>(0, x) = abs(src.at<byte>(1, x) - src.at<byte>(0, x)) * 2; for (int y = 1; y < H - 1; y++) Iy.at<int>(y, x) = abs(src.at<byte>(y + 1, x) - src.at<byte>(y - 1, x)); Iy.at<int>(H - 1, x) = abs(src.at<byte>(H - 1, x) - src.at<byte>(H - 2, x)) * 2; } /*for (int j = 0; j < H; j++) for (int k = 0; k < W; k++) { mag.at<byte>(j, k) = min(Ix.at<int>(j,k) + Iy.at<int>(j, k), 255); }*/ convertScaleAbs(min(Ix + Iy, 255), mag); //这句话和上面的for循环是同样的功能 } </span>求解梯度图,首先要注意数据的转换。因为本身读取的图像是8位的,其灰度值范围为0~255,而在求梯度的过程中,会出现梯度值大于255或者小于0的情况,因此在梯度图所存储的数据应该是以有符号整型的形式,即CV_32S。
另外,我采用的梯度求解方法为,当前像素的后一个像素减前一个像素的差值,作为当前像素的当前方向的梯度值。因为梯度本质上是求微分,因此对与图像,其存在两个方向上的梯度,即水平和垂直方向。这也是程序中求了两次灰度的原因。
在求了两个方向的梯度之后,再如何求出当前像素的梯度值,其方法也是很多的。我采用的是将水平和垂直梯度的绝对值相加,也可以采用平方和再开根号的方法,或者直接取二者最大值都行。
另外,解释一下,最后的那个类型转换函数,其作用是将当前数据类型的图像转换为unsigned char型,另外,在opencv中,其重载了很多算术运算符,比如这个函数里面的Ix+Iy就是代表对应位相加。
下面给出原图和效果图:
原图:
效果图:
是不是比较像素描~~~~
原文地址:http://blog.csdn.net/autocyz/article/details/43193123