2016-11-25 99 views
3

有Mat是灰度像素的正方形区域。如何创建一个直线,其方向是垂直于大多数像素值的方向而改变方向(平均梯度,整个Mat上的平均值,结果将只是一个方向(然后可以绘制为一条直线))?如何检测强度梯度方向

例如,具有

enter image description here

它看起来像

enter image description here

一个怎么能做出这种事情的OpenCV(在Python或C++)?

+0

听起来就像平均梯度... – Julien

+0

我看到的图片几乎是均匀的象素。你是什​​么意思? –

+1

请定义“垂直于像素值”。 – Ripi2

回答

6

的OpenCV的实现看起来像下面这样。它以与Mark Setchell的解答相似的方式解决了这个问题,除了规范化图像对结果方向没有任何影响。

Mat img = imread("img.png", IMREAD_GRAYSCALE); 

// compute the image derivatives for both the x and y direction 
Mat dx, dy; 
Sobel(img, dx, CV_32F, 1, 0); 
Sobel(img, dy, CV_32F, 0, 1); 

Scalar average_dx = mean(dx); 
Scalar average_dy = mean(dy); 

double average_gradient = atan2(-average_dy[0], average_dx[0]); 
cout << "average_gradient = " << average_gradient << endl; 

,并显示所得到的方向

Point center = Point(img.cols/2, img.rows/2); 
Point direction = Point(cos(average_gradient) * 100, -sin(average_gradient) * 100); 

Mat img_rgb = imread("img.png"); // read the image in colour 
line(img_rgb, center, center + direction, Scalar(0,0,255)); 
imshow("image", img_rgb); 
waitKey(); 

image direction

+2

不错 - 做得好!我同意正常化 - 我主要是为了可视化而做的,因为对原始问题的一些评论意味着人们无法看到OP在寻找什么。 –

4

我不能轻易告诉你如何与OpenCV的做到这一点,但我可以告诉你的方法,用ImageMagick的只要在命令行演示。

首先,我认为你需要将图像转换为灰度图像,并将其归到全方位的黑到白的 - 这样的:

convert gradient.png -colorspace gray -normalize stage1.png 

enter image description here

然后,你需要计算X -gradient并使用Sobel滤波器,然后采取逆黄褐色的Y梯度的X梯度的图像的Y梯度:

convert stage1.png -define convolve:scale='50%!' -bias 50% \ 
    \(-clone 0 -morphology Convolve Sobel:0 \) \ 
    \(-clone 0 -morphology Convolve Sobel:90 \) \ 
    -fx '0.5+atan2(v-0.5,0.5-u)/pi/2' result.jpg 

其次,平均值result.jpg中的像素值是您线条的方向。

你可以看到在卷积X和Y梯度这样使用的系数:

convert xc: -define morphology:showkernel=1 -morphology Convolve Sobel:0 null: 

Kernel "Sobel" of size 3x3+1+1 with values from -2 to 2 
Forming a output range from -4 to 4 (Zero-Summing) 
0:   1   0  -1 
1:   2   0  -2 
2:   1   0  -1 


convert xc: -define morphology:showkernel=1 -morphology Convolve Sobel:90 null: 
Kernel "[email protected]" of size 3x3+1+1 with values from -2 to 2 
Forming a output range from -4 to 4 (Zero-Summing) 
0:   1   2   1 
1:   0   0   0 
2:  -1  -2  -1 

参见维基百科here - 特别是这一行:

enter image description here

2

将图像转换为灰度和基于所述灰度级分类其像素。对于分类,你可以使用类似Otsu方法或kmeans与2个集群。然后采取形态梯度来检测边界。

这是使用Otsu方法的分类像素和边界。

otsu boundaty

现在找到边界图像的非零像素和拟合2D线使用fitLine函数找到的加权最小二乘线或使用this RANSAC执行的那些像素。 fitLine给出了与该线共线的归一化向量。使用这个向量,你可以找到一个正交向量。

我使用下面的代码得到[0.983035, -0.183421]为共线向量。所以,[0.183421 0.983035]与这个向量正交。

这里,在左图中,红线是最小二乘线,蓝线是与红线垂直的线。在右图中,红色线是最小二乘线,绿色线是使用上述RANSAC库拟合的线。

lsq ransac

Mat im = imread("LP24W.png", 0); 

Mat bw, gr; 

threshold(im, bw, 0, 255, CV_THRESH_BINARY|CV_THRESH_OTSU); 

Mat kernel = getStructuringElement(MORPH_ELLIPSE, Size(3, 3)); 
morphologyEx(bw, gr, CV_MOP_GRADIENT, kernel); 

vector<vector<Point>> contours; 
findContours(gr, contours, CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE); 
vector<Point> points; 
for (vector<Point>& cont: contours) 
{ 
    points.insert(points.end(), cont.begin(), cont.end()); 
} 
Vec4f line; 
fitLine(points, line, CV_DIST_L2, 0, 0.01, 0.01); 
cout << line << endl;