使用 Python 从地图图像中提取建筑物边缘
Extract building edges from map image using Python
我这里有一张地图图片。
我需要提取建筑物的边缘以进行进一步处理,结果类似于 post here.
的第 2 步
我对这个领域不熟悉,请问OpenCV之类的库可以做吗?
这是一个简单的方法
- 将图像转换为灰度和高斯模糊以平滑边缘
- 阈值图像
- 执行Canny边缘检测
- 查找等高线并绘制等高线
阈值图像使用 cv2.threshold()
使用 cv2.Canny()
执行 Canny 边缘检测
使用 cv2.findContours()
和 cv2.drawContours()
查找轮廓
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
thresh = cv2.threshold(blurred, 240 ,255, cv2.THRESH_BINARY_INV)[1]
canny = cv2.Canny(thresh, 50, 255, 1)
cnts = cv2.findContours(canny, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image,[c], 0, (36,255,12), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('canny', canny)
cv2.imshow('image', image)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('canny.png', canny)
cv2.imwrite('image.png', image)
cv2.waitKey(0)
看来你要select个别建筑,所以我用了分色。墙壁较暗,这使得 HSV colorspace 中的分隔效果很好。请注意,使用压缩率较低的图像类型(例如 PNG)放大更多 and/or 可以改善最终结果。
Select 墙壁
首先,我确定了良好的分离值。为此,我稍微使用了 this script. I found that the best result would be to separate the yellow and the gray separately and then combine the resulting masks. Not all walls closed perfectly, so I improved the result by closing 掩码。结果是显示所有墙壁的遮罩:
从左到右:黄色蒙版、灰色蒙版、组合固化蒙版
查找建筑物
接下来,我使用 findCountours to separate out buildings. Since the wall contours will probably not be very useful (as walls are interconnected), I used the hierarchy 找到 'lowest' 等高线(其中没有其他等高线)。这些是建筑物。
findContours 的结果:所有等高线的轮廓为绿色,个别建筑物的轮廓为红色
请注意,未检测到边缘上的建筑物。这是因为使用这种技术它们不是单独的轮廓,而是图像外部的一部分。这可以通过在图像的边框上绘制一个灰色的 rectangle 来解决。您可能不希望在最终申请中使用它,但我将其包含在您需要的情况下。
代码:
import cv2
import numpy as np
#load image and convert to hsv
img = cv2.imread("fLzI9.jpg")
# draw gray box around image to detect edge buildings
h,w = img.shape[:2]
cv2.rectangle(img,(0,0),(w-1,h-1), (50,50,50),1)
# convert image to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define color ranges
low_yellow = (0,28,0)
high_yellow = (27,255,255)
low_gray = (0,0,0)
high_gray = (179,255,233)
# create masks
yellow_mask = cv2.inRange(hsv, low_yellow, high_yellow )
gray_mask = cv2.inRange(hsv, low_gray, high_gray)
# combine masks
combined_mask = cv2.bitwise_or(yellow_mask, gray_mask)
kernel = np.ones((3,3), dtype=np.uint8)
combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_DILATE,kernel)
# findcontours
contours, hier = cv2.findContours(combined_mask,cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# find and draw buildings
for x in range(len(contours)):
# if a contour has not contours inside of it, draw the shape filled
c = hier[0][x][2]
if c == -1:
cv2.drawContours(img,[contours[x]],0,(0,0,255),-1)
# draw the outline of all contours
for cnt in contours:
cv2.drawContours(img,[cnt],0,(0,255,0),2)
# display result
cv2.imshow("Result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
建筑物绘制为纯红色,所有等高线为绿色覆盖
我这里有一张地图图片。 我需要提取建筑物的边缘以进行进一步处理,结果类似于 post here.
的第 2 步我对这个领域不熟悉,请问OpenCV之类的库可以做吗?
这是一个简单的方法
- 将图像转换为灰度和高斯模糊以平滑边缘
- 阈值图像
- 执行Canny边缘检测
- 查找等高线并绘制等高线
阈值图像使用 cv2.threshold()
使用 cv2.Canny()
使用 cv2.findContours()
和 cv2.drawContours()
import cv2
image = cv2.imread('1.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (3, 3), 0)
thresh = cv2.threshold(blurred, 240 ,255, cv2.THRESH_BINARY_INV)[1]
canny = cv2.Canny(thresh, 50, 255, 1)
cnts = cv2.findContours(canny, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if len(cnts) == 2 else cnts[1]
for c in cnts:
cv2.drawContours(image,[c], 0, (36,255,12), 2)
cv2.imshow('thresh', thresh)
cv2.imshow('canny', canny)
cv2.imshow('image', image)
cv2.imwrite('thresh.png', thresh)
cv2.imwrite('canny.png', canny)
cv2.imwrite('image.png', image)
cv2.waitKey(0)
看来你要select个别建筑,所以我用了分色。墙壁较暗,这使得 HSV colorspace 中的分隔效果很好。请注意,使用压缩率较低的图像类型(例如 PNG)放大更多 and/or 可以改善最终结果。
Select 墙壁
首先,我确定了良好的分离值。为此,我稍微使用了 this script. I found that the best result would be to separate the yellow and the gray separately and then combine the resulting masks. Not all walls closed perfectly, so I improved the result by closing 掩码。结果是显示所有墙壁的遮罩:
查找建筑物
接下来,我使用 findCountours to separate out buildings. Since the wall contours will probably not be very useful (as walls are interconnected), I used the hierarchy 找到 'lowest' 等高线(其中没有其他等高线)。这些是建筑物。
请注意,未检测到边缘上的建筑物。这是因为使用这种技术它们不是单独的轮廓,而是图像外部的一部分。这可以通过在图像的边框上绘制一个灰色的 rectangle 来解决。您可能不希望在最终申请中使用它,但我将其包含在您需要的情况下。
代码:
import cv2
import numpy as np
#load image and convert to hsv
img = cv2.imread("fLzI9.jpg")
# draw gray box around image to detect edge buildings
h,w = img.shape[:2]
cv2.rectangle(img,(0,0),(w-1,h-1), (50,50,50),1)
# convert image to HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# define color ranges
low_yellow = (0,28,0)
high_yellow = (27,255,255)
low_gray = (0,0,0)
high_gray = (179,255,233)
# create masks
yellow_mask = cv2.inRange(hsv, low_yellow, high_yellow )
gray_mask = cv2.inRange(hsv, low_gray, high_gray)
# combine masks
combined_mask = cv2.bitwise_or(yellow_mask, gray_mask)
kernel = np.ones((3,3), dtype=np.uint8)
combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_DILATE,kernel)
# findcontours
contours, hier = cv2.findContours(combined_mask,cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# find and draw buildings
for x in range(len(contours)):
# if a contour has not contours inside of it, draw the shape filled
c = hier[0][x][2]
if c == -1:
cv2.drawContours(img,[contours[x]],0,(0,0,255),-1)
# draw the outline of all contours
for cnt in contours:
cv2.drawContours(img,[cnt],0,(0,255,0),2)
# display result
cv2.imshow("Result", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
结果:
建筑物绘制为纯红色,所有等高线为绿色覆盖