一、完整项目代码
import numpy as np
import argparse
import cv2
#设置参数
ap =argparse.ArgumentParser()
ap.add_argument("-i","--image",required =True,help="Path to the image to be scanned")
args = vars(ap.parse_args())
def order_points(pts):
#一共四个坐标点
rect = np.zeros((4,2),dtype= "float32")
#按顺序找到对应坐标0123分别是 左上,右上,右下,左下
#计算左上,右下
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
#计算右上.左下
diff = np.diff(pts,axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image,pts):
#获取输入坐标点
rect = order_points(pts)
(tl,tr,br,bl)=rect
#计算输入的w和h值 根号下x平方+y平方
widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1]-bl[1]) ** 2))
widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
maxWidth = max(int(widthA),int(widthB))
heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
maxHeight = max(int(heightA), int(heightB))
#变换后对应坐标位置
dst = np.array([[0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1],
[0, maxHeight - 1]], dtype="float32") #3x3的矩阵
#计算变换矩阵
M =cv2.getPerspectiveTransform(rect,dst)
warped =cv2.warpPerspective(image, M ,(maxWidth,maxHeight))
#返回变换后的结果
return warped
def resize(image, width=None,height=None,inter=cv2.INTER_AREA):
dim =None
(h, w)=image.shape[:2]
if width is None and height is None:
return image
if width is None:
r = height / float(h)
dim = (int(w * r), height)
else:
r = width / float(w) #比例
dim = (width, int(h * r))
resized = cv2.resize(image, dim, interpolation=inter)
return resized
#读取输入
image = cv2.imread('E:/anaconda3/picture/receipt.jpg')
#坐标相同变化
ratio = image.shape[0] / 500.0
orig = image.copy()
image = resize(orig, height =500)
#预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #灰度图
gray = cv2.GaussianBlur(gray, (5,5),0) #高斯滤波,用来去除一些噪音点
edged = cv2.Canny(gray, 75, 200) #进行边缘检测,以便后边的轮廓提取
#显示预处理结果
print("STEP 1: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1]
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
# 遍历轮廓
for c in cnts:
# 计算轮廓近似
peri = cv2.arcLength(c, True) #轮廓长度
# C表示输入的点集
# epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数
# True表示封闭的
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
# 4个点的时候就拿出来
if len(approx) == 4: #得到近似 4个点
screenCnt = approx #近似完的结果
break #拿出来
# 展示结果
print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2) #画出轮廓
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 透视变换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 二值处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)
# 展示结果
print("STEP 3: 变换")
cv2.imshow("Original", resize(orig, height=650))
cv2.imshow("Scanned", resize(ref, height=650))
cv2.waitKey(0)
二、具体操作
1、先导入工具包,设置参数(设置图片读取路径)
import numpy as np
import argparse
import cv2
#设置参数
ap =argparse.ArgumentParser()
ap.add_argument("-i","--image",required =True,help="Path to the image to be scanned")
args = vars(ap.parse_args())
2、读取输入,图片读取后,计算其四个坐标点,要做边缘检测就要是坐标有相同的变化。然后,再进行图片片预处理(转灰度图、高斯滤波-为了滤除一些噪音点、边缘检测)
#读取输入
image = cv2.imread('E:/anaconda3/picture/receipt.jpg')
#坐标相同变化
ratio = image.shape[0] / 500.0
orig = image.copy()
image = resize(orig, height =500)
#预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) #灰度图
gray = cv2.GaussianBlur(gray, (5,5),0) #高斯滤波,用来去除一些噪音点
edged = cv2.Canny(gray, 75, 200) #进行边缘检测,以便后边的轮廓提取
#显示预处理结果
print("STEP 1: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()
输出结果:
边缘检测之后,要进行具体的轮廓检测
3、轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[1] #
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]
会检测出这张图上所有的轮廓,但我们需要的是最外边,所以为了计算他外接的东西,所以进行排序前五个最大的轮廓,然后再遍历每一个轮廓:
for c in cnts:
# 计算轮廓近似
peri = cv2.arcLength(c, True) #轮廓长度
# C表示输入的点集
# epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数
# True表示封闭的
approx = cv2.approxPolyDP(c, 0.02 * peri, True) #对轮廓点进行【近似】(c是每一个轮廓/0.02 * peri就是epsilon/)
# 4个点的时候就拿出来
if len(approx) == 4: #得到近似 4个点
screenCnt = approx #近似完的结果
break #拿出来
输出结果:
这样一来,图片中的文字凸显出来,要想得到图片中具体的文字内容就要安装tesseract_ocr
三、tesseract_OCR 的安装与配置
1、安装包网址:https://digi.bib.uni-mannheim.de/tesseract/
选择最新版本下载即可,设置好安装路径(我是E:\opencv)
2、配置环境变量:属性>高级设置>环境变量>上下两个框中path都加进去E:\opencv的路径 就OK
3、WIN+R输入cmd后输入tesseract -v进行测试,得到版本号就成功
4、tesseract XXX.png 得到结果 输入一个图片
5、tesseract XXX.png result 得到图片识别到的文字结果 这就说明 tesseract安装成功
6、在命令栏tesseract的目录下输入pip install pytesseract, 安装pytesseract
7、在anaconda3> lib> site-packges> pytesseract.py >tesseract_cmd 修改为绝对路径即可
绝对路径也就是tesseract安装的具体路径.
到这里tesseract就可以运行来识别图片文字内容了:
from PIL import Image
import pytesseract
import cv2
import os
preprocess = 'blur' #thresh
image = cv2.imread('scan.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
if preprocess == "thresh":
gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
if preprocess == "blur":
gray = cv2.medianBlur(gray, 3)
filename = "{}.png".format(os.getpid())
cv2.imwrite(filename, gray)
text = pytesseract.image_to_string(Image.open(filename))
print(text)
os.remove(filename)
cv2.imshow("Image", image)
cv2.imshow("Output", gray)
cv2.waitKey(0)
就会得到"超市小片上的文字:"
输出的: