Fork me on GitHub

OpenCV实战(1)——图像相似度算法(比对像素方差,感知哈希算法,模板匹配(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()

   两张原图:

 

  图像结果如下:

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

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

 

   3,下载numpy,对应的版本

https://pypi.python.org/pypi/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

posted @ 2018-10-16 22:01  战争热诚  阅读(65307)  评论(7编辑  收藏  举报