检测图像中的矩形并裁剪
detect rectangle in image and crop
我有很多矩形(小的)内手写数字的扫描图像。
请帮我裁剪每张包含数字的图像,并通过为每一行指定相同的名称来保存它们。
import cv2
img = cv2.imread('Data\Scan_20170612_4.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200)
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
i = 0
for c in contours:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.09 * peri, True)
if len(approx) == 4:
screenCnt = approx
cv2.drawContours(img, [screenCnt], -1, (0, 255, 0), 3)
cv2.imwrite('cropped\' + str(i) + '_img.jpg', img)
i += 1
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
您正在使用 cv2.RETR_LIST 在图像中查找轮廓。为了使您的图像获得更好的输出,请使用 cv2.RETR_EXTERNAL。在使用第一个 从图像中删除黑色 border 线之前。
cv2.RETR_LIST 为您提供图像的所有轮廓列表
cv2.RETR_EXTERNAL 只给出外部或外轮廓,不给出内部轮廓
将行更改为
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
如果您尝试的话,这是一件容易的事情。这是我的输出-(图像及其一小部分)
我做了什么?
- 先调整图片大小,因为它在我的屏幕上太大了
- 腐蚀、膨胀以去除小点并加粗线条
- 图像阈值
- 洪水填充,从正确的点开始
- 反转洪水填充
- 查找等高线并一次绘制一个,其范围大约为
矩形上的面积。对于我调整大小的 (500x500) 图像,我将 Area of
轮廓在 500 到 2500 范围内(无论如何都要反复试验)。
- 找到边界矩形并从主图像中裁剪该蒙版。
然后用正确的名字保存那个片段——我没有这样做。
也许有更简单的方法,但我喜欢这个。不放代码是因为
我把它弄得很笨拙。如果你还需要的话会放的。
这是一次找到每个轮廓时蒙版的样子
代码:
import cv2;
import numpy as np;
# Run the code with the image name, keep pressing space bar
# Change the kernel, iterations, Contour Area, position accordingly
# These values work for your present image
img = cv2.imread("your_image.jpg", 0);
h, w = img.shape[:2]
kernel = np.ones((15,15),np.uint8)
e = cv2.erode(img,kernel,iterations = 2)
d = cv2.dilate(e,kernel,iterations = 1)
ret, th = cv2.threshold(d, 150, 255, cv2.THRESH_BINARY_INV)
mask = np.zeros((h+2, w+2), np.uint8)
cv2.floodFill(th, mask, (200,200), 255); # position = (200,200)
out = cv2.bitwise_not(th)
out= cv2.dilate(out,kernel,iterations = 3)
cnt, h = cv2.findContours(out,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(cnt)):
area = cv2.contourArea(cnt[i])
if(area>10000 and area<100000):
mask = np.zeros_like(img)
cv2.drawContours(mask, cnt, i, 255, -1)
x,y,w,h = cv2.boundingRect(cnt[i])
crop= img[ y:h+y,x:w+x]
cv2.imshow("snip",crop )
if(cv2.waitKey(0))==27:break
cv2.destroyAllWindows()
这是我的版本
import cv2
import numpy as np
fileName = ['9','8','7','6','5','4','3','2','1','0']
img = cv2.imread('Data\Scan_20170612_17.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(gray,kernel,iterations = 2)
kernel = np.ones((4,4),np.uint8)
dilation = cv2.dilate(erosion,kernel,iterations = 2)
edged = cv2.Canny(dilation, 30, 200)
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
rects = [cv2.boundingRect(cnt) for cnt in contours]
rects = sorted(rects,key=lambda x:x[1],reverse=True)
i = -1
j = 1
y_old = 5000
x_old = 5000
for rect in rects:
x,y,w,h = rect
area = w * h
if area > 47000 and area < 70000:
if (y_old - y) > 200:
i += 1
y_old = y
if abs(x_old - x) > 300:
x_old = x
x,y,w,h = rect
out = img[y+10:y+h-10,x+10:x+w-10]
cv2.imwrite('cropped\' + fileName[i] + '_' + str(j) + '.jpg', out)
j+=1
看起来位置定义得很好..它应该不需要 opencv..只是将图像分割成图块。
我想到了 Imagemagik。我经常使用它来创建类似的布局..从单个瓷砖..(与您正在做的相反)
它可以在命令行执行此操作。
我有很多矩形(小的)内手写数字的扫描图像。
请帮我裁剪每张包含数字的图像,并通过为每一行指定相同的名称来保存它们。
import cv2
img = cv2.imread('Data\Scan_20170612_4.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
edged = cv2.Canny(gray, 30, 200)
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
i = 0
for c in contours:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.09 * peri, True)
if len(approx) == 4:
screenCnt = approx
cv2.drawContours(img, [screenCnt], -1, (0, 255, 0), 3)
cv2.imwrite('cropped\' + str(i) + '_img.jpg', img)
i += 1
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
您正在使用 cv2.RETR_LIST 在图像中查找轮廓。为了使您的图像获得更好的输出,请使用 cv2.RETR_EXTERNAL。在使用第一个 从图像中删除黑色 border 线之前。
cv2.RETR_LIST 为您提供图像的所有轮廓列表
cv2.RETR_EXTERNAL 只给出外部或外轮廓,不给出内部轮廓
将行更改为
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
如果您尝试的话,这是一件容易的事情。这是我的输出-(图像及其一小部分)
我做了什么?
- 先调整图片大小,因为它在我的屏幕上太大了
- 腐蚀、膨胀以去除小点并加粗线条
- 图像阈值
- 洪水填充,从正确的点开始
- 反转洪水填充
- 查找等高线并一次绘制一个,其范围大约为 矩形上的面积。对于我调整大小的 (500x500) 图像,我将 Area of 轮廓在 500 到 2500 范围内(无论如何都要反复试验)。
- 找到边界矩形并从主图像中裁剪该蒙版。
然后用正确的名字保存那个片段——我没有这样做。
也许有更简单的方法,但我喜欢这个。不放代码是因为 我把它弄得很笨拙。如果你还需要的话会放的。
这是一次找到每个轮廓时蒙版的样子
代码:
import cv2;
import numpy as np;
# Run the code with the image name, keep pressing space bar
# Change the kernel, iterations, Contour Area, position accordingly
# These values work for your present image
img = cv2.imread("your_image.jpg", 0);
h, w = img.shape[:2]
kernel = np.ones((15,15),np.uint8)
e = cv2.erode(img,kernel,iterations = 2)
d = cv2.dilate(e,kernel,iterations = 1)
ret, th = cv2.threshold(d, 150, 255, cv2.THRESH_BINARY_INV)
mask = np.zeros((h+2, w+2), np.uint8)
cv2.floodFill(th, mask, (200,200), 255); # position = (200,200)
out = cv2.bitwise_not(th)
out= cv2.dilate(out,kernel,iterations = 3)
cnt, h = cv2.findContours(out,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
for i in range(len(cnt)):
area = cv2.contourArea(cnt[i])
if(area>10000 and area<100000):
mask = np.zeros_like(img)
cv2.drawContours(mask, cnt, i, 255, -1)
x,y,w,h = cv2.boundingRect(cnt[i])
crop= img[ y:h+y,x:w+x]
cv2.imshow("snip",crop )
if(cv2.waitKey(0))==27:break
cv2.destroyAllWindows()
这是我的版本
import cv2
import numpy as np
fileName = ['9','8','7','6','5','4','3','2','1','0']
img = cv2.imread('Data\Scan_20170612_17.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.bilateralFilter(gray, 11, 17, 17)
kernel = np.ones((5,5),np.uint8)
erosion = cv2.erode(gray,kernel,iterations = 2)
kernel = np.ones((4,4),np.uint8)
dilation = cv2.dilate(erosion,kernel,iterations = 2)
edged = cv2.Canny(dilation, 30, 200)
_, contours, hierarchy = cv2.findContours(edged, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
rects = [cv2.boundingRect(cnt) for cnt in contours]
rects = sorted(rects,key=lambda x:x[1],reverse=True)
i = -1
j = 1
y_old = 5000
x_old = 5000
for rect in rects:
x,y,w,h = rect
area = w * h
if area > 47000 and area < 70000:
if (y_old - y) > 200:
i += 1
y_old = y
if abs(x_old - x) > 300:
x_old = x
x,y,w,h = rect
out = img[y+10:y+h-10,x+10:x+w-10]
cv2.imwrite('cropped\' + fileName[i] + '_' + str(j) + '.jpg', out)
j+=1
看起来位置定义得很好..它应该不需要 opencv..只是将图像分割成图块。 我想到了 Imagemagik。我经常使用它来创建类似的布局..从单个瓷砖..(与您正在做的相反) 它可以在命令行执行此操作。