使用 OpenCV 检测图像中的粗黑线
Detect thick black lines in image with OpenCV
我有下面这张乐高板的图片,上面有一些积木
现在我正尝试用 OpenCV 检测粗黑线(连接白色方块)。我已经对 HoughLinesP 进行了很多试验,之前将图像转换为灰色或 b/w,应用模糊,...
Nonthing 导致了可用的结果。
# Read image
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Resize Image
img = cv2.resize(img, (0,0), fx=0.25, fy=0.25)
# Initialize output
out = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# Median blurring to get rid of the noise; invert image
img = cv2.medianBlur(img, 5)
# Adaptive Treshold
bw = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,15,8)
# HoughLinesP
linesP = cv2.HoughLinesP(bw, 500, np.pi / 180, 50, None, 50, 10)
# Draw Lines
if linesP is not None:
for i in range(0, len(linesP)):
l = linesP[i][0]
cv2.line(out, (l[0], l[1]), (l[2], l[3]), (0,0,255), 3, cv2.LINE_AA)
自适应阈值让您可以很好地看到边缘,但是使用 HoughLinesP 您无法从中得到任何可用的东西
我做错了什么?
感谢@fmw42 和@jeru-luke 对这个问题的出色解决方案!我喜欢隔离/遮盖绿板,所以我将两者结合起来:
import cv2
import numpy as np
img = cv2.imread("image.jpg")
scale_percent = 50 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_component = lab[:,:,1]
# binary threshold the a-channel
th = cv2.threshold(a_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# numpy black
black = np.zeros((img.shape[0],img.shape[1]),np.uint8)
# function to obtain the largest contour in given image after filling it
def get_region(image):
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
mask = cv2.drawContours(black,[c],0,255, -1)
return mask
mask = get_region(th)
# turning the region outside the green block white
green_block = cv2.bitwise_and(img, img, mask = mask)
green_block[black==0]=(255,255,255)
# median blur
median = cv2.medianBlur(green_block, 5)
# threshold on black
lower = (0,0,0)
upper = (15,15,15)
thresh = cv2.inRange(median, lower, upper)
# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# filter contours on area
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = green_block.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 2)
# view result
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
这是 Python/OpenCV 中的一种方法。
- 阅读图片
- 应用中值模糊
- 使用 cv2.inRange()
的黑色阈值
- 应用形态学进行清理
- 获取等高线并过滤区域
- 根据输入绘制等高线
- 保存结果
输入:
import cv2
import numpy as np
# read image
img = cv2.imread('black_lines.jpg')
# median blur
median = cv2.medianBlur(img, 5)
# threshold on black
lower = (0,0,0)
upper = (15,15,15)
thresh = cv2.inRange(median, lower, upper)
# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# filter contours on area
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 2)
# save result
cv2.imwrite("black_lines_threshold.jpg", thresh)
cv2.imwrite("black_lines_morphology.jpg", morph)
cv2.imwrite("black_lines_result.jpg", result)
# view result
cv2.imshow("threshold", thresh)
cv2.imshow("morphology", morph)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
阈值图像:
形态图像:
结果:
在这里,我将介绍一种使用颜色的重复分割方法。
此答案基于 LAB color space
的用法
1。隔离绿色乐高积木
img = cv2.imread(image_path)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_component = lab[:,:,1]
# binary threshold the a-channel
th = cv2.threshold(a_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
th
# function to obtain the largest contour in given image after filling it
def get_region(image):
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
black = np.zeros((image.shape[0], image.shape[1]), np.uint8)
mask = cv2.drawContours(black,[c],0,255, -1)
return mask
mask = get_region(th)
mask
# turning the region outside the green block white
green_block = cv2.bitwise_and(img, img, mask = mask)
green_block[black==0]=(255,255,255)
green_block
2。分割道路
- 为了得到道路的大致区域,我减去
mask
和 th
。
cv2.subtract()
执行算术减法,其中 cv2
将处理负值。
road = cv2.subtract(mask,th)
# `road` contains some unwanted spots/contours which are removed using the function "get_region"
only_road = get_region(road)
only_road
仅用原始图像遮盖路段给出
road_colored = cv2.bitwise_and(img, img, mask = only_road)
road_colored[only_road==0]=(255,255,255)
road_colored
上图只有黑色区域(道路),很容易分割:
# converting to grayscale and applying threshold
th2 = cv2.threshold(road_colored[:,:,1],127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# using portion of the code from fmw42's answer, to get contours above certain area
contours = cv2.findContours(th2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 4)
result
注:
要清理最终结果,您可以在绘制轮廓之前对 th2
应用形态学操作。
我有下面这张乐高板的图片,上面有一些积木
现在我正尝试用 OpenCV 检测粗黑线(连接白色方块)。我已经对 HoughLinesP 进行了很多试验,之前将图像转换为灰色或 b/w,应用模糊,... Nonthing 导致了可用的结果。
# Read image
img = cv2.imread('image.jpg', cv2.IMREAD_GRAYSCALE)
# Resize Image
img = cv2.resize(img, (0,0), fx=0.25, fy=0.25)
# Initialize output
out = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
# Median blurring to get rid of the noise; invert image
img = cv2.medianBlur(img, 5)
# Adaptive Treshold
bw = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\
cv2.THRESH_BINARY,15,8)
# HoughLinesP
linesP = cv2.HoughLinesP(bw, 500, np.pi / 180, 50, None, 50, 10)
# Draw Lines
if linesP is not None:
for i in range(0, len(linesP)):
l = linesP[i][0]
cv2.line(out, (l[0], l[1]), (l[2], l[3]), (0,0,255), 3, cv2.LINE_AA)
自适应阈值让您可以很好地看到边缘,但是使用 HoughLinesP 您无法从中得到任何可用的东西
我做错了什么?
感谢@fmw42 和@jeru-luke 对这个问题的出色解决方案!我喜欢隔离/遮盖绿板,所以我将两者结合起来:
import cv2
import numpy as np
img = cv2.imread("image.jpg")
scale_percent = 50 # percent of original size
width = int(img.shape[1] * scale_percent / 100)
height = int(img.shape[0] * scale_percent / 100)
dim = (width, height)
# resize image
img = cv2.resize(img, dim, interpolation = cv2.INTER_AREA)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_component = lab[:,:,1]
# binary threshold the a-channel
th = cv2.threshold(a_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# numpy black
black = np.zeros((img.shape[0],img.shape[1]),np.uint8)
# function to obtain the largest contour in given image after filling it
def get_region(image):
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
mask = cv2.drawContours(black,[c],0,255, -1)
return mask
mask = get_region(th)
# turning the region outside the green block white
green_block = cv2.bitwise_and(img, img, mask = mask)
green_block[black==0]=(255,255,255)
# median blur
median = cv2.medianBlur(green_block, 5)
# threshold on black
lower = (0,0,0)
upper = (15,15,15)
thresh = cv2.inRange(median, lower, upper)
# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# filter contours on area
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = green_block.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 2)
# view result
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
这是 Python/OpenCV 中的一种方法。
- 阅读图片
- 应用中值模糊
- 使用 cv2.inRange() 的黑色阈值
- 应用形态学进行清理
- 获取等高线并过滤区域
- 根据输入绘制等高线
- 保存结果
输入:
import cv2
import numpy as np
# read image
img = cv2.imread('black_lines.jpg')
# median blur
median = cv2.medianBlur(img, 5)
# threshold on black
lower = (0,0,0)
upper = (15,15,15)
thresh = cv2.inRange(median, lower, upper)
# apply morphology open and close
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3,3))
morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (29,29))
morph = cv2.morphologyEx(morph, cv2.MORPH_CLOSE, kernel)
# filter contours on area
contours = cv2.findContours(morph, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 2)
# save result
cv2.imwrite("black_lines_threshold.jpg", thresh)
cv2.imwrite("black_lines_morphology.jpg", morph)
cv2.imwrite("black_lines_result.jpg", result)
# view result
cv2.imshow("threshold", thresh)
cv2.imshow("morphology", morph)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()
阈值图像:
形态图像:
结果:
在这里,我将介绍一种使用颜色的重复分割方法。 此答案基于 LAB color space
的用法1。隔离绿色乐高积木
img = cv2.imread(image_path)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
a_component = lab[:,:,1]
# binary threshold the a-channel
th = cv2.threshold(a_component,127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
th
# function to obtain the largest contour in given image after filling it
def get_region(image):
contours, hierarchy = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
c = max(contours, key = cv2.contourArea)
black = np.zeros((image.shape[0], image.shape[1]), np.uint8)
mask = cv2.drawContours(black,[c],0,255, -1)
return mask
mask = get_region(th)
mask
# turning the region outside the green block white
green_block = cv2.bitwise_and(img, img, mask = mask)
green_block[black==0]=(255,255,255)
green_block
2。分割道路
- 为了得到道路的大致区域,我减去
mask
和th
。
cv2.subtract()
执行算术减法,其中 cv2
将处理负值。
road = cv2.subtract(mask,th)
# `road` contains some unwanted spots/contours which are removed using the function "get_region"
only_road = get_region(road)
only_road
仅用原始图像遮盖路段给出
road_colored = cv2.bitwise_and(img, img, mask = only_road)
road_colored[only_road==0]=(255,255,255)
road_colored
上图只有黑色区域(道路),很容易分割:
# converting to grayscale and applying threshold
th2 = cv2.threshold(road_colored[:,:,1],127,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)[1]
# using portion of the code from fmw42's answer, to get contours above certain area
contours = cv2.findContours(th2, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
result = img.copy()
for c in contours:
area = cv2.contourArea(c)
if area > 1000:
cv2.drawContours(result, [c], -1, (0, 0, 255), 4)
result
注:
要清理最终结果,您可以在绘制轮廓之前对 th2
应用形态学操作。