日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

OpenCV實(shí)戰(zhàn)(1)

 wuxinit_ 2021-02-03

如果需要處理的原圖及代碼,請(qǐng)移步小編的GitHub地址

  傳送門:請(qǐng)點(diǎn)擊我

  如果點(diǎn)擊有誤:https://github.com/LeBron-Jian/ComputerVisionPractice

  最近一段時(shí)間學(xué)習(xí)并做的都是對(duì)圖像進(jìn)行處理,其實(shí)自己也是新手,各種嘗試,所以我這個(gè)門外漢想總結(jié)一下自己學(xué)習(xí)的東西,圖像處理的流程。但是動(dòng)起筆來想總結(jié),一下卻不知道自己要寫什么,那就把自己做過的相似圖片搜索的流程整理一下,想到什么說什么吧。

一:圖片相似度算法(對(duì)像素求方差并比對(duì))的學(xué)習(xí)

1.1 算法邏輯

1.1.1  縮放圖片

  將需要處理的圖片所放到指定尺寸,縮放后圖片大小由圖片的信息量和復(fù)雜度決定。譬如,一些簡(jiǎn)單的圖標(biāo)之類圖像包含的信息量少,復(fù)雜度低,可以縮放小一點(diǎn)。風(fēng)景等復(fù)雜場(chǎng)景信息量大,復(fù)雜度高就不能縮放太小,容易丟失重要信息。根據(jù)自己需求,彈性的縮放。在效率和準(zhǔn)確度之間維持平衡。

1.1.2  灰度處理

  通常對(duì)比圖像相似度和顏色關(guān)系不是很大,所以處理為灰度圖,減少后期計(jì)算的復(fù)雜度。如果有特殊需求則保留圖像色彩。

1.1.3 計(jì)算平均值

  此處開始,與傳統(tǒng)的哈希算法不同:分別依次計(jì)算圖像每行像素點(diǎn)的平均值,記錄每行像素點(diǎn)的平均值。每一個(gè)平均值對(duì)應(yīng)著一行的特征。

1.1.4  計(jì)算方差

  對(duì)得到的所有平均值進(jìn)行計(jì)算方差,得到的方差就是圖像的特征值。方差可以很好的反應(yīng)每行像素特征的波動(dòng),既記錄了圖片的主要信息。

1.1.5  比較方差

  經(jīng)過上面的計(jì)算之后,每張圖都會(huì)生成一個(gè)特征值(方差)。到此,比較圖像相似度就是比較圖像生成方差的接近成程度。
  一組數(shù)據(jù)方差的大小可以判斷穩(wěn)定性,多組數(shù)據(jù)方差的接近程度可以反應(yīng)數(shù)據(jù)波動(dòng)的接近程度。我們不關(guān)注方差的大小,只關(guān)注兩個(gè)方差的差值的大小。方差差值越小圖像越相似!

 

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
#計(jì)算方差
def getss(list):
    #計(jì)算平均值
    avg=sum(list)/len(list)
    #定義方差變量ss,初值為0
    ss=0
    #計(jì)算方差
    for l in list:
        ss+=(l-avg)*(l-avg)/len(list)
    #返回方差
    return ss
#獲取每行像素平均值
def getdiff(img):
    #定義邊長(zhǎng)
    Sidelength=30
    #縮放圖像
    img=cv2.resize(img,(Sidelength,Sidelength),interpolation=cv2.INTER_CUBIC)
    #灰度處理
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    #avglist列表保存每行像素平均值
    avglist=[]
    #計(jì)算每行均值,保存到avglist列表
    for i in range(Sidelength):
        avg=sum(gray[i])/len(gray[i])
        avglist.append(avg)
    #返回avglist平均值
    return avglist
#讀取測(cè)試圖片
img1=cv2.imread("james.jpg")
diff1=getdiff(img1)
print('img1:',getss(diff1))
#讀取測(cè)試圖片
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()

   兩張?jiān)瓐D:

 

  圖像結(jié)果如下:

1
2
3
img1: 357.03162469135805
img11: 202.56193703703704
兩張照片的方差為:154.469687654321

 

   實(shí)驗(yàn)環(huán)境開始設(shè)置了圖片像素值,而且進(jìn)行灰度化處理,此方法比對(duì)圖像相似對(duì)不同的圖片方差很大,結(jié)果很明顯,但是對(duì)比比較相似,特別相似的圖片不適應(yīng)。

二:圖片相似度算法(感知哈希算法)的學(xué)習(xí)

  "感知哈希算法"(Perceptual hash algorithm),它的作用是對(duì)每張圖片生成一個(gè)"指紋"(fingerprint)字符串,然后比較不同圖片的指紋。結(jié)果越接近,就說明圖片越相似。

2.1  算法步驟

2.1.1 縮小尺寸

  將圖片縮小到8x8的尺寸,總共64個(gè)像素。這一步的作用是去除圖片的細(xì)節(jié),只保留結(jié)構(gòu)、明暗等基本信息,摒棄不同尺寸、比例帶來的圖片差異。

2.1.2  簡(jiǎn)化色彩

  將縮小后的圖片,轉(zhuǎn)為64級(jí)灰度。也就是說,所有像素點(diǎn)總共只有64種顏色。

2.1.3  計(jì)算平均值

  計(jì)算所有64個(gè)像素的灰度平均值

2.1.4  比較像素的灰度平均值

  將每個(gè)像素的灰度,與平均值進(jìn)行比較。大于或等于平均值,記為1;小于平均值,記為0。

2.1.5 計(jì)算哈希值

  將上一步的比較結(jié)果,組合在一起,就構(gòu)成了一個(gè)64位的整數(shù),這就是這張圖片的指紋。組合的次序并不重要,只要保證所有圖片都采用同樣次序就行了。

  得到指紋以后,就可以對(duì)比不同的圖片,看看64位中有多少位是不一樣的。在理論上,這等同于計(jì)算"漢明距離"(Hamming distance)。如果不相同的數(shù)據(jù)位不超過5,就說明兩張圖片很相似;如果大于10,就說明這是兩張不同的圖片。

1
2
3
4
    此算法參考博客:http://www.ruanyifeng.com/blog/2011/07
    /principle_of_similar_image_search.html
     
但是未實(shí)現(xiàn)代碼,代碼如下:

 

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  模板匹配的定義

  模板就是一幅已知的小圖像,而模板匹配就是在一幅大圖像中搜尋目標(biāo),已知該圖中有要找的目標(biāo),且該目標(biāo)與模板有相同的尺度,方向和圖像元素,通過一定的算法可以在圖像中找到目標(biāo)。

  模板匹配和卷積原理很像,模板在原圖像上開始滑動(dòng),計(jì)算模板與圖像被模板覆蓋的地方的差別程度,這個(gè)差別程度的計(jì)算方法在opencv里有6種,然后將每次計(jì)算的結(jié)果放入一個(gè)矩陣?yán)?,作為結(jié)果輸出。假如原圖像是A*B大小,而模板是 a*b大小,則輸出結(jié)果的矩陣是 (A-a+1)*(B-b+1)。

3.2  模板匹配方法

  模板匹配在opencv中的函數(shù)為 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

   下面對(duì)模板匹配方法進(jìn)行解釋:

  • cv2.TM_CCOEFF:系數(shù)匹配法,計(jì)算相關(guān)系數(shù),計(jì)算出來的值越大,越相關(guān)
  • cv2.TM_CCOEFF_NORMED:相關(guān)系數(shù)匹配法,計(jì)算歸一化相關(guān)系數(shù),計(jì)算出來的值越接近1,越相關(guān)
  • cv2.TM_CCORR:相關(guān)匹配法,計(jì)算相關(guān)性,計(jì)算出來的值越大,越相關(guān)
  • cv2.TM_CCORR_NORMED:歸一化相關(guān)匹配法,計(jì)算歸一化相關(guān)性,計(jì)算出來的值越接近1,越相關(guān)
  • cv2.TM_SQDIFF:平方差匹配法,計(jì)算平方不同,計(jì)算出來的值越小,越相關(guān)
  • cv2.TM_SQDIFF_NORMED:歸一化平方差匹配法,計(jì)算歸一化平方不同,計(jì)算出來的值越接近0,越相關(guān)

  公式復(fù)制opencv官網(wǎng),如下:

   下面實(shí)操一下。

3.2  模板匹配一個(gè)對(duì)象實(shí)例

  原圖是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)
# 函數(shù)返回值就是矩陣的最小值,最大值,最小值的索引,最大值的索引。
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)
    # 函數(shù)返回值就是矩陣的最小值,最大值,最小值的索引,最大值的索引。
    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([])  # 隱藏坐標(biāo)軸
    plt.subplot(122), plt.imshow(img2, cmap='gray')
    plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)
    plt.show()

   效果如下:

