有Mat是灰度像素的正方形区域。如何创建一个直线,其方向是垂直于大多数像素值的方向而改变方向(平均梯度,整个Mat上的平均值,结果将只是一个方向(然后可以绘制为一条直线))?如何检测强度梯度方向
例如,具有
它看起来像
一个怎么能做出这种事情的OpenCV(在Python或C++)?
有Mat是灰度像素的正方形区域。如何创建一个直线,其方向是垂直于大多数像素值的方向而改变方向(平均梯度,整个Mat上的平均值,结果将只是一个方向(然后可以绘制为一条直线))?如何检测强度梯度方向
例如,具有
它看起来像
一个怎么能做出这种事情的OpenCV(在Python或C++)?
的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();
不错 - 做得好!我同意正常化 - 我主要是为了可视化而做的,因为对原始问题的一些评论意味着人们无法看到OP在寻找什么。 –
我不能轻易告诉你如何与OpenCV的做到这一点,但我可以告诉你的方法,用ImageMagick的只要在命令行演示。
首先,我认为你需要将图像转换为灰度图像,并将其归到全方位的黑到白的 - 这样的:
convert gradient.png -colorspace gray -normalize stage1.png
然后,你需要计算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 - 特别是这一行:
将图像转换为灰度和基于所述灰度级分类其像素。对于分类,你可以使用类似Otsu方法或kmeans与2个集群。然后采取形态梯度来检测边界。
这是使用Otsu方法的分类像素和边界。
现在找到边界图像的非零像素和拟合2D线使用fitLine函数找到的加权最小二乘线或使用this RANSAC执行的那些像素。 fitLine给出了与该线共线的归一化向量。使用这个向量,你可以找到一个正交向量。
我使用下面的代码得到[0.983035, -0.183421]
为共线向量。所以,[0.183421 0.983035]
与这个向量正交。
这里,在左图中,红线是最小二乘线,蓝线是与红线垂直的线。在右图中,红色线是最小二乘线,绿色线是使用上述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;
听起来就像平均梯度... – Julien
我看到的图片几乎是均匀的象素。你是什么意思? –
请定义“垂直于像素值”。 – Ripi2