从opencv中的数独中提取框
Extract boxes from sudoku in opencv
我已经使用 opencv 将数独图像转换为数独网格
现在我想从图像中提取每个框,最好的方法是什么?
据我所知,我正在尝试找到线的交点以找到每个框的角
class SudokuSolverPlay:
def __init__(self, image):
def __preProcess(self, img):
"""return grayscale image"""
def __maskSudoku(self, img):
"""return masked image"""
def __dectactEdge(self, img):
"""return sudoku grid"""
def drawLines(src, dest, iteration=1):
minLineLength = 100
src = cv2.convertScaleAbs(src)
for _ in range(iteration):
lines = cv2.HoughLinesP(image=src, rho=1, theta=np.pi / 180,
threshold=100, lines=np.array([]),
minLineLength=minLineLength, maxLineGap=100)
a, b, c = lines.shape
for i in range(a):
x1, y1, x2, y2 = lines[i][0][0], lines[i][0][1], lines[i][0][2], lines[i][0][3]
cv2.line(dest, (x1, y1), (x2, y2),255, 1, cv2.LINE_AA)
src = cv2.convertScaleAbs(dest)
def findVerticalLines(img):
imgX = cv2.GaussianBlur(img, (5, 5), 0)
kernelx = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 10))
imgY = cv2.Sobel(img, cv2.CV_64F, 1, 0)
imgY = cv2.convertScaleAbs(imgY)
cv2.normalize(imgY, imgY, 0, 255, cv2.NORM_MINMAX)
imgY = cv2.morphologyEx(imgY, cv2.MORPH_CLOSE, kernelx, iterations=1)
return imgY
def findHorizontalLines(img):
"""same as above only args different"""
img1 = np.zeros(img.shape)
edges = cv2.Canny(img, 50, 150, apertureSize=3)
laplacian = cv2.Laplacian(edges, cv2.CV_64F)
drawLines(laplacian, img1, iteration=1)
sby = findVerticalLines(img1)
sbx = findHorizontalLines(img1)
return img1
def solveSudoku(self):
gray = self.__preProcess(self.__originalImg)
masked = self.__maskSudoku(gray)
grid = self.__dectactGrid(masked)
if __name__ == '__main__':
colorImg = cv2.imread('sudoku1.jpg')
solver = SudokuSolverPlay(colorImg)
solver.solveSudoku()
此处findVerticalLines()
和findHorizontalLines()
无法正确地指示水平和垂直线
- original image
- masked image
- Canny edge dictation
- hough line transform
- horizontal lines
- Vertical Lines
一种解决方法是做一个形态学操作,从canny边缘图像中找到垂直和水平线,然后做一个连接的组件分析来找到盒子。
我在下面做了一个示例版本。您可以进一步微调它以使其更好。
我从蒙版图像作为输入开始。
### reading input image
gray_scale=cv2.imread('masked_image.jpg',0)
执行 Canny 边缘检测并添加膨胀层
img_bin = cv2.Canny(gray_scale,50,110)
dil_kernel = np.ones((3,3), np.uint8)
img_bin=cv2.dilate(img_bin,dil_kernel,iterations=1)
现在,膨胀后的二值图像看起来像这样。
假设最小盒子尺寸为 20*20
line_min_width = 20
寻找水平线
kernal_h = np.ones((1,line_min_width), np.uint8)
img_bin_h = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, kernal_h)
寻找垂直线
kernal_v = np.ones((line_min_width,1), np.uint8)
img_bin_v = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, kernal_v)
合并并添加一个膨胀层来关闭小间隙
img_bin_final=img_bin_h|img_bin_v
final_kernel = np.ones((3,3), np.uint8)
img_bin_final=cv2.dilate(img_bin_final,final_kernel,iterations=1)
应用连通分量分析
ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)
可视化连通分量图像
如您所见,我们检测到一些文本也作为方框,我们可以通过简单的过滤条件轻松删除它们,这里我过滤的条件是区域至少应为 1000 像素。
在检测到的框上绘制矩形。
### 1 and 0 and the background and residue connected components whihc we do not require
for x,y,w,h,area in stats[2:]:
# cv2.putText(image,'box',(x-10,y-10),cv2.FONT_HERSHEY_SIMPLEX, 1.0,(0,255,0), 2)
if area>1000:
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
最终输出图像
此答案基于我使用 OpenCV 在图像中查找 checkboxes/tables 的解决方案。您可以在我位于 Towards Data Science 的 blog 中找到详细解释。
希望这会让您更接近解决方案。
编码愉快:)
-- 编辑 1
进行连通分量可视化的代码
def imshow_components(labels):
### creating a hsv image, with a unique hue value for each label
label_hue = np.uint8(179*labels/np.max(labels))
### making saturation and volume to be 255
empty_channel = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, empty_channel, empty_channel])
### converting the hsv image to BGR image
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
labeled_img[label_hue==0] = 0
### returning the color image for visualising Connected Componenets
return labeled_img
我已经使用 opencv 将数独图像转换为数独网格
现在我想从图像中提取每个框,最好的方法是什么?
据我所知,我正在尝试找到线的交点以找到每个框的角
class SudokuSolverPlay:
def __init__(self, image):
def __preProcess(self, img):
"""return grayscale image"""
def __maskSudoku(self, img):
"""return masked image"""
def __dectactEdge(self, img):
"""return sudoku grid"""
def drawLines(src, dest, iteration=1):
minLineLength = 100
src = cv2.convertScaleAbs(src)
for _ in range(iteration):
lines = cv2.HoughLinesP(image=src, rho=1, theta=np.pi / 180,
threshold=100, lines=np.array([]),
minLineLength=minLineLength, maxLineGap=100)
a, b, c = lines.shape
for i in range(a):
x1, y1, x2, y2 = lines[i][0][0], lines[i][0][1], lines[i][0][2], lines[i][0][3]
cv2.line(dest, (x1, y1), (x2, y2),255, 1, cv2.LINE_AA)
src = cv2.convertScaleAbs(dest)
def findVerticalLines(img):
imgX = cv2.GaussianBlur(img, (5, 5), 0)
kernelx = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 10))
imgY = cv2.Sobel(img, cv2.CV_64F, 1, 0)
imgY = cv2.convertScaleAbs(imgY)
cv2.normalize(imgY, imgY, 0, 255, cv2.NORM_MINMAX)
imgY = cv2.morphologyEx(imgY, cv2.MORPH_CLOSE, kernelx, iterations=1)
return imgY
def findHorizontalLines(img):
"""same as above only args different"""
img1 = np.zeros(img.shape)
edges = cv2.Canny(img, 50, 150, apertureSize=3)
laplacian = cv2.Laplacian(edges, cv2.CV_64F)
drawLines(laplacian, img1, iteration=1)
sby = findVerticalLines(img1)
sbx = findHorizontalLines(img1)
return img1
def solveSudoku(self):
gray = self.__preProcess(self.__originalImg)
masked = self.__maskSudoku(gray)
grid = self.__dectactGrid(masked)
if __name__ == '__main__':
colorImg = cv2.imread('sudoku1.jpg')
solver = SudokuSolverPlay(colorImg)
solver.solveSudoku()
此处findVerticalLines()
和findHorizontalLines()
无法正确地指示水平和垂直线
- original image
- masked image
- Canny edge dictation
- hough line transform
- horizontal lines
- Vertical Lines
一种解决方法是做一个形态学操作,从canny边缘图像中找到垂直和水平线,然后做一个连接的组件分析来找到盒子。 我在下面做了一个示例版本。您可以进一步微调它以使其更好。 我从蒙版图像作为输入开始。
### reading input image
gray_scale=cv2.imread('masked_image.jpg',0)
执行 Canny 边缘检测并添加膨胀层
img_bin = cv2.Canny(gray_scale,50,110)
dil_kernel = np.ones((3,3), np.uint8)
img_bin=cv2.dilate(img_bin,dil_kernel,iterations=1)
现在,膨胀后的二值图像看起来像这样。
假设最小盒子尺寸为 20*20
line_min_width = 20
寻找水平线
kernal_h = np.ones((1,line_min_width), np.uint8)
img_bin_h = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, kernal_h)
寻找垂直线
kernal_v = np.ones((line_min_width,1), np.uint8)
img_bin_v = cv2.morphologyEx(img_bin, cv2.MORPH_OPEN, kernal_v)
合并并添加一个膨胀层来关闭小间隙
img_bin_final=img_bin_h|img_bin_v
final_kernel = np.ones((3,3), np.uint8)
img_bin_final=cv2.dilate(img_bin_final,final_kernel,iterations=1)
应用连通分量分析
ret, labels, stats,centroids = cv2.connectedComponentsWithStats(~img_bin_final, connectivity=8, ltype=cv2.CV_32S)
可视化连通分量图像
如您所见,我们检测到一些文本也作为方框,我们可以通过简单的过滤条件轻松删除它们,这里我过滤的条件是区域至少应为 1000 像素。
在检测到的框上绘制矩形。
### 1 and 0 and the background and residue connected components whihc we do not require
for x,y,w,h,area in stats[2:]:
# cv2.putText(image,'box',(x-10,y-10),cv2.FONT_HERSHEY_SIMPLEX, 1.0,(0,255,0), 2)
if area>1000:
cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),2)
最终输出图像
此答案基于我使用 OpenCV 在图像中查找 checkboxes/tables 的解决方案。您可以在我位于 Towards Data Science 的 blog 中找到详细解释。 希望这会让您更接近解决方案。
编码愉快:)
-- 编辑 1
进行连通分量可视化的代码
def imshow_components(labels):
### creating a hsv image, with a unique hue value for each label
label_hue = np.uint8(179*labels/np.max(labels))
### making saturation and volume to be 255
empty_channel = 255*np.ones_like(label_hue)
labeled_img = cv2.merge([label_hue, empty_channel, empty_channel])
### converting the hsv image to BGR image
labeled_img = cv2.cvtColor(labeled_img, cv2.COLOR_HSV2BGR)
labeled_img[label_hue==0] = 0
### returning the color image for visualising Connected Componenets
return labeled_img