3.3  模板匹配多個(gè)對(duì)象

  有時(shí)候,你需要匹配的模板在圖像中多次出現(xiàn),那么就需要多對(duì)象匹配。多對(duì)象匹配的原理很簡(jiǎn)單,因?yàn)閛pencv里的模板匹配里的每一處和模板進(jìn)行對(duì)比,所以同一個(gè)模板下,多對(duì)象匹配情況下,結(jié)果矩陣?yán)飼?huì)有好幾個(gè)值,和最大(小)值接近,如果我們?cè)O(shè)置一個(gè)閾值,在這個(gè)閾值以上(以下)的值都提取出來,再分別得到他們的坐標(biāo),理論上只要這個(gè)閾值設(shè)置的恰當(dāng),就可以將多對(duì)象都匹配出來。

  原圖是超級(jí)瑪麗的一張截圖,模板是金幣,也可以去我GitHub上拿。

  這種方法的方框要 粗一點(diǎn),因?yàn)槲覀兌x的 threshold 為 0.8,(只針對(duì)代碼中的 cv2.TM_CCOEFF_NORMED 匹配方法),loc檢測(cè)出來有 9個(gè)點(diǎn),相當(dāng)于畫了三次框,所以要粗一點(diǎn)。

  代碼:

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% 的坐標(biāo)
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):  # *表示可選參數(shù)
    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()函數(shù)用法

  cv2.minMaxLoc() 函數(shù)功能:假設(shè)有一個(gè)矩陣 a,現(xiàn)在需要求這個(gè)矩陣的最小值,最大值,并得到最大值,最小值的索引。就可以使用此函數(shù)。

  cv2.minMaxLoc()函數(shù)源碼如下:

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

   函數(shù)返回值就是矩陣的最小值,最大值,最小值的索引,最大值的索引。

  下面舉個(gè)例子:

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 實(shí)戰(zhàn)——信用卡數(shù)字識(shí)別

  關(guān)于圖片和代碼,可以去我的GitHub中拿,GitHub地址在文章上面。

  這一實(shí)戰(zhàn)的目的是識(shí)別出信用卡中的數(shù)字,也就是我們的卡號(hào),信用卡如下:

 

  而我們的模板圖片如下:

  對(duì)模板圖像處理過程:

  首先對(duì)圖像二值化,并對(duì)二值化的圖片進(jìn)行逆運(yùn)算:

  然后拿到原二值圖像的輪廓:

  最后將數(shù)字對(duì)應(yīng)的輪廓存入字典中,方便后面對(duì)比。

  對(duì)信用卡進(jìn)行圖像處理,這里不再一一贅述,均在代碼中,只展示圖像的處理過程及其結(jié)果:

  灰度化信用卡圖像:

  對(duì)信用卡圖像進(jìn)行頂帽處理:

   對(duì)圖像進(jìn)行sobel算子的X方向:

   再進(jìn)一步處理:

  對(duì)圖像進(jìn)行膨脹腐蝕:

  再對(duì)圖像進(jìn)行二值化:  進(jìn)一步二值化:

  找到原圖的地址:

   設(shè)置篩選范圍,找到我們需要的區(qū)域:

  最后展示在原圖:

   也可以將結(jié)果展示在編譯器:

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
    # 用一個(gè)最小的矩形,把找到的形狀包起來 x, y, h, w
    boundingBoxes = [cv2.boundingRect(c) for c in cnts]
    # #zip(*)相當(dāng)于解壓,是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)
    # 計(jì)算輪廓
    # cv2.findContours() 函數(shù)接受的參數(shù)為二值圖,即黑白的(不是灰度圖)
    # cv2.RETR_EXTERNAL 只檢測(cè)外輪廓  cv2.CHAIN_APPROX_SIMPLE 只保留終點(diǎn)坐標(biāo)
    # 返回的list中每個(gè)元素都是圖像中的一個(gè)輪廓
    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 = {}
    # 遍歷每一個(gè)輪廓
    for (i, c) in enumerate(refCnts1):
        # 計(jì)算外接矩陣并且resize成合適大小
        (x, y, w, h) = cv2.boundingRect(c)
        roi = ref[y:y+h, x:x+w]
        roi = cv2.resize(roi, (57, 88))
        # 每一個(gè)數(shù)字對(duì)應(yīng)每個(gè)模板
        digits[i] = roi
    # print(digits)
    return digits
