pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)...

如果需要处理的原图及代码,请移步小编的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()

两张原图:

1226410-20181011202007860-247616267.png

1226410-20181011201608008-1027505445.png

图像结果如下:

img1: 357.03162469135805

img11: 202.56193703703704

两张照片的方差为:154.469687654321

1226410-20181011201754547-1118765723.png

实验环境开始设置了图片像素值,而且进行灰度化处理,此方法比对图像相似对不同的图片方差很大,结果很明显,但是对比比较相似,特别相似的图片不适应。

二:图片相似度算法(感知哈希算法)的学习

"感知哈希算法"(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官网,如下:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第1张图片

下面实操一下。

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()

效果如下:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第2张图片

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第3张图片

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第4张图片

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第5张图片

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第6张图片

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第7张图片

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()

效果如下:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第8张图片

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地址在文章上面。

这一实战的目的是识别出信用卡中的数字,也就是我们的卡号,信用卡如下:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第9张图片

而我们的模板图片如下:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第10张图片

对模板图像处理过程:

首先对图像二值化,并对二值化的图片进行逆运算:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第11张图片

然后拿到原二值图像的轮廓:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第12张图片

最后将数字对应的轮廓存入字典中,方便后面对比。

对信用卡进行图像处理,这里不再一一赘述,均在代码中,只展示图像的处理过程及其结果:

灰度化信用卡图像:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第13张图片

对信用卡图像进行顶帽处理:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第14张图片

对图像进行sobel算子的X方向:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第15张图片

再进一步处理:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第16张图片

对图像进行膨胀腐蚀:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第17张图片

再对图像进行二值化:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第18张图片  进一步二值化:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第19张图片

找到原图的地址:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第20张图片

设置筛选范围,找到我们需要的区域:

1226410-20201103105522137-1085635001.png

1226410-20201103105537609-1037377176.png

1226410-20201103105548398-613020604.png

1226410-20201103105558219-321420910.png

最后展示在原图:

pythonopencv算法_深入学习OpenCV中图像相似度的算法(比对像素方差,感知哈希算法,模板匹配(OCR数字匹配),OpenCV的安装)..._第21张图片

也可以将结果展示在编译器:

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发生错误的解决方案

错误如下:

1226410-20180814145559259-388383145.png

1,进入cmd控制台,查看python版本

1226410-20180814145948474-253056895.png

2 根据自己用的python版本,下载对应的OpenCV

1226410-20181006120009041-1853978328.png

3,下载numpy,对应的版本

cp36代表着匹配python3.6版本。

win32、amd64代表着32位、64位系统。

4,安装OpenCV,下载下来是一个whl格式文件,把此文件放在安装的文件名下,直接安装。

1226410-20180814152141056-845204699.png

就这样安装成功。

于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

你可能感兴趣的:(pythonopencv算法)