I am using OpenCV HoughlinesP to find horizontal and vertical lines. It is not finding any lines most of the time. Even when it finds a lines it is not even close to actual image.
我使用OpenCV HoughlinesP查找水平和垂直线条。它大多数时候都找不到任何线。即使它找到了一条线,它也不接近真实的图像。
import cv2
import numpy as np
img = cv2.imread('image_with_edges.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
flag,b = cv2.threshold(gray,0,255,cv2.THRESH_OTSU)
element = cv2.getStructuringElement(cv2.MORPH_CROSS,(1,1))
cv2.erode(b,element)
edges = cv2.Canny(b,10,100,apertureSize = 3)
lines = cv2.HoughLinesP(edges,1,np.pi/2,275, minLineLength = 100, maxLineGap = 200)[0].tolist()
for x1,y1,x2,y2 in lines:
for index, (x3,y3,x4,y4) in enumerate(lines):
if y1==y2 and y3==y4: # Horizontal Lines
diff = abs(y1-y3)
elif x1==x2 and x3==x4: # Vertical Lines
diff = abs(x1-x3)
else:
diff = 0
if diff < 10 and diff is not 0:
del lines[index]
gridsize = (len(lines) - 2) / 2
cv2.line(img,(x1,y1),(x2,y2),(0,0,255),2)
cv2.imwrite('houghlines3.jpg',img)
输入图像:
Output Image: (see the Red Line):
输出图像:(见红线):
@ljetibo Try this with: c_6.jpg
@ljetibo试试这个:c_6.jpg
1 个解决方案
#1
7
There's quite a bit wrong here so I'll just start from the beginning.
这里有一些错误,我就从头开始。
Ok, first thing you do after opening an image is tresholding. I recommend strongly that you have another look at the OpenCV manual on tresholding and the exact meaning of the treshold methods.
打开一张图片后,你要做的第一件事就是对它进行重新命名。我强烈建议您再看一下OpenCV手册关于treshoning和treshold方法的确切含义。
The manual mentions that
手动提到
cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
cv2。阈值(src,推敲,maxval类型(dst))→retval,dst
the special value THRESH_OTSU may be combined with one of the above values. In this case, the function determines the optimal threshold value using the Otsu’s algorithm and uses it instead of the specified thresh .
特殊值阈值可以与上述值之一相结合。在这种情况下,该函数使用Otsu算法确定最优阈值,并使用它代替指定的thresh。
I know it's a bit confusing because you don't actully combine THRESH_OTSU with any of the other methods (THRESH_BINARY etc...), unfortunately that manual can be like that. What this method actually does is it assumes that there's a "foreground" and a "background" that follow a bi-modal histogram and then applies the THRESH_BINARY I believe.
我知道这有点让人困惑,因为您没有将阈值- otsu与任何其他方法(阈值-二进制等等)结合起来,不幸的是,这个手册可能是这样的。这个方法的实际作用是它假设有一个“前景”和一个“背景”,它遵循双模态直方图,然后应用我认为的阈值二进制。
Imagine this as if you're taking an image of a cathedral or a high building mid day. On a sunny day the sky will be very bright and blue, and the cathedral/building will be quite a bit darker. This means the group of pixels belonging to the sky will all have high brightness values, that is will be on the right side of the histogram, and the pixels belonging to the church will be darker, that is to the middle and left side of the histogram.
想象一下,如果你正在拍摄一座大教堂或一座高层建筑的照片。在阳光明媚的日子里,天空将会非常明亮和蔚蓝,大教堂/建筑将会更加阴暗。这意味着属于天空的像素组都具有较高的亮度值,即在直方图的右边,属于教堂的像素会更暗,也就是直方图的中间和左边。
Otsu uses this to try and guess the right "cutoff" point, called thresh. For your image Otsu's alg. supposes that all that white on the side of the map is the background, and the map itself the foreground. Therefore your image after thresholding looks like this:
Otsu用这个来尝试猜测正确的“截止”点,叫做thresh。为了你的形象,Otsu的alg。假设地图侧面所有的白色都是背景,而地图本身就是前景。因此,阈值后的图像是这样的:
After this point it's not hard to guess what goes wrong. But let's go on, What you're trying to achieve is, I believe, something like this:
在这之后,就不难猜到哪里出了问题。但是让我们继续,你想要达到的,我相信,是这样的:
flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY)
Then you go on, and try to erode the image. I'm not sure why you're doing this, was your intention to "bold" the lines, or was your intention to remove noise. In any case you never assigned the result of erosion to something. Numpy arrays, which is the way images are represented, are mutable but it's not the way the syntax works:
然后你继续,试着破坏图像。我不知道你为什么要这么做,是你想“加粗”线条,还是你想消除噪音。无论如何,你从来没有把侵蚀的结果分配给某物。Numpy数组是表示图像的方式,是可变的,但它不是语法的工作方式:
cv2.erode(src, kernel, [optionalOptions] ) → dst
So you have to write:
所以你必须写:
b = cv2.erode(b,element)
Ok, now for the element and how the erosion works. Erosion drags a kernel over an image. Kernel is a simple matrix with 1's and 0's in it. One of the elements of that matrix, usually centre one, is called an anchor. An anchor is the element that will be replaced at the end of the operation. When you created
好了,现在来看看元素和侵蚀是如何工作的。侵蚀在图像上拖动内核。核是一个包含1和0的简单矩阵。这个矩阵的一个元素,通常是中心的,叫做锚。锚是在操作结束时将被替换的元素。当你创建
cv2.getStructuringElement(cv2.MORPH_CROSS, (1, 1))
what you created is actually a 1x1 matrix (1 column, 1 row). This makes erosion completely useless.
你创建的实际上是一个1x1矩阵(1列,1行)这使得侵蚀毫无用处。
What erosion does, is firstly retrieves all the values of pixel brightness from the original image where the kernel element, overlapping the image segment, has a "1". Then it finds a minimal value of retrieved pixels and replaces the anchor with that value.
侵蚀的作用是,首先从原始图像中检索像素亮度的所有值,在原始图像中,与图像段重叠的核元素有一个“1”。然后它找到一个最小值的检索像素,并用这个值替换锚。
What this means, in your case, is that you drag [1]
matrix over the image, compare if the source image pixel brightness is larger, equal or smaller than itself and then you replace it with itself.
这意味着,在你的例子中,你在图像上拖拽[1]矩阵,比较源图像像素的亮度是否大于、等于或小于它本身,然后用它本身替换它。
If your intention was to remove "noise", then it's probably better to use a rectangular kernel over the image. Think of it this way, "noise" is that thing that "doesn't fit in" with the surroundings. So if you compare your centre pixel with it's surroundings and you find it doesn't fit, it's most likely noise.
如果您的目的是删除“噪声”,那么最好在图像上使用矩形内核。这样想,“噪音”就是那些与周围环境“格格不入”的东西。如果你把中心像素和周围环境进行比较,你会发现它不合适,很可能是噪音。
Additionally, I've said it replaces the anchor with the minimal value retrieved by the kernel. Numerically, minimal value is 0, which is coincidentally how black is represented in the image. This means that in your case of a predominantly white image, erosion would "bloat up" the black pixels. Erosion would replace the 255 valued white pixels with 0 valued black pixels if they're in the reach of the kernel. In any case it shouldn't be of a shape (1,1), ever.
此外,我还说过它用内核检索到的最小值替换锚点。数值上,最小值为0,这恰好是黑色在图像中的表示方式。这意味着,在以白色为主的图像中,侵蚀会“膨胀”黑色像素。如果位于内核的范围内,那么侵蚀将把255个值的白色像素替换为0个值的黑色像素。无论如何,它不应该是(1,1)的形状。
>>> cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]], dtype=uint8)
If we erode the second image with a 3x3 rectangular kernel we get the image bellow.
如果我们用一个3x3的矩形核破坏第二个图像,我们得到图像波纹。
Ok, now we got that out of the way, next thing you do is you find edges using Canny edge detection. The image you get from that is:
好了,现在我们已经解决了这个问题,接下来你要做的就是通过精明的边缘检测找到边缘。你从中得到的印象是:
Ok, now we look for EXACTLY vertical and EXACTLY horizontal lines ONLY. Of course there are no such lines apart from the meridian on the left of the image (is that what it's called?) and the end image you get after you did it right would be this:
现在我们只看垂直和水平的直线。当然,除了图像左边的子午线(这就是它的名字吗?)之外,没有这样的线,而你做对后得到的最终图像是这样的:
Now since you never described your exact idea, and my best guess is that you want the parallels and meridians, you'll have more luck on maps with lesser scale because those aren't lines to begin with, they are curves. Additionally, is there a specific reason to get a Probability Hough done? The "regular" Hough doesn't suffice?
既然你从来没有描述过你的确切想法,我最好的猜测是你想要平行和经脉,你会在地图上有更多的运气,因为那些不是开始的线,它们是曲线。此外,是否有一个特定的原因去完成一个概率值?“普通”的树枝不够用吗?
Sorry for the too-long post, hope it helps a bit.
很抱歉,这篇文章太长了,希望它能有所帮助。
Text here was added as a request for clarification from the OP Nov. 24th. because there's no way to fit the answer into a char limited comment.
此处的文字是11月24日OP要求澄清的。因为没有办法将答案填入字符限制的注释中。
I'd suggest OP asks a new question more specific to the detection of curves because you are dealing with curves op, not horizontal and vertical lines.
我建议OP问一个关于曲线检测的新问题,因为你要处理的是曲线OP,而不是水平和垂直线。
There are several ways to detect curves but none of them are easy. In the order of simplest-to-implement to hardest:
有几种方法可以检测曲线,但没有一种是容易的。按照最简单实现到最困难的顺序:
- Use RANSAC algorithm. Develop a formula describing the nature of the long. and lat. lines depending on the map in question. I.e. latitude curves will almost be a perfect straight lines on the map when you're near the equator, with the equator being the perfectly straight line, but will be very curved, resembling circle segments, when you're at high latitudes (near the poles). SciPy already has RANSAC implemented as a class all you have to do is find and the programatically define the model you want to try to fit to the curves. Of course there's the ever-usefull 4dummies text here. This is the easiest because all you have to do is the math.
- 使用RANSAC算法。制定一个公式来描述“长”的性质。和纬度。线取决于所讨论的地图。例如,当你在赤道附近时,纬度曲线几乎是地图上完美的直线,赤道是完美的直线,但当你在高纬度(靠近两极)时,它会非常弯曲,就像圆形的线段。SciPy已经将RANSAC实现为一个类,您所要做的就是找到并通过编程方式定义您希望适合于曲线的模型。当然,这里有永远有用的4dummies文本。这是最简单的,因为你要做的就是计算。
- A bit harder to do would be to create a rectangular grid and then try to use cv findHomography to warp the grid into place on the image. For various geometric transformations you can do to the grid you can check out OpenCv manual. This is sort of a hack-ish approach and might work worse than 1. because it depends on the fact that you can re-create a grid with enough details and objects on it that cv can identify the structures on the image you're trying to warp it to. This one requires you to do similar math to 1. and just a bit of coding to compose the end solution out of several different functions.
- 更困难的是创建一个矩形网格,然后尝试使用cv findHomography将网格转换为图像上的位置。对于各种几何变换,您可以对网格进行处理,您可以查看OpenCv手册。这是一种类似于黑客的方法,可能比1更糟糕。因为这取决于你可以重新创建一个包含足够细节和对象的网格,cv就可以识别你想要扭曲的图像上的结构。这个需要你做和1类似的运算。用几个不同的函数来编写最终的解决方案。
- To actually do it. There are mathematically neat ways of describing curves as a list of tangent lines on the curve. You can try to fit a bunch of shorter HoughLines to your image or image segment and then try to group all found lines and determine, by assuming that they're tangents to a curve, if they really follow a curve of the desired shape or are they random. See this paper on this matter. Out of all approaches this one is the hardest because it requires a quite a bit of solo-coding and some math about the method.
- 去做它。在数学上,有很好的方法把曲线描述成曲线上的切线。你可以试着把一些较短的象形线贴到你的图像或图像片段上,然后把所有找到的线条进行分组,通过假设它们与曲线相切,如果它们真的沿着期望的形状的曲线,或者它们是随机的来确定。请看这篇关于这件事的论文。在所有的方法中,这一个是最难的,因为它需要相当多的solo编码和一些关于方法的数学知识。
There could be easier ways, I've never actually had to deal with curve detection before. Maybe there are tricks to do it easier, I don't know. If you ask a new question, one that hasn't been closed as an answer already you might have more people notice it. Do make sure to ask a full and complete question on the exact topic you're interested in. People won't usually spend so much time writing on such a broad topic.
可能有更简单的方法,我以前从来没有处理过曲线检测。也许有更简单的方法,我不知道。如果你问了一个新的问题,一个还没有结束的问题,你可能会让更多的人注意到它。一定要就你感兴趣的话题问一个完整的问题。人们通常不会花那么多时间在这么宽泛的话题上。
To show you what you can do with just Hough transform check out bellow:
为了向你展示你可以用Hough变换做什么,看看下面的bellow:
import cv2
import numpy as np
def draw_lines(hough, image, nlines):
n_x, n_y=image.shape
#convert to color image so that you can see the lines
draw_im = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
for (rho, theta) in hough[0][:nlines]:
try:
x0 = np.cos(theta)*rho
y0 = np.sin(theta)*rho
pt1 = ( int(x0 + (n_x+n_y)*(-np.sin(theta))),
int(y0 + (n_x+n_y)*np.cos(theta)) )
pt2 = ( int(x0 - (n_x+n_y)*(-np.sin(theta))),
int(y0 - (n_x+n_y)*np.cos(theta)) )
alph = np.arctan( (pt2[1]-pt1[1])/( pt2[0]-pt1[0]) )
alphdeg = alph*180/np.pi
#OpenCv uses weird angle system, see: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
if abs( np.cos( alph - 180 )) > 0.8: #0.995:
cv2.line(draw_im, pt1, pt2, (255,0,0), 2)
if rho>0 and abs( np.cos( alphdeg - 90)) > 0.7:
cv2.line(draw_im, pt1, pt2, (0,0,255), 2)
except:
pass
cv2.imwrite("/home/dino/Desktop/3HoughLines.png", draw_im,
[cv2.IMWRITE_PNG_COMPRESSION, 12])
img = cv2.imread('a.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY)
cv2.imwrite("1tresh.jpg", b)
element = np.ones((3,3))
b = cv2.erode(b,element)
cv2.imwrite("2erodedtresh.jpg", b)
edges = cv2.Canny(b,10,100,apertureSize = 3)
cv2.imwrite("3Canny.jpg", edges)
hough = cv2.HoughLines(edges, 1, np.pi/180, 200)
draw_lines(hough, b, 100)
As you can see from the image bellow, straight lines are only longitudes. Latitudes are not as straight therefore for each latitude you have several detected lines that behave like tangents on the line. Blue drawn lines are drawn by the if abs( np.cos( alph - 180 )) > 0.8:
while the red drawn lines are drawn by rho>0 and abs( np.cos( alphdeg - 90)) > 0.7
condition. Pay close attention when comparing the original image with the image with lines drawn on it. The resemblance is uncanny (heh, get it?) but because they're not lines a lot of it only looks like junk. (especially that highest detected latitude line that seems like it's too "angled" but in reality those lines make a perfect tangent to the latitude line on its thickest point, just as hough algorithm demands it). Acknowledge that there are limitations to detecting curves with a line detection algorithm
从图像中可以看出,直线只是经度。纬度并不是笔直的,因此在每一个纬度上,你有几条被探测到的线,就像线上的切线一样。蓝色画线是由if abs(np)绘制的。cos(alph - 180)) > 0.8:红色的线条是由>0和abs(np)绘制的。cos(alphdeg - 90)) >0.7条件。在比较原始图像与绘制在其上的线条时,请密切注意。这种相似是不可思议的(嘿,明白吗?)但因为它们不是线条,很多看起来都像是垃圾。(特别是探测到的最高纬度线,看起来太“倾斜”了,但实际上这些线与纬度线最厚的点相切,就像霍夫算法要求的那样)。认识到用线检测算法检测曲线是有局限性的。
#1
7
There's quite a bit wrong here so I'll just start from the beginning.
这里有一些错误,我就从头开始。
Ok, first thing you do after opening an image is tresholding. I recommend strongly that you have another look at the OpenCV manual on tresholding and the exact meaning of the treshold methods.
打开一张图片后,你要做的第一件事就是对它进行重新命名。我强烈建议您再看一下OpenCV手册关于treshoning和treshold方法的确切含义。
The manual mentions that
手动提到
cv2.threshold(src, thresh, maxval, type[, dst]) → retval, dst
cv2。阈值(src,推敲,maxval类型(dst))→retval,dst
the special value THRESH_OTSU may be combined with one of the above values. In this case, the function determines the optimal threshold value using the Otsu’s algorithm and uses it instead of the specified thresh .
特殊值阈值可以与上述值之一相结合。在这种情况下,该函数使用Otsu算法确定最优阈值,并使用它代替指定的thresh。
I know it's a bit confusing because you don't actully combine THRESH_OTSU with any of the other methods (THRESH_BINARY etc...), unfortunately that manual can be like that. What this method actually does is it assumes that there's a "foreground" and a "background" that follow a bi-modal histogram and then applies the THRESH_BINARY I believe.
我知道这有点让人困惑,因为您没有将阈值- otsu与任何其他方法(阈值-二进制等等)结合起来,不幸的是,这个手册可能是这样的。这个方法的实际作用是它假设有一个“前景”和一个“背景”,它遵循双模态直方图,然后应用我认为的阈值二进制。
Imagine this as if you're taking an image of a cathedral or a high building mid day. On a sunny day the sky will be very bright and blue, and the cathedral/building will be quite a bit darker. This means the group of pixels belonging to the sky will all have high brightness values, that is will be on the right side of the histogram, and the pixels belonging to the church will be darker, that is to the middle and left side of the histogram.
想象一下,如果你正在拍摄一座大教堂或一座高层建筑的照片。在阳光明媚的日子里,天空将会非常明亮和蔚蓝,大教堂/建筑将会更加阴暗。这意味着属于天空的像素组都具有较高的亮度值,即在直方图的右边,属于教堂的像素会更暗,也就是直方图的中间和左边。
Otsu uses this to try and guess the right "cutoff" point, called thresh. For your image Otsu's alg. supposes that all that white on the side of the map is the background, and the map itself the foreground. Therefore your image after thresholding looks like this:
Otsu用这个来尝试猜测正确的“截止”点,叫做thresh。为了你的形象,Otsu的alg。假设地图侧面所有的白色都是背景,而地图本身就是前景。因此,阈值后的图像是这样的:
After this point it's not hard to guess what goes wrong. But let's go on, What you're trying to achieve is, I believe, something like this:
在这之后,就不难猜到哪里出了问题。但是让我们继续,你想要达到的,我相信,是这样的:
flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY)
Then you go on, and try to erode the image. I'm not sure why you're doing this, was your intention to "bold" the lines, or was your intention to remove noise. In any case you never assigned the result of erosion to something. Numpy arrays, which is the way images are represented, are mutable but it's not the way the syntax works:
然后你继续,试着破坏图像。我不知道你为什么要这么做,是你想“加粗”线条,还是你想消除噪音。无论如何,你从来没有把侵蚀的结果分配给某物。Numpy数组是表示图像的方式,是可变的,但它不是语法的工作方式:
cv2.erode(src, kernel, [optionalOptions] ) → dst
So you have to write:
所以你必须写:
b = cv2.erode(b,element)
Ok, now for the element and how the erosion works. Erosion drags a kernel over an image. Kernel is a simple matrix with 1's and 0's in it. One of the elements of that matrix, usually centre one, is called an anchor. An anchor is the element that will be replaced at the end of the operation. When you created
好了,现在来看看元素和侵蚀是如何工作的。侵蚀在图像上拖动内核。核是一个包含1和0的简单矩阵。这个矩阵的一个元素,通常是中心的,叫做锚。锚是在操作结束时将被替换的元素。当你创建
cv2.getStructuringElement(cv2.MORPH_CROSS, (1, 1))
what you created is actually a 1x1 matrix (1 column, 1 row). This makes erosion completely useless.
你创建的实际上是一个1x1矩阵(1列,1行)这使得侵蚀毫无用处。
What erosion does, is firstly retrieves all the values of pixel brightness from the original image where the kernel element, overlapping the image segment, has a "1". Then it finds a minimal value of retrieved pixels and replaces the anchor with that value.
侵蚀的作用是,首先从原始图像中检索像素亮度的所有值,在原始图像中,与图像段重叠的核元素有一个“1”。然后它找到一个最小值的检索像素,并用这个值替换锚。
What this means, in your case, is that you drag [1]
matrix over the image, compare if the source image pixel brightness is larger, equal or smaller than itself and then you replace it with itself.
这意味着,在你的例子中,你在图像上拖拽[1]矩阵,比较源图像像素的亮度是否大于、等于或小于它本身,然后用它本身替换它。
If your intention was to remove "noise", then it's probably better to use a rectangular kernel over the image. Think of it this way, "noise" is that thing that "doesn't fit in" with the surroundings. So if you compare your centre pixel with it's surroundings and you find it doesn't fit, it's most likely noise.
如果您的目的是删除“噪声”,那么最好在图像上使用矩形内核。这样想,“噪音”就是那些与周围环境“格格不入”的东西。如果你把中心像素和周围环境进行比较,你会发现它不合适,很可能是噪音。
Additionally, I've said it replaces the anchor with the minimal value retrieved by the kernel. Numerically, minimal value is 0, which is coincidentally how black is represented in the image. This means that in your case of a predominantly white image, erosion would "bloat up" the black pixels. Erosion would replace the 255 valued white pixels with 0 valued black pixels if they're in the reach of the kernel. In any case it shouldn't be of a shape (1,1), ever.
此外,我还说过它用内核检索到的最小值替换锚点。数值上,最小值为0,这恰好是黑色在图像中的表示方式。这意味着,在以白色为主的图像中,侵蚀会“膨胀”黑色像素。如果位于内核的范围内,那么侵蚀将把255个值的白色像素替换为0个值的黑色像素。无论如何,它不应该是(1,1)的形状。
>>> cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
array([[0, 1, 0],
[1, 1, 1],
[0, 1, 0]], dtype=uint8)
If we erode the second image with a 3x3 rectangular kernel we get the image bellow.
如果我们用一个3x3的矩形核破坏第二个图像,我们得到图像波纹。
Ok, now we got that out of the way, next thing you do is you find edges using Canny edge detection. The image you get from that is:
好了,现在我们已经解决了这个问题,接下来你要做的就是通过精明的边缘检测找到边缘。你从中得到的印象是:
Ok, now we look for EXACTLY vertical and EXACTLY horizontal lines ONLY. Of course there are no such lines apart from the meridian on the left of the image (is that what it's called?) and the end image you get after you did it right would be this:
现在我们只看垂直和水平的直线。当然,除了图像左边的子午线(这就是它的名字吗?)之外,没有这样的线,而你做对后得到的最终图像是这样的:
Now since you never described your exact idea, and my best guess is that you want the parallels and meridians, you'll have more luck on maps with lesser scale because those aren't lines to begin with, they are curves. Additionally, is there a specific reason to get a Probability Hough done? The "regular" Hough doesn't suffice?
既然你从来没有描述过你的确切想法,我最好的猜测是你想要平行和经脉,你会在地图上有更多的运气,因为那些不是开始的线,它们是曲线。此外,是否有一个特定的原因去完成一个概率值?“普通”的树枝不够用吗?
Sorry for the too-long post, hope it helps a bit.
很抱歉,这篇文章太长了,希望它能有所帮助。
Text here was added as a request for clarification from the OP Nov. 24th. because there's no way to fit the answer into a char limited comment.
此处的文字是11月24日OP要求澄清的。因为没有办法将答案填入字符限制的注释中。
I'd suggest OP asks a new question more specific to the detection of curves because you are dealing with curves op, not horizontal and vertical lines.
我建议OP问一个关于曲线检测的新问题,因为你要处理的是曲线OP,而不是水平和垂直线。
There are several ways to detect curves but none of them are easy. In the order of simplest-to-implement to hardest:
有几种方法可以检测曲线,但没有一种是容易的。按照最简单实现到最困难的顺序:
- Use RANSAC algorithm. Develop a formula describing the nature of the long. and lat. lines depending on the map in question. I.e. latitude curves will almost be a perfect straight lines on the map when you're near the equator, with the equator being the perfectly straight line, but will be very curved, resembling circle segments, when you're at high latitudes (near the poles). SciPy already has RANSAC implemented as a class all you have to do is find and the programatically define the model you want to try to fit to the curves. Of course there's the ever-usefull 4dummies text here. This is the easiest because all you have to do is the math.
- 使用RANSAC算法。制定一个公式来描述“长”的性质。和纬度。线取决于所讨论的地图。例如,当你在赤道附近时,纬度曲线几乎是地图上完美的直线,赤道是完美的直线,但当你在高纬度(靠近两极)时,它会非常弯曲,就像圆形的线段。SciPy已经将RANSAC实现为一个类,您所要做的就是找到并通过编程方式定义您希望适合于曲线的模型。当然,这里有永远有用的4dummies文本。这是最简单的,因为你要做的就是计算。
- A bit harder to do would be to create a rectangular grid and then try to use cv findHomography to warp the grid into place on the image. For various geometric transformations you can do to the grid you can check out OpenCv manual. This is sort of a hack-ish approach and might work worse than 1. because it depends on the fact that you can re-create a grid with enough details and objects on it that cv can identify the structures on the image you're trying to warp it to. This one requires you to do similar math to 1. and just a bit of coding to compose the end solution out of several different functions.
- 更困难的是创建一个矩形网格,然后尝试使用cv findHomography将网格转换为图像上的位置。对于各种几何变换,您可以对网格进行处理,您可以查看OpenCv手册。这是一种类似于黑客的方法,可能比1更糟糕。因为这取决于你可以重新创建一个包含足够细节和对象的网格,cv就可以识别你想要扭曲的图像上的结构。这个需要你做和1类似的运算。用几个不同的函数来编写最终的解决方案。
- To actually do it. There are mathematically neat ways of describing curves as a list of tangent lines on the curve. You can try to fit a bunch of shorter HoughLines to your image or image segment and then try to group all found lines and determine, by assuming that they're tangents to a curve, if they really follow a curve of the desired shape or are they random. See this paper on this matter. Out of all approaches this one is the hardest because it requires a quite a bit of solo-coding and some math about the method.
- 去做它。在数学上,有很好的方法把曲线描述成曲线上的切线。你可以试着把一些较短的象形线贴到你的图像或图像片段上,然后把所有找到的线条进行分组,通过假设它们与曲线相切,如果它们真的沿着期望的形状的曲线,或者它们是随机的来确定。请看这篇关于这件事的论文。在所有的方法中,这一个是最难的,因为它需要相当多的solo编码和一些关于方法的数学知识。
There could be easier ways, I've never actually had to deal with curve detection before. Maybe there are tricks to do it easier, I don't know. If you ask a new question, one that hasn't been closed as an answer already you might have more people notice it. Do make sure to ask a full and complete question on the exact topic you're interested in. People won't usually spend so much time writing on such a broad topic.
可能有更简单的方法,我以前从来没有处理过曲线检测。也许有更简单的方法,我不知道。如果你问了一个新的问题,一个还没有结束的问题,你可能会让更多的人注意到它。一定要就你感兴趣的话题问一个完整的问题。人们通常不会花那么多时间在这么宽泛的话题上。
To show you what you can do with just Hough transform check out bellow:
为了向你展示你可以用Hough变换做什么,看看下面的bellow:
import cv2
import numpy as np
def draw_lines(hough, image, nlines):
n_x, n_y=image.shape
#convert to color image so that you can see the lines
draw_im = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
for (rho, theta) in hough[0][:nlines]:
try:
x0 = np.cos(theta)*rho
y0 = np.sin(theta)*rho
pt1 = ( int(x0 + (n_x+n_y)*(-np.sin(theta))),
int(y0 + (n_x+n_y)*np.cos(theta)) )
pt2 = ( int(x0 - (n_x+n_y)*(-np.sin(theta))),
int(y0 - (n_x+n_y)*np.cos(theta)) )
alph = np.arctan( (pt2[1]-pt1[1])/( pt2[0]-pt1[0]) )
alphdeg = alph*180/np.pi
#OpenCv uses weird angle system, see: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_imgproc/py_houghlines/py_houghlines.html
if abs( np.cos( alph - 180 )) > 0.8: #0.995:
cv2.line(draw_im, pt1, pt2, (255,0,0), 2)
if rho>0 and abs( np.cos( alphdeg - 90)) > 0.7:
cv2.line(draw_im, pt1, pt2, (0,0,255), 2)
except:
pass
cv2.imwrite("/home/dino/Desktop/3HoughLines.png", draw_im,
[cv2.IMWRITE_PNG_COMPRESSION, 12])
img = cv2.imread('a.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
flag,b = cv2.threshold(gray,160,255,cv2.THRESH_BINARY)
cv2.imwrite("1tresh.jpg", b)
element = np.ones((3,3))
b = cv2.erode(b,element)
cv2.imwrite("2erodedtresh.jpg", b)
edges = cv2.Canny(b,10,100,apertureSize = 3)
cv2.imwrite("3Canny.jpg", edges)
hough = cv2.HoughLines(edges, 1, np.pi/180, 200)
draw_lines(hough, b, 100)
As you can see from the image bellow, straight lines are only longitudes. Latitudes are not as straight therefore for each latitude you have several detected lines that behave like tangents on the line. Blue drawn lines are drawn by the if abs( np.cos( alph - 180 )) > 0.8:
while the red drawn lines are drawn by rho>0 and abs( np.cos( alphdeg - 90)) > 0.7
condition. Pay close attention when comparing the original image with the image with lines drawn on it. The resemblance is uncanny (heh, get it?) but because they're not lines a lot of it only looks like junk. (especially that highest detected latitude line that seems like it's too "angled" but in reality those lines make a perfect tangent to the latitude line on its thickest point, just as hough algorithm demands it). Acknowledge that there are limitations to detecting curves with a line detection algorithm
从图像中可以看出,直线只是经度。纬度并不是笔直的,因此在每一个纬度上,你有几条被探测到的线,就像线上的切线一样。蓝色画线是由if abs(np)绘制的。cos(alph - 180)) > 0.8:红色的线条是由>0和abs(np)绘制的。cos(alphdeg - 90)) >0.7条件。在比较原始图像与绘制在其上的线条时,请密切注意。这种相似是不可思议的(嘿,明白吗?)但因为它们不是线条,很多看起来都像是垃圾。(特别是探测到的最高纬度线,看起来太“倾斜”了,但实际上这些线与纬度线最厚的点相切,就像霍夫算法要求的那样)。认识到用线检测算法检测曲线是有局限性的。