def origin_img_processing(origin_image, digits):
    # 讀入輸入圖像,并進(jìn)行預(yù)處理
    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))
    # 禮帽操作,突出更明顯的區(qū)域
    tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel)
    # cv_show('tophat', tophat)
    # ksize = -1 相當(dāng)于用 3*3 的 ksize=-1
    gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)
    # 下面np函數(shù)等價(jià)于  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)
    # 通過閉操作(先膨脹,再腐蝕)將數(shù)字連在一起
    gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKernel)
    # cv_show('gradX', gradX)
    # THRESH_OTSU 會(huì)自動(dòng)尋找合適的閾值,適合雙峰,需把閾值參數(shù)設(shè)置為0
    thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
    # cv_show('thresh', thresh)
    # 再來一個(gè)閉操作  先膨脹后腐蝕
    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, sqKernel)
    # cv_show('thresh_again', thresh)
    #計(jì)算輪廓
    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):
        # 計(jì)算矩形
        (x, y, w, h) = cv2.boundingRect(c)
        ar = w / float(h)
        # 選擇合適的區(qū)域,根據(jù)實(shí)際任務(wù)來,這里的基本都是四個(gè)數(shù)字一組
        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 = []
    # 遍歷輪廓中每一個(gè)數(shù)字
    for (i, (gX, gY, gW, gH)) in enumerate(locs):
        # initialize the list of group digits
        groupOutput = []
        # 根據(jù)坐標(biāo)提取每一個(gè)組
        group = gray[gY - 5: gY + gH + 6, gX - 5:gX + gW + 5]
        # cv_show('group', group)
        # 預(yù)處理
        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)
        # 計(jì)算每一組的輪廓
        digitCnts, hierarchy = cv2.findContours(group1.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
        digitCnts = sort_contours(digitCnts, method='left-to-right')[0]
        # 計(jì)算每一組中的每一個(gè)數(shù)值
        for c in digitCnts:
            # 找到當(dāng)前數(shù)值的輪廓,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)
            # 計(jì)算匹配得分
            scores = []
            # 在模板中計(jì)算每一個(gè)得分
            for (digit, digitROI) in digits.items():
                # 模板匹配
                result = cv2.matchTemplate(roi, digitROI, cv2.TM_CCOEFF)
                (_, score, _, _) = cv2.minMaxLoc(result)
                scores.append(score)
            # 得到最合適的數(shù)字
            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)
        # 得到結(jié)果
        output.extend(groupOutput)
    # 打印結(jié)果
    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)

   這里我跳出一些許多代碼,直接找到關(guān)鍵代碼:

  首先對(duì)于 locs = np.where(res > threshold) 進(jìn)行分析,threshold 就是我們?cè)O(shè)定的閾值,res>thrshold 就是將結(jié)果array 里符合條件的數(shù)值換成True,反之換成 False。

  其次對(duì)于 np.where 的作用就是將 true的索引輸出到 Loc變量里面。

  loc[::-1] 將輸出的索引換成 x, y 坐標(biāo),因?yàn)樗饕?x , y 坐標(biāo)是正好相反的,所以要對(duì)換下位置。

 

四:關(guān)于OpenCV的安裝

  OpenCV的全稱open Sourse Computer Vision Library ,是一個(gè)跨平臺(tái)的計(jì)算機(jī)視覺庫(kù),OpenCV可用于開發(fā)實(shí)時(shí)的圖像處理,計(jì)算機(jī)視覺以及模式識(shí)別的程序。

  OpenCV是用C++語(yǔ)言編寫,它的主要接口也是C++語(yǔ)言,但是依然保留了大量的C語(yǔ)言接口,該庫(kù)也有大量的Python,Java和MATLAB的接口,另外,一個(gè)使用CUDA的GPU接口也用于2010.9 開始實(shí)現(xiàn)。

3.1  為什么使用Python+OpenCV

  雖然python很強(qiáng)大,而且也有自己的圖像處理庫(kù)PIL,但是相對(duì)于OpenCV來講,它還是弱小很多。跟很多開源軟件一樣OpenCV也提供了完善的python接口,非常便于調(diào)用。OpenCV 的穩(wěn)定版是2.4.8,最新版是3.0,包含了超過2500個(gè)算法和函數(shù),幾乎任何一個(gè)能想到的成熟算法都可以通過調(diào)用OpenCV的函數(shù)來實(shí)現(xiàn),超級(jí)方便。

 

3.2  import cv2發(fā)生錯(cuò)誤的解決方案

   錯(cuò)誤如下:

  1,進(jìn)入cmd控制臺(tái),查看python版本

  2 根據(jù)自己用的python版本,下載對(duì)應(yīng)的OpenCV

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

 

   3,下載numpy,對(duì)應(yīng)的版本

https://pypi./pypi/numpy 

1
2
3
cp36代表著匹配python3.6版本。
win32、amd64代表著32位、64位系統(tǒng)。

  

  4,安裝OpenCV,下載下來是一個(gè)whl格式文件,把此文件放在安裝的文件名下,直接安裝。

 

  就這樣安裝成功。

 

  于2020.10.12 整理

參考文獻(xiàn):https://blog.csdn.net/wsp_1138886114/article/details/81368890

公式地址如下:https://docs./3.3.1/df/dfb/group__imgproc__object.html#ga3a7850640f1fe1f58fe91a2d7583695d

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多