改进图片以检测区域内的字符
Improve a picture to detect the characters within an area
我的目标是检测这类图像上的字符。
我需要改进图像以便 Tesseract 能够更好地识别,可能需要执行以下步骤:
- 旋转图像,使蓝色矩形处于水平状态[需要帮助]
- 根据蓝色矩形裁剪图像[需要帮助]
- 应用阈值过滤器和高斯模糊
使用Tesseract检测字符
img = Image.open('grid.jpg')
image = np.array(img.convert("RGB"))[:, :, ::-1].copy()
# Need to rotate the image here and fill the blanks
# Need to crop the image here
# Gray the image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# Otsu's thresholding
ret3, th3 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Gaussian Blur
blur = cv2.GaussianBlur(th3, (5, 5), 0)
# Save the image
cv2.imwrite("preproccessed.jpg", blur)
# Apply the OCR
pytesseract.pytesseract.tesseract_cmd = r'C:/Program Files (x86)/Tesseract-OCR/tesseract.exe'
tessdata_dir_config = r'--tessdata-dir "C:/Program Files (x86)/Tesseract-OCR/tessdata" --psm 6'
preprocessed = Image.open('preproccessed.jpg')
boxes = pytesseract.image_to_data(preprocessed, config=tessdata_dir_config)
这是我得到的输出图像,它不适合 OCR:
OCR 问题:
- 蓝色矩形有时会被识别为字符,这就是我要裁剪图像的原因
- 有时 Tesseract 将一行中的字符识别为单词 (GCVDRTEUQCEBURSIDEEC),有时将其识别为单个字母。我希望它永远是一个词。
- 右下角的小金字塔被识别为字符
欢迎提出任何其他提高识别度的建议
我认为移除颜色比裁剪更好。
可以用opencv来完成见:
这是一种继续进行的方法...
转换为 HSV,然后从每个角开始,向图片的中间前进,寻找离每个角最近的像素,该像素有点饱和,并且色调与您周围的蓝色矩形相匹配。这将为您提供标记为红色的 4 点:
现在使用透视变换将每个点移动到角落,使图像呈直线。我使用了 ImageMagick,但您应该能够看到我将坐标 (210,51) 处的左上角红点转换为 (0,0) 处新图像的左上角。同样,位于 (1754,19) 的右上角红点会移动到 (2064,0)。终端中的 ImageMagick 命令是:
convert wordsearch.jpg \
-distort perspective '210,51,0,0 1754,19,2064,0 238,1137,0,1161 1776,1107,2064,1161' result.jpg
结果是:
下一个问题是光线不均匀 - 即左下角比图像的其余部分暗。为了抵消这一点,我克隆了图像并对其进行模糊处理以去除高频(只是一个盒子模糊,或者盒子平均就可以)所以它现在代表缓慢变化的照明。然后我从中减去图像,这样我就有效地去除了背景变化,只留下高频的东西——比如你的字母。然后我将结果归一化,使白人变白,黑人变黑,阈值为 50%。
convert result.jpg -colorspace gray \( +clone -blur 50x50 \) \
-compose difference -composite -negate -normalize -threshold 50% final.jpg
如果您知道字体和字母,或者如果您不知道,则结果应该适合模板匹配。
以下是我识别字符的步骤:
(1) detect the blue in hsv space, approx the inner blur contour and sort the corner points:
(2) find persprctive transform matrix and do perspective transform
(3) threshold it (and find characters)
(4) use `mnist` algorithms to recognize the chars
step (1) find the corners of the blur rect
Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
step (2) crop
step (3) threshold (and find the chars)
step (4) on working...
这里有一个稍微不同的方法,使用 pyvips。
如果图像只是旋转(即很少或没有透视),您可以使用 FFT 来找到旋转的角度。漂亮、规则的字符网格将在变换上产生一组清晰的线条。它应该非常坚固。这是对整个图像进行 FFT,但如果您想要更快的速度,可以先将其缩小一点。
import sys
import pyvips
image = pyvips.Image.new_from_file(sys.argv[1])
# to monochrome, take the fft, wrap the origin to the centre, get magnitude
fft = image.colourspace('b-w').fwfft().wrap().abs()
制作:
要找到直线的角度,从极坐标转为直角坐标并寻找水平线:
def to_rectangular(image):
xy = pyvips.Image.xyz(image.width, image.height)
xy *= [1, 360.0 / image.height]
index = xy.rect()
scale = min(image.width, image.height) / float(image.width)
index *= scale / 2.0
index += [image.width / 2.0, image.height / 2.0]
return image.mapim(index)
# sum of columns, sum of rows
cols, rows = to_rectangular(fft).project()
制作:
投影为:
然后就是找峰旋转:
# blur the rows projection a bit, then get the maxpos
v, x, y = rows.gaussblur(10).maxpos()
# and turn to an angle in degrees we should counter-rotate by
angle = 270 - 360 * y / rows.height
image = image.rotate(angle)
为了裁剪,我再次进行水平和垂直投影,然后搜索 B > G 的峰。
cols, rows = image.project()
h = (cols[2] - cols[1]) > 10000
v = (rows[2] - rows[1]) > 10000
# search in from the edges for the first non-zero value
cols, rows = h.profile()
left = rows.avg()
cols, rows = h.fliphor().profile()
right = h.width - rows.avg()
width = right - left
cols, rows = v.profile()
top = cols.avg()
cols, rows = v.flipver().profile()
bottom = v.height - cols.avg()
height = bottom - top
# move the crop in by a margin
margin = 10
left += margin
top += margin
width -= 2 * margin
height -= 2 * margin
# and crop!
image = image.crop(left, top, width, height)
制作:
最后去除背景,用大半径模糊并减去:
image = image.colourspace('b-w').gaussblur(70) - image
制作:
我的目标是检测这类图像上的字符。
我需要改进图像以便 Tesseract 能够更好地识别,可能需要执行以下步骤:
- 旋转图像,使蓝色矩形处于水平状态[需要帮助]
- 根据蓝色矩形裁剪图像[需要帮助]
- 应用阈值过滤器和高斯模糊
使用Tesseract检测字符
img = Image.open('grid.jpg') image = np.array(img.convert("RGB"))[:, :, ::-1].copy() # Need to rotate the image here and fill the blanks # Need to crop the image here # Gray the image gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # Otsu's thresholding ret3, th3 = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) # Gaussian Blur blur = cv2.GaussianBlur(th3, (5, 5), 0) # Save the image cv2.imwrite("preproccessed.jpg", blur) # Apply the OCR pytesseract.pytesseract.tesseract_cmd = r'C:/Program Files (x86)/Tesseract-OCR/tesseract.exe' tessdata_dir_config = r'--tessdata-dir "C:/Program Files (x86)/Tesseract-OCR/tessdata" --psm 6' preprocessed = Image.open('preproccessed.jpg') boxes = pytesseract.image_to_data(preprocessed, config=tessdata_dir_config)
这是我得到的输出图像,它不适合 OCR:
OCR 问题:
- 蓝色矩形有时会被识别为字符,这就是我要裁剪图像的原因
- 有时 Tesseract 将一行中的字符识别为单词 (GCVDRTEUQCEBURSIDEEC),有时将其识别为单个字母。我希望它永远是一个词。
- 右下角的小金字塔被识别为字符
欢迎提出任何其他提高识别度的建议
我认为移除颜色比裁剪更好。
可以用opencv来完成见:
这是一种继续进行的方法...
转换为 HSV,然后从每个角开始,向图片的中间前进,寻找离每个角最近的像素,该像素有点饱和,并且色调与您周围的蓝色矩形相匹配。这将为您提供标记为红色的 4 点:
现在使用透视变换将每个点移动到角落,使图像呈直线。我使用了 ImageMagick,但您应该能够看到我将坐标 (210,51) 处的左上角红点转换为 (0,0) 处新图像的左上角。同样,位于 (1754,19) 的右上角红点会移动到 (2064,0)。终端中的 ImageMagick 命令是:
convert wordsearch.jpg \
-distort perspective '210,51,0,0 1754,19,2064,0 238,1137,0,1161 1776,1107,2064,1161' result.jpg
结果是:
下一个问题是光线不均匀 - 即左下角比图像的其余部分暗。为了抵消这一点,我克隆了图像并对其进行模糊处理以去除高频(只是一个盒子模糊,或者盒子平均就可以)所以它现在代表缓慢变化的照明。然后我从中减去图像,这样我就有效地去除了背景变化,只留下高频的东西——比如你的字母。然后我将结果归一化,使白人变白,黑人变黑,阈值为 50%。
convert result.jpg -colorspace gray \( +clone -blur 50x50 \) \
-compose difference -composite -negate -normalize -threshold 50% final.jpg
如果您知道字体和字母,或者如果您不知道,则结果应该适合模板匹配。
以下是我识别字符的步骤:
(1) detect the blue in hsv space, approx the inner blur contour and sort the corner points:
(2) find persprctive transform matrix and do perspective transform
(3) threshold it (and find characters)
(4) use `mnist` algorithms to recognize the chars
step (1) find the corners of the blur rect
Choosing the correct upper and lower HSV boundaries for color detection with`cv::inRange` (OpenCV)
step (2) crop
step (3) threshold (and find the chars)
step (4) on working...
这里有一个稍微不同的方法,使用 pyvips。
如果图像只是旋转(即很少或没有透视),您可以使用 FFT 来找到旋转的角度。漂亮、规则的字符网格将在变换上产生一组清晰的线条。它应该非常坚固。这是对整个图像进行 FFT,但如果您想要更快的速度,可以先将其缩小一点。
import sys
import pyvips
image = pyvips.Image.new_from_file(sys.argv[1])
# to monochrome, take the fft, wrap the origin to the centre, get magnitude
fft = image.colourspace('b-w').fwfft().wrap().abs()
制作:
要找到直线的角度,从极坐标转为直角坐标并寻找水平线:
def to_rectangular(image):
xy = pyvips.Image.xyz(image.width, image.height)
xy *= [1, 360.0 / image.height]
index = xy.rect()
scale = min(image.width, image.height) / float(image.width)
index *= scale / 2.0
index += [image.width / 2.0, image.height / 2.0]
return image.mapim(index)
# sum of columns, sum of rows
cols, rows = to_rectangular(fft).project()
制作:
投影为:
然后就是找峰旋转:
# blur the rows projection a bit, then get the maxpos
v, x, y = rows.gaussblur(10).maxpos()
# and turn to an angle in degrees we should counter-rotate by
angle = 270 - 360 * y / rows.height
image = image.rotate(angle)
为了裁剪,我再次进行水平和垂直投影,然后搜索 B > G 的峰。
cols, rows = image.project()
h = (cols[2] - cols[1]) > 10000
v = (rows[2] - rows[1]) > 10000
# search in from the edges for the first non-zero value
cols, rows = h.profile()
left = rows.avg()
cols, rows = h.fliphor().profile()
right = h.width - rows.avg()
width = right - left
cols, rows = v.profile()
top = cols.avg()
cols, rows = v.flipver().profile()
bottom = v.height - cols.avg()
height = bottom - top
# move the crop in by a margin
margin = 10
left += margin
top += margin
width -= 2 * margin
height -= 2 * margin
# and crop!
image = image.crop(left, top, width, height)
制作:
最后去除背景,用大半径模糊并减去:
image = image.colourspace('b-w').gaussblur(70) - image
制作: