OpenCV实战(1)

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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()

  两张原图:

  图像结果如下:

?
1
2
3
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,就说明这是两张不同的图片。

?
1
2
3
4
    此算法参考博客:http://www.ruanyifeng.com/blog/2011/07
    /principle_of_similar_image_search.html
     
但是未实现代码,代码如下:
+ View Code?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#!/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(),下面看一下其源码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
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上拿。

  代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#_*_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个点,相当于画了三次框,所以要粗一点。

  代码:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#_*_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()函数源码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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

  函数返回值就是矩阵的最小值,最大值,最小值的索引,最大值的索引。

  下面举个例子:

?
1
2
3
4
5
6
7
8
9
#_*_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方向:

  再进一步处理:

  对图像进行膨胀腐蚀:

  再对图像进行二值化:

  进一步二值化:

  找到原图的地址:

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

  最后展示在原图:

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

?
1
2
Credit Card Type: Visa
Credit Card #: 4000123456789010

  完整代码如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#_*_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)

  部分代码解释:

?
1
2
3
4
5
6
7
8
9
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

https://www.lfd.uci.edu/~gohlke/pythonlibs/

   3,下载numpy,对应的版本

https://pypi.python.org/pypi/numpy

?
1
2
3
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

(0)

相关推荐

  • python进阶—OpenCV之图像处理(一)

    文章目录 颜色空间转换 RGB色彩空间 HSV色彩空间 YUV色彩空间 简单的物体跟踪示例 HSV空间目标阈值选取 图像几何变换 图像的缩放 图像的位移 图像的旋转 图像的仿射 图像的投射 图像阈值( ...

  • 计算机视觉(五)双三次插值(Bicubic Interpolation)算法介绍及opencv和matlab的代码实现及区别

    超分辨率基础_插值算法简介 1.插值算法 数学的数值分析领域中,内插或称插值(英语:interpolation)是一种通过已知的.离散的数据点,在范围内推求新数据点的过程或方法.常见的三种插值算法为最 ...

  • python+opencv图像处理(十五)

    直方图均衡 直方图均衡化是通过对图像的直方图进行修正来获得图像增强效果的方法,主要是进行对比度增强,就是让亮的更亮,暗的更亮. 1.灰度图像的直方图均衡 先上图看效果. 如图上标题所示,第一张是原图, ...

  • 常见的图像处理技术

    重磅干货,第一时间送达 本期文章中,让我们一起来学习以下内容. 通过PIL和OpenCV来使用一些常见的图像处理技术,例如将RGB图像转换为灰度图像.旋转图像.对图像进行消噪.检测图像中的边缘以及裁剪 ...

  • OpenCV中使用模板匹配识别空闲的货架空间

    重磅干货,第一时间送达 假设你是一名在超市工作的员工,被要求在商店里四处走动,检查需要重新进货的货架.但是,超市有时会有多个区域来存放一种特定的产品,所以要跟踪购物者购买产品的确切位置并不容易.最重要 ...

  • 基于python和OpenCV构建智能停车系统

    重磅干货,第一时间送达 当今时代最令人头疼的事情就是找不到停车位,尤其是找20分钟还没有找到停车位. 根据复杂性和效率的不同,任何问题都具有一个或多个解决方案.目前智能停车系统的解决方案,主要包括基于 ...

  • 使用OpenCV和TesseractOCR进行车牌检测

    重磅干货,第一时间送达 目录 1)目的和简介 2)前言 3)使用OpenCV和Haar级联进行车牌检测 4)使用TesseractOCR识别和提取车牌号 目的与简介 当我们谈论AI时,计算机视觉绝对是 ...

  • python+opencv图像处理(十二)

    图像仿射变换和透视变换 天晴了...... 1.仿射变换 图像的仿射变换就是图像的旋转加上拉升,说直白点,就是把矩形变成平行四边形. 要把矩形变成平行四边行,只需要拉伸其四个角点就行了,事实上,只需要 ...

  • 使用OpenCV实现图像增强

    重磅干货,第一时间送达 本期将介绍如何通过图像处理从低分辨率/模糊/低对比度的图像中提取有用信息. 下面让我们一起来探究这个过程: 首先我们获取了一个LPG气瓶图像,该图像取自在传送带上运行的仓库.我 ...

  • python+opencv图像处理(四十一)

    提前过了个万圣节,南瓜灯还蛮好看的,就是那些装鬼的有点吓人...... Scharr算子 1.Scharr算子 Scharr算子也是用于提取图像边缘的. 它是对Sobel算子差异性的增强,两者的原理和 ...

  • Python进阶——OpenCV之Core Operations

    文章目录 图像基本操作 访问并修改像素值 访问图像的属性 设置图像区域 图像分割与合并 画图像边框 图像的数学操作 图像叠加 图像融合 图像位操作 Python OpenCV代码检测与速度优化 时隔一 ...

  • 使用OpenCV进行对象检测

    重磅干货,第一时间送达 目标检测是图像处理的重要组成部分.自动驾驶汽车必须检测车道,路面,其他车辆,人,标志和信号等.我们生活在一个动态的世界中,一切都在不断变化.对象检测的应用无处不在. 我们正在研 ...

  • 基于Python的颜色识别器

    重磅干货,第一时间送达 在这篇文章中,我们将展示如何使用Python构建颜色识别器.此过程也称为"颜色检测".我们将创建一个基本应用程序,该应用程序将帮助我们检测图像中的颜色.该程 ...