如果需要处理的原图及代码,请移步小编的GitHub地址
传送门:请点击我
如果点击有误:https://github.com/LeBron-Jian/ComputerVisionPractice
最近一段时间学习并做的都是对图像进行处理,其实自己也是新手,各种尝试,所以我这个门外汉想总结一下自己学习的东西,图像处理的流程。但是动起笔来想总结,一下却不知道自己要写什么,那就把自己做过的相似图片搜索的流程整理一下,想到什么说什么吧。
一:图片相似度算法(对像素求方差并比对)的学习
1.1 算法逻辑
1.1.1 缩放图片
将需要处理的图片所放到指定尺寸,缩放后图片大小由图片的信息量和复杂度决定。譬如,一些简单的图标之类图像包含的信息量少,复杂度低,可以缩放小一点。风景等复杂场景信息量大,复杂度高就不能缩放太小,容易丢失重要信息。根据自己需求,弹性的缩放。在效率和准确度之间维持平衡。
1.1.2 灰度处理
通常对比图像相似度和颜色关系不是很大,所以处理为灰度图,减少后期计算的复杂度。如果有特殊需求则保留图像色彩。
1.1.3 计算平均值
此处开始,与传统的哈希算法不同:分别依次计算图像每行像素点的平均值,记录每行像素点的平均值。每一个平均值对应着一行的特征。
1.1.4 计算方差
对得到的所有平均值进行计算方差,得到的方差就是图像的特征值。方差可以很好的反应每行像素特征的波动,既记录了图片的主要信息。
1.1.5 比较方差
经过上面的计算之后,每张图都会生成一个特征值(方差)。到此,比较图像相似度就是比较图像生成方差的接近成程度。
一组数据方差的大小可以判断稳定性,多组数据方差的接近程度可以反应数据波动的接近程度。我们不关注方差的大小,只关注两个方差的差值的大小。方差差值越小图像越相似!
1.2 代码:
import cv2
import matplotlib.pyplot as plt
#计算方差
def getss(list):
#计算平均值
avg=sum(list)/len(list)
#定义方差变量ss,初值为0
ss=0
#计算方差
for l in list:
ss+=(l-avg)*(l-avg)/len(list)
#返回方差
return ss
#获取每行像素平均值
def getdiff(img):
#定义边长
Sidelength=30
#缩放图像
img=cv2.resize(img,(Sidelength,Sidelength),interpolation=cv2.INTER_CUBIC)
#灰度处理
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#avglist列表保存每行像素平均值
avglist=[]
#计算每行均值,保存到avglist列表
for i in range(Sidelength):
avg=sum(gray[i])/len(gray[i])
avglist.append(avg)
#返回avglist平均值
return avglist
#读取测试图片
img1=cv2.imread("james.jpg")
diff1=getdiff(img1)
print('img1:',getss(diff1))
#读取测试图片
img11=cv2.imread("durant.jpg")
diff11=getdiff(img11)
print('img11:',getss(diff11))
ss1=getss(diff1)
ss2=getss(diff11)
print("两张照片的方差为:%s"%(abs(ss1-ss2)))
x=range(30)
plt.figure("avg")
plt.plot(x,diff1,marker="*",label="$jiames$")
plt.plot(x,diff11,marker="*",label="$durant$")
plt.title("avg")
plt.legend()
plt.show()
cv2.waitKey(0)
cv2.destroyAllWindows()
两张原图:
图像结果如下:
img1: 357.03162469135805
img11: 202.56193703703704
两张照片的方差为:154.469687654321
实验环境开始设置了图片像素值,而且进行灰度化处理,此方法比对图像相似对不同的图片方差很大,结果很明显,但是对比比较相似,特别相似的图片不适应。
二:图片相似度算法(感知哈希算法)的学习
"感知哈希算法"(Perceptual hash algorithm),它的作用是对每张图片生成一个"指纹"(fingerprint)字符串,然后比较不同图片的指纹。结果越接近,就说明图片越相似。
2.1 算法步骤
2.1.1 缩小尺寸
将图片缩小到8x8的尺寸,总共64个像素。这一步的作用是去除图片的细节,只保留结构、明暗等基本信息,摒弃不同尺寸、比例带来的图片差异。
2.1.2 简化色彩
将缩小后的图片,转为64级灰度。也就是说,所有像素点总共只有64种颜色。
2.1.3 计算平均值
计算所有64个像素的灰度平均值
2.1.4 比较像素的灰度平均值
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0。
2.1.5 计算哈希值
将上一步的比较结果,组合在一起,就构成了一个64位的整数,这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了。
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算"汉明距离"(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片。
此算法参考博客:http://www.ruanyifeng.com/blog/2011/07
/principle_of_similar_image_search.html
但是未实现代码,代码如下:
#!/usr/bin/python
import glob
import os
import sys
from PIL import Image
EXTS = 'jpg', 'jpeg', 'JPG', 'JPEG', 'gif', 'GIF', 'png', 'PNG'
def avhash(im):
if not isinstance(im, Image.Image):
im = Image.open(im)
im = im.resize((8, 8), Image.ANTIALIAS).convert('L')
avg = reduce(lambda x, y: x + y, im.getdata()) / 64.
return reduce(lambda x, (y, z): x | (z << y),
enumerate(map(lambda i: 0 if i < avg else 1, im.getdata())),
0)
def hamming(h1, h2):
h, d = 0, h1 ^ h2
while d:
h += 1
d &= d - 1
return h
if __name__ == '__main__':
if len(sys.argv) <= 1 or len(sys.argv) > 3:
print "Usage: %s image.jpg [dir]" % sys.argv[0]
else:
im, wd = sys.argv[1], '.' if len(sys.argv) < 3 else sys.argv[2]
h = avhash(im)
os.chdir(wd)
images = []
for ext in EXTS:
images.extend(glob.glob('*.%s' % ext))
seq = []
prog = int(len(images) > 50 and sys.stdout.isatty())
for f in images:
seq.append((f, hamming(avhash(f), h)))
if prog:
perc = 100. * prog / len(images)
x = int(2 * perc / 5)
print '\rCalculating... [' + '#' * x + ' ' * (40 - x) + ']',
print '%.2f%%' % perc, '(%d/%d)' % (prog, len(images)),
sys.stdout.flush()
prog += 1
if prog: print
for f, ham in sorted(seq, key=lambda i: i[1]):
print "%d\t%s" % (ham, f)
三:模板匹配
3.1 模板匹配的定义
模板就是一幅已知的小图像,而模板匹配就是在一幅大图像中搜寻目标,已知该图中有要找的目标,且该目标与模板有相同的尺度,方向和图像元素,通过一定的算法可以在图像中找到目标。
模板匹配和卷积原理很像,模板在原图像上开始滑动,计算模板与图像被模板覆盖的地方的差别程度,这个差别程度的计算方法在opencv里有6种,然后将每次计算的结果放入一个矩阵里,作为结果输出。假如原图像是A*B大小,而模板是 a*b大小,则输出结果的矩阵是 (A-a+1)*(B-b+1)。
3.2 模板匹配方法
模板匹配在opencv中的函数为 cv2.matchTemplate(),下面看一下其源码:
def matchTemplate(image, templ, method, result=None, mask=None): # real signature unknown; restored from __doc__
"""
matchTemplate(image, templ, method[, result[, mask]]) -> result
. @brief Compares a template against overlapped image regions.
.
. The function slides through image , compares the overlapped patches of size \f$w \times h\f$ against
. templ using the specified method and stores the comparison results in result . Here are the formulae
. for the available comparison methods ( \f$I\f$ denotes image, \f$T\f$ template, \f$R\f$ result ). The summation
. is done over template and/or the image patch: \f$x' = 0...w-1, y' = 0...h-1\f$
.
. After the function finishes the comparison, the best matches can be found as global minimums (when
. #TM_SQDIFF was used) or maximums (when #TM_CCORR or #TM_CCOEFF was used) using the
. #minMaxLoc function. In case of a color image, template summation in the numerator and each sum in
. the denominator is done over all of the channels and separate mean values are used for each channel.
. That is, the function can take a color template and a color image. The result will still be a
. single-channel image, which is easier to analyze.
.
. @param image Image where the search is running. It must be 8-bit or 32-bit floating-point.
. @param templ Searched template. It must be not greater than the source image and have the same
. data type.
. @param result Map of comparison results. It must be single-channel 32-bit floating-point. If image
. is \f$W \times H\f$ and templ is \f$w \times h\f$ , then result is \f$(W-w+1) \times (H-h+1)\f$ .
. @param method Parameter specifying the comparison method, see #TemplateMatchModes
. @param mask Mask of searched template. It must have the same datatype and size with templ. It is
. not set by default. Currently, only the #TM_SQDIFF and #TM_CCORR_NORMED methods are supported.
"""
pass
下面对模板匹配方法进行解释:
cv2.TM_CCOEFF:系数匹配法,计算相关系数,计算出来的值越大,越相关
cv2.TM_CCOEFF_NORMED:相关系数匹配法,计算归一化相关系数,计算出来的值越接近1,越相关
cv2.TM_CCORR:相关匹配法,计算相关性,计算出来的值越大,越相关
cv2.TM_CCORR_NORMED:归一化相关匹配法,计算归一化相关性,计算出来的值越接近1,越相关
cv2.TM_SQDIFF:平方差匹配法,计算平方不同,计算出来的值越小,越相关
cv2.TM_SQDIFF_NORMED:归一化平方差匹配法,计算归一化平方不同,计算出来的值越接近0,越相关
公式复制opencv官网,如下:
下面实操一下。
3.2 模板匹配一个对象实例
原图是lena.jpg,模板是lena的脸,均可以去我GitHub上拿。
代码:
#_*_coding:utf-8_*_
import cv2
import numpy as np
import matplotlib.pyplot as plt
img_path = 'lena.jpg'
img = cv2.imread(img_path, 0)
template = cv2.imread('face.jpg', 0)
template_h, template_w = template.shape[:2]
print(img.shape) # (263, 263)
print(template.shape) # (110, 85)
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
res = cv2.matchTemplate(img, template, cv2.TM_SQDIFF)
# 函数返回值就是矩阵的最小值,最大值,最小值的索引,最大值的索引。
min_val, max_val, min_index, max_index = cv2.minMaxLoc(res)
# print(min_val, max_val, min_index, max_index)
# 39168.0 74403584.0 (107, 89) (159, 62)
for meth in methods:
img2 = img.copy()
# 匹配方法的真值
method = eval(meth)
# print(meth, method)
'''
cv2.TM_CCOEFF 4
cv2.TM_CCOEFF_NORMED 5
cv2.TM_CCORR 2
cv2.TM_CCORR_NORMED 3
cv2.TM_SQDIFF 0
cv2.TM_SQDIFF_NORMED 1
'''
res = cv2.matchTemplate(img, template, method)
# 函数返回值就是矩阵的最小值,最大值,最小值的索引,最大值的索引。
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
# 如果是平方差匹配 TM_SQDIFF 或归一化平方差匹配 TM_SQDIFF_NORMED,取最小值
if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
top_left = min_loc
else:
top_left = max_loc
bottom_right = (top_left[0] + template_w, top_left[1] + template_h)
# 画矩形
cv2.rectangle(img2, top_left, bottom_right, 255, 2)
plt.subplot(121), plt.imshow(res, cmap='gray')
plt.xticks([]), plt.yticks([]) # 隐藏坐标轴
plt.subplot(122), plt.imshow(img2, cmap='gray')
plt.xticks([]), plt.yticks([])
plt.suptitle(meth)
plt.show()
效果如下:
3.3 模板匹配多个对象
有时候,你需要匹配的模板在图像中多次出现,那么就需要多对象匹配。多对象匹配的原理很简单,因为opencv里的模板匹配里的每一处和模板进行对比,所以同一个模板下,多对象匹配情况下,结果矩阵里会有好几个值,和最大(小)值接近,如果我们设置一个阈值,在这个阈值以上(以下)的值都提取出来,再分别得到他们的坐标,理论上只要这个阈值设置的恰当,就可以将多对象都匹配出来。
原图是超级玛丽的一张截图,模板是金币,也可以去我GitHub上拿。
这种方法的方框要 粗一点,因为我们定义的 threshold 为 0.8,(只针对代码中的 cv2.TM_CCOEFF_NORMED 匹配方法),loc检测出来有 9个点,相当于画了三次框,所以要粗一点。
代码:
#_*_coding:utf-8_*_
import cv2
import numpy as np
import matplotlib.pyplot as plt
img_path = 'mario.jpg'
img_rgb = cv2.imread(img_path)
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
template_h, template_w = template.shape[:2]
print(img_gray.shape) # (207, 225)
print(template.shape) # (27, 16)
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
# 取匹配程度大于 80% 的坐标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]): # *表示可选参数
bottom_right = (pt[0] + template_w, pt[1] + template_h)
cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)
cv2.destroyAllWindows()
效果如下:
3.4 cv2.minMaxLoc()函数用法
cv2.minMaxLoc() 函数功能:假设有一个矩阵 a,现在需要求这个矩阵的最小值,最大值,并得到最大值,最小值的索引。就可以使用此函数。
cv2.minMaxLoc()函数源码如下:
def minMaxLoc(src, mask=None): # real signature unknown; restored from __doc__
"""
minMaxLoc(src[, mask]) -> minVal, maxVal, minLoc, maxLoc
. @brief Finds the global minimum and maximum in an array.
.
. The function cv::minMaxLoc finds the minimum and maximum element values and their positions. The
. extremums are searched across the whole array or, if mask is not an empty array, in the specified
. array region.
.
. The function do not work with multi-channel arrays. If you need to find minimum or maximum
. elements across all the channels, use Mat::reshape first to reinterpret the array as
. single-channel. Or you may extract the particular channel using either extractImageCOI , or
. mixChannels , or split .
. @param src input single-channel array.
. @param minVal pointer to the returned minimum value; NULL is used if not required.
. @param maxVal pointer to the returned maximum value; NULL is used if not required.
. @param minLoc pointer to the returned minimum location (in 2D case); NULL is used if not required.
. @param maxLoc pointer to the returned maximum location (in 2D case); NULL is used if not required.
. @param mask optional mask used to select a sub-array.
. @sa max, min, compare, inRange, extractImageCOI, mixChannels, split, Mat::reshape
"""
pass
函数返回值就是矩阵的最小值,最大值,最小值的索引,最大值的索引。
下面举个例子:
#_*_coding:utf-8_*_
import cv2
import numpy as np
a = np.array([[1,2,3,4], [5,6,7,8]])
min_val, max_val, min_index, max_index = cv2.minMaxLoc(a)
print(min_val, max_val, min_index, max_index)
# 1.0 8.0 (0, 0) (3, 1)
3.5 实战——信用卡数字识别
关于图片和代码,可以去我的GitHub中拿,GitHub地址在文章上面。
这一实战的目的是识别出信用卡中的数字,也就是我们的卡号,信用卡如下:
而我们的模板图片如下:
对模板图像处理过程:
首先对图像二值化,并对二值化的图片进行逆运算:
然后拿到原二值图像的轮廓:
最后将数字对应的轮廓存入字典中,方便后面对比。
对信用卡进行图像处理,这里不再一一赘述,均在代码中,只展示图像的处理过程及其结果:
灰度化信用卡图像:
对信用卡图像进行顶帽处理:
对图像进行sobel算子的X方向:
再进一步处理:
对图像进行膨胀腐蚀:
再对图像进行二值化:
找到原图的地址:
设置筛选范围,找到我们需要的区域:
最后展示在原图:
也可以将结果展示在编译器:
Credit Card Type: Visa
Credit Card #: 4000123456789010
完整代码如下:
#_*_coding:utf-8_*_
import cv2
import numpy as np
# 指定信用卡类型
FIRST_NUMBER = {
'3': "Amerian Express",
'4': "Visa",
'5': 'MasterCard',
'6': 'Discover Card'
}
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
def sort_contours(cnts, method='left-to-right'):
reverse = False
i = 0
if method == 'right-to-left' or method == 'bottom-to-top':
reverse = True
if method == 'top-to-bottom' or method == ' bottom-to-top':
i = 1
# 用一个最小的矩形,把找到的形状包起来 x, y, h, w
boundingBoxes = [cv2.boundingRect(c) for c in cnts]
# #zip(*)相当于解压,是zip的逆过程
(cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
key=lambda b: b[1][i], reverse=reverse))
return cnts, boundingBoxes
def myresize(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
def trmplate_processing(template_image):
# 读取模板图像
template_img = cv2.imread(template_image)
# cv_show('template_img', template_img)
gray = cv2.cvtColor(template_img, cv2.COLOR_BGR2GRAY)
# cv_show('gray', gray)
# 二值图像
ref = cv2.threshold(gray, 10, 255, cv2.THRESH_BINARY_INV)[1]
# cv_show('ref', ref)
# 计算轮廓
# cv2.findContours() 函数接受的参数为二值图,即黑白的(不是灰度图)
# cv2.RETR_EXTERNAL 只检测外轮廓 cv2.CHAIN_APPROX_SIMPLE 只保留终点坐标
# 返回的list中每个元素都是图像中的一个轮廓
refCnts, hierarchy = cv2.findContours(ref.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
drawimg = template_img.copy()
cv2.drawContours(drawimg, refCnts, -1, (0, 0, 255), 3)
# cv_show('drawimg', drawimg)
# print(np.array(refCnts).shape) # (10,)
# 排序,从左到右,从上到下
refCnts1 = sort_contours(refCnts, method='left-to-right')[0]
digits = {}
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts1):
# 计算外接矩阵并且resize成合适大小
(x, y, w, h) = cv2.boundingRect(c)
roi = ref[y:y+h, x:x+w]
roi = cv2.resize(roi, (57, 88))
# 每一个数字对应每个模板
digits[i] = roi
# print(digits)
return digits
def origin_img_processing(origin_image, digits):
# 读入输入图像,并进行预处理
img = cv2.imread(origin_image)
# cv_show('img', img)
# print(img.shape) # (368, 583, 3)
img = myresize(img, width=300)
# print(img.shape) # (189, 300, 3)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# cv_show('gray', gray)
# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
# 礼帽操作,突出更明显的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
# cv_show('tophat', tophat)
# ksize = -1 相当于用 3*3 的 ksize=-1
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
# 下面np函数等价于 cv2.convertScaleAbs(gradX)
gradX = np.absolute(gradX)
# cv_show('gradX', gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255*((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype('uint8')
# print(np.array(gradX).shape) # (189, 300)
# cv_show('gradX', gradX)
# 通过闭操作(先膨胀,再腐蚀)将数字连在一起
gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
# cv_show('gradX', gradX)
# THRESH_OTSU 会自动寻找合适的阈值,适合双峰,需把阈值参数设置为0
thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
# cv_show('thresh', thresh)
# 再来一个闭操作 先膨胀后腐蚀
thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
# cv_show('thresh_again', thresh)
#计算轮廓
threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cur_img = img.copy()
cv2.drawContours(cur_img, threshCnts, -1, (0, 0, 255), 3)
# cv_show('cur_image', cur_img)
locs = []
# 遍历轮廓
for (i, c) in enumerate(threshCnts):
# 计算矩形
(x, y, w, h) = cv2.boundingRect(c)
ar = w / float(h)
# 选择合适的区域,根据实际任务来,这里的基本都是四个数字一组
if ar > 2.5 and ar<4.0:
if (w > 40 and w < 55) and (h > 10 and h < 20):
# 符合的留下来
locs.append((x, y, w, h))
# 将符合的轮廓从左到右排序
locs = sorted(locs, key=lambda x:x[0])
output = []
# 遍历轮廓中每一个数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):
# initialize the list of group digits
groupOutput = []
# 根据坐标提取每一个组
group = gray[gY - 5: gY + gH + 6, gX - 5:gX + gW + 5]
# cv_show('group', group)
# 预处理
group1 = cv2.threshold(group, 0, 255, cv2.THRESH_BINARY|cv2.THRESH_OTSU)[1]
# cv_show('group', group)
# res = np.hstack((group, group1))
# cv_show('group & threshold', res)
# 计算每一组的轮廓
digitCnts, hierarchy = cv2.findContours(group1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
digitCnts = sort_contours(digitCnts, method='left-to-right')[0]
# 计算每一组中的每一个数值
for c in digitCnts:
# 找到当前数值的轮廓,resize成合适的大小
(x, y, w, h) = cv2.boundingRect(c)
roi = group1[y:y+h, x:x+w]
roi = cv2.resize(roi, (57, 88))
# cv_show('roi', roi)
# 计算匹配得分
scores = []
# 在模板中计算每一个得分
for (digit, digitROI) in digits.items():
# 模板匹配
result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)
(_, score, _, _) = cv2.minMaxLoc(result)
scores.append(score)
# 得到最合适的数字
groupOutput.append(str(np.argmax(scores)))
# 画出来
cv2.rectangle(img, (gX-5, gY-5), (gX+gW+5, gY+gH+5), (0, 0, 255), 1)
cv2.putText(img, "".join(groupOutput), (gX, gY-15),
cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)
# 得到结果
output.extend(groupOutput)
# 打印结果
print('Credit Card Type: {}'.format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv_show('Image', img)
if __name__ == '__main__':
origin_image = r'images/credit_card_01.png'
template_image = r'images/ocr_a_reference.png'
digits = trmplate_processing(template_image)
origin_img_processing(origin_image, digits)
部分代码解释:
img = cv2.imread("card.png", 1)
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.imread("temp.png", 0)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
locs = np.where(res >=0.985)
for loc in zip(*locs[::-1]):
img =cv2.rectangle(img, loc, (loc[0] + w, loc[1] + h), (0, 0, 255), 3)
这里我跳出一些许多代码,直接找到关键代码:
首先对于 locs = np.where(res > threshold) 进行分析,threshold 就是我们设定的阈值,res>thrshold 就是将结果array 里符合条件的数值换成True,反之换成 False。
其次对于 np.where 的作用就是将 true的索引输出到 Loc变量里面。
loc[::-1] 将输出的索引换成 x, y 坐标,因为索引和 x , y 坐标是正好相反的,所以要对换下位置。
四:关于OpenCV的安装
OpenCV的全称open Sourse Computer Vision Library ,是一个跨平台的计算机视觉库,OpenCV可用于开发实时的图像处理,计算机视觉以及模式识别的程序。
OpenCV是用C++语言编写,它的主要接口也是C++语言,但是依然保留了大量的C语言接口,该库也有大量的Python,Java和MATLAB的接口,另外,一个使用CUDA的GPU接口也用于2010.9 开始实现。
3.1 为什么使用Python+OpenCV
虽然python很强大,而且也有自己的图像处理库PIL,但是相对于OpenCV来讲,它还是弱小很多。跟很多开源软件一样OpenCV也提供了完善的python接口,非常便于调用。OpenCV 的稳定版是2.4.8,最新版是3.0,包含了超过2500个算法和函数,几乎任何一个能想到的成熟算法都可以通过调用OpenCV的函数来实现,超级方便。
3.2 import cv2发生错误的解决方案
错误如下:
1,进入cmd控制台,查看python版本
2 根据自己用的python版本,下载对应的OpenCV
3,下载numpy,对应的版本
cp36代表着匹配python3.6版本。
win32、amd64代表着32位、64位系统。
4,安装OpenCV,下载下来是一个whl格式文件,把此文件放在安装的文件名下,直接安装。
就这样安装成功。
于2020.10.12 整理
参考文献:https://blog.csdn.net/wsp_1138886114/article/details/81368890
公式地址如下:https://docs.opencv.org/3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d