OPEN-CV学习笔记(1)

目录

图像基本操作

图像读取

视频读取

ROI区域

边界填充

 放缩融合

 图像阈值与平滑处理

图像阈值

图像平滑

图像形态学操作

图像腐蚀

图像膨胀 

​编辑开运算与闭运算

梯度计算

 礼帽与黑帽

图像梯度

Sobel算子

Scharr算子

 laplacian算子

Canny边缘检测

图像金字塔与轮廓检测

图像金字塔

 轮廓检测

模板匹配

图像直方图与傅里叶变换

直方图 

傅里叶变换 

案例1——信用卡卡号识别

介绍

模版图像处理

信用卡图像处理

循环匹配

案例2——文档扫描OCR识别

边缘检测

轮廓提取

透视变换

OCR识别

图像基本操作

图像读取

基本操作的代码如下:

#opencv读取的格式是BGR
import cv2 

#读取图片
img=cv2.imread('cat.jpg') 
#图像的显示也可以创建多个窗口
cv2.imshow('image',img)
#等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
#关闭所有打开的窗口的函数
cv2.destroyAllWindows()
#转为灰度图,cv2.IMREAD_COLOR:彩色图像
img=cv2.imread('cat.jpg',cv2.IMREAD_GRAYSCALE)
#保存图像
cv2.imwrite('mycat.png',img)

#查看像素点个数
img.size    
#查看维度
img.shape 
#查看数据类型
img.dtype 

视频读取

视频读取代码如下:

import cv2
# 打开视频文件
vc = cv2.VideoCapture('test.mp4')

# 检查是否成功打开视频文件
if vc.isOpened(): 
    open, frame = vc.read()  # 读取第一帧
else:
    open = False

# 循环遍历视频帧
while open:
    ret, frame = vc.read()  # 读取下一帧
    if frame is None:  # 判断是否到达视频末尾
        break
    if ret == True:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)  # 将帧转换为灰度图像
        cv2.imshow('result', gray)  # 在窗口中显示灰度图像
        if cv2.waitKey(100) & 0xFF == 27:  # 检测按键,按下 ESC 键退出显示
            break

# 释放视频流和关闭窗口
vc.release()
cv2.destroyAllWindows()

ROI区域

ROI:简而言之就是图像截取(裁剪)

img=cv2.imread('cat.jpg')
#索引切片
cat=img[0:200,0:200] 
plt.subplot(121), plt.imshow(img), plt.title('初始图片')
plt.subplot(122), plt.imshow(cat ), plt.title('裁剪后图片')

颜色通道提取:分别提取查看R,G,B三个通道的值,opencv读取的格式是BGR

# 分割图像通道
b, g, r = cv2.split(img)
# 合并图像通道
img = cv2.merge((b, g, r))
# 只保留R通道
R_img = img.copy()
R_img[:, :, 0] = 0  # 将G通道和B通道置为0,只保留R通道
# 只保留G通道
G_img = img.copy()
G_img[:, :, 0] = 0  # 将B通道和R通道置为0,只保留G通道
# 只保留B通道
B_img = img.copy()
B_img[:, :, 1] = 0  # 将G通道和R通道置为0,只保留B通道
# 使用matplotlib绘制并显示图像
plt.subplot(131), plt.imshow(R_img), plt.title('只保留R')
plt.subplot(132), plt.imshow(G_img), plt.title('只保留G')
plt.subplot(133), plt.imshow(B_img), plt.title('只保留B')

边界填充

# 定义边界尺寸
top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)

# 使用不同的边界扩展方式创建不同效果的图像
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType=cv2.BORDER_CONSTANT, value=0)

# 使用matplotlib绘制并显示图像
plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')
plt.show()

  • BORDER_REPLICATE:复制法,也就是复制最边缘像素。
  • BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制例如:fedcba|abcdefgh|hgfedcb
  • BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
  • BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
  • BORDER_CONSTANT:常量法,常数值填充。

 放缩融合


# 读取猫和狗的图像
img_cat = cv2.imread('cat.jpg')
img_dog = cv2.imread('dog.jpg')

# 调整狗的图像大小,使其和猫的图像大小一样
img_dog = cv2.resize(img_dog, (500, 414))

# 将猫和狗的图像进行融合 0.4 和0.6分别为猫狗权重,0表示加到每个总和的标量值
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0)

# 对融合后的图像进行放缩,(0, 0)是目标图像的大小,设置为(0, 0)表示输出图像的尺寸由fx和fy的缩放比例决定,fx表示沿水平轴的缩放比例,fy表示沿垂直轴的缩放比例
res1 = cv2.resize(res, (0, 0), fx=4, fy=4)
res2 = cv2.resize(res, (0, 0), fx=1, fy=3)

# 使用matplotlib绘制并显示图像
plt.subplot(131), plt.imshow(res), plt.title('融合')
plt.subplot(132), plt.imshow(res1), plt.title('放缩1')
plt.subplot(133), plt.imshow(res2), plt.title('放缩2')
plt.show()

 图像阈值与平滑处理

图像阈值

图像阈值处理是图像处理领域中常用的技术之一,它用于将图像转换为二值图像(即黑白图像),以便更容易地提取图像中的信息或特征。图像阈值处理的基本思想是将图像中的像素值与预先设定的阈值进行比较,并根据比较结果将像素的值设置为两个预先定义的阈值之一。

ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
    plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

 ret, dst = cv2.threshold(src, thresh, maxval, type)

  • src: 输入图,只能输入单通道图像,通常来说为灰度图

  • dst: 输出图

  • thresh: 阈值

  • maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值

  • type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV

  • cv2.THRESH_BINARY           超过阈值部分取maxval(最大值),否则取0

  • cv2.THRESH_BINARY_INV    THRESH_BINARY的反转

  • cv2.THRESH_TRUNC            大于阈值部分设为阈值,否则不变

  • cv2.THRESH_TOZERO          大于阈值部分不改变,否则设为0

  • cv2.THRESH_TOZERO_INV  THRESH_TOZERO的反转

图像平滑

图像平滑是图像处理中常用的技术之一,用于减少图像中的噪声,使图像更加清晰和易于处理。图像平滑通常通过应用一些滤波器或卷积核来实现,主要包括以下几种方法:

  1. 均值滤波:均值滤波是一种最简单的图像平滑方法。它通过将像素周围邻域内的像素值取平均来减小噪声。该方法对于轻度噪声有较好的效果,但可能会使图像细节变得模糊。
  2. 高斯滤波:高斯滤波使用一个高斯函数作为卷积核,可以更好地保留图像的边缘信息,同时有效地去除高斯噪声。相比均值滤波,高斯滤波对图像的影响较小,同时能有效平滑噪声。

  3. 中值滤波:中值滤波将每个像素的值替换为其邻域像素值的中值。它对去除椒盐噪声(即高斯噪声的一种)非常有效,且能保持图像细节。

img = cv2.imread('lenaNoise.png')
# 均值滤波  简单的平均卷积操作,卷积核3*3
blur = cv2.blur(img, (3, 3))
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的,卷积核5*5,标准差1
aussian = cv2.GaussianBlur(img, (5, 5), 1)  
# 相当于用中值代替,卷积核5
median = cv2.medianBlur(img, 5)  # 中值滤波
# 展示所有的
res = np.hstack((blur,aussian,median))
#print (res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像形态学操作

图像腐蚀

图像腐蚀(Erosion)是一种常见的形态学图像处理操作,用于消除图像中的细小噪点、缩小边界或分离连接的物体。

# 读入图像
pie = cv2.imread('pie.png')

# 显示原始图像
cv2.imshow('pie', pie)
cv2.waitKey(0)
cv2.destroyAllWindows()

# 定义腐蚀核
kernel = np.ones((30,30),np.uint8) 
# 分别对图像进行1、2、3次腐蚀操作
erosion_1 = cv2.erode(pie, kernel, iterations=1)
erosion_2 = cv2.erode(pie, kernel, iterations=2)
erosion_3 = cv2.erode(pie, kernel, iterations=3)
# 将腐蚀结果进行水平拼接
res = np.hstack((erosion_1, erosion_2, erosion_3))
# 显示腐蚀结果
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

 原图

 腐蚀后的图

图像膨胀 

图像膨胀(Dilation)是数字图像处理中的一种形态学操作,用于增强图像中的物体区域或连接相邻的物体。膨胀操作通过将一个结构元素(也称为膨胀核)与图像进行卷积来执行,从而扩大或放大图像中的亮部分。

# 读入图像
pie = cv2.imread('pie.png')
# 定义膨胀核
kernel = np.ones((30,30),np.uint8) 
# 对图像进行1、2、3次膨胀操作
dilate_1 = cv2.dilate(pie, kernel, iterations=1)
dilate_2 = cv2.dilate(pie, kernel, iterations=2)
dilate_3 = cv2.dilate(pie, kernel, iterations=3)
# 将膨胀结果进行水平拼接
res = np.hstack((dilate_1, dilate_2, dilate_3))
# 显示膨胀结果
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

原图

膨胀结果

开运算与闭运算

开运算:先进行腐蚀操作,再进行膨胀操作。开运算能够消除小的亮区域,断开狭窄的连接,平滑边界,去除细小的突出部分,实际上是对图像进行的一种边缘保留滤波。

闭运算:先进行膨胀操作,再进行腐蚀操作。闭运算一般用于填充物体内细小空洞、连接邻近物体、平滑物体边界。

# 导入OpenCV库
import cv2
import numpy as np
# 读入图像
img = cv2.imread('dige.png')
# 定义腐蚀和膨胀所需的核
kernel = np.ones((5, 5), np.uint8)
# 执行开运算: 先腐蚀,再膨胀
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
# 执行闭运算: 先膨胀,再腐蚀
# closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
# 显示开运算的结果
cv2.imshow('Opening', opening)
# 等待用户按下任意键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()

梯度计算

图像的梯度运算在形态学图像处理中用于突出图像中物体的边界和轮廓部分。

# 读入图像
pie = cv2.imread('pie.png')
# 定义梯度运算所需的核
kernel = np.ones((7, 7), np.uint8)
# 执行梯度运算
gradient = cv2.morphologyEx(pie, cv2.MORPH_GRADIENT, kernel)
# 显示梯度运算的结果
cv2.imshow('Gradient', gradient)
# 等待用户按下任意键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()

上文圆形的运算结果

 礼帽与黑帽

礼帽运算(Top Hat):礼帽操作是指原始图像与其进行开运算(先腐蚀后膨胀)的结果之差。礼帽操作可以提取图像中的小细节、噪音或者比周围区域更亮的部分,常用于处理背景光照不均或者边缘检测。

黑帽运算(Black Hat):黑帽操作是指先对原始图像进行闭运算(先膨胀后腐蚀),然后再与原始图像取差。黑帽操作可以突出图像中的小细节、噪音或者比周围区域更暗的部分,常用于处理背景光照不均或者边缘检测。

#礼帽
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
#黑帽
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)

图像梯度

图像梯度表示图像中每个像素的变化率或变化程度。梯度可以帮助我们识别图像中的边缘、轮廓和纹理等特征。在图像处理中,常用的梯度算子包括Sobel算子、Scharr算子和Laplacian算子。

Sobel算子

Sobel算子是一种离散微分算子,可以计算图像的一阶水平和垂直导数。它通过卷积操作计算每个像素的梯度幅值和方向。Sobel算子特别适用于边缘检测。

Scharr算子

Scharr算子也是一种离散微分算子,类似于Sobel算子,但是在计算上更加精确。它可以计算图像的一阶水平和垂直导数。Scharr算子在边缘检测等应用中比Sobel算子效果更好。 

 laplacian算子

Laplacian算子是对图像进行二阶导数运算,可以提取图像中的纹理和灰度变化。Laplacian算子对于边缘的检测比较敏感,可用于图像增强和特征提取。

# 读入灰度图像
img = cv2.imread('lena.jpg', cv2.IMREAD_GRAYSCALE)

def cv_show(img, name):
    cv2.imshow(name, img)  # 显示图像
    cv2.waitKey(0)  # 等待按键按下
    cv2.destroyAllWindows()  # 关闭所有窗口

# 使用Sobel算子计算梯度
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)  # 计算x方向上的梯度
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)  # 计算y方向上的梯度
sobelx = cv2.convertScaleAbs(sobelx)  # 转换为绝对值
sobely = cv2.convertScaleAbs(sobely)  # 转换为绝对值
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)  # 梯度合并

# 使用Scharr算子计算梯度
scharrx = cv2.Scharr(img, cv2.CV_64F, 1, 0)  # 计算x方向上的梯度
scharry = cv2.Scharr(img, cv2.CV_64F, 0, 1)  # 计算y方向上的梯度
scharrx = cv2.convertScaleAbs(scharrx)  # 转换为绝对值
scharry = cv2.convertScaleAbs(scharry)  # 转换为绝对值
scharrxy = cv2.addWeighted(scharrx, 0.5, scharry, 0.5, 0)  # 梯度合并

# 使用Laplacian算子计算梯度
laplacian = cv2.Laplacian(img, cv2.CV_64F)  # 计算梯度
laplacian = cv2.convertScaleAbs(laplacian)  # 转换为绝对值

# 水平拼接结果图像
res = np.hstack((sobelxy, scharrxy, laplacian))
cv_show(res, 'res')  # 显示结果图像

算子结果:

Canny边缘检测

Canny边缘检测是一种多阶段的著名边缘检测算法,它结合了噪声抑制、梯度计算、非极大值抑制和双阈值等步骤,能够有效地检测图像中的边缘。其步骤如下:

1.使用高斯滤波器,以平滑图像,滤除噪声。

2.计算图像中每个像素点的梯度强度和方向。

3.应用非极大值抑制,以消除边缘检测带来的杂散效应。

非极大值抑制的基本思想是,对于每个像素点,根据其梯度方向,判断其是否为局部的极大值。只有在其梯度方向上的响应值比相邻两个像素点的响应值都大的情况下,才将其视为边缘点,否则将其抑制。简而言之,就是判断上图中C点与dtmp1和dtmp2的像素对比,比他们都大则保留,否则抑制。同道理下图的A若比C和B的值大则保留。

4.应用双阈值检测来确定真实的和潜在的边缘

在双阈值检测中,将边缘像素分为两个阈值范围内的像素和处于阈值之间的像素。通常使用两个阈值:高阈值(high threshold)和低阈值(low threshold)。以下是双阈值检测的一般步骤:

  1. 根据非极大值抑制得到的边缘强度图像,对每个像素进行阈值化处理。
  2. 如果某个像素的边缘强度超过高阈值,则将其标记为真实的边缘像素(强边缘)。
  3. 如果某个像素的边缘强度小于低阈值,则将其标记为非边缘像素。
  4. 对于介于低阈值和高阈值之间的像素,如果它与强边缘相连(在8邻域内有强边缘像素),则将其标记为强边缘;否则标记为非边缘像素。

通过双阈值检测,可以将边缘像素分为三类:真实的边缘像素(强边缘),潜在的边缘像素(在低阈值和高阈值之间),以及非边缘像素。这个过程有助于识别真正的边缘,同时抑制噪声和非边缘部分,最终得到更准确清晰的边缘图像。

# 读取灰度图像
img = cv2.imread("car.png", cv2.IMREAD_GRAYSCALE)

# 使用不同的阈值进行Canny边缘检测
v1 = cv2.Canny(img, 120, 250)  # 低阈值:120,高阈值:250
v2 = cv2.Canny(img, 50, 100)   # 低阈值:50,高阈值:100

# 拼接两个边缘检测结果
res = np.hstack((v1, v2))

# 显示拼接结果
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

结果如下:

可以看出阈值不同,检测出的边缘效果是有所区别的。 

图像金字塔与轮廓检测

图像金字塔

高斯金字塔是通过对图像进行反复的高斯模糊和下采样操作得到的一系列图像。每个金字塔层级都是通过对上一层级的图像进行高斯模糊得到的,同时将图像尺寸缩小为原来的一半。高斯模糊操作可以平滑图像并过滤高频噪声,而下采样操作可以减小图像尺寸。高斯金字塔通常用于图像金字塔表示、图像融合以及多尺度图像处理。

向下采样方法

向上采样方法

# 读取图像
img = cv2.imread("AM.png")
# 上采样(放大)
up = cv2.pyrUp(img)
# 下采样(缩小)
down = cv2.pyrDown(img)
# 绘制图像和子图
plt.subplot(131), plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title('img')  # 转换颜色空间以便在Matplotlib中正确显示
plt.subplot(132), plt.imshow(cv2.cvtColor(up, cv2.COLOR_BGR2RGB)), plt.title('up')    # 转换颜色空间以便在Matplotlib中正确显示
plt.subplot(133), plt.imshow(cv2.cvtColor(down, cv2.COLOR_BGR2RGB)), plt.title('down')  # 转换颜色空间以便在Matplotlib中正确显示
plt.show()

乍一看好像没啥变化,但是可以观察像素值是增大和缩小了的。

拉普拉斯金字塔:拉普拉斯金字塔是一种图像金字塔,由高斯金字塔计算得到。在计算拉普拉斯金字塔时,首先需要构建高斯金字塔,然后通过对高斯金字塔进行上采样和差分操作得到拉普拉斯金字塔。拉普拉斯金字塔通常用于图像分解、压缩、增强等应用。

up=cv2.pyrUp(img)
up_down=cv2.pyrDown(up)
cv_show(np.hstack((img,img-up_down)),'up_down')

 轮廓检测

图像边缘和轮廓的区别:边缘强调了图像中灰度或颜色发生突变的位置,而轮廓则表示了物体的外形边界。边缘是高频信息,而轮廓是低频信息。边缘可以通过边缘检测算法来提取,而轮廓可以通过阈值化和形态学等操作来提取。

import cv2
from matplotlib import pyplot as plt
# 读取图像
img = cv2.imread('contours.png')
# 将图像转换为灰度图
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 二值化处理
ret, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# 寻找轮廓
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
# 绘制轮廓
draw_img = img.copy()
res = cv2.drawContours(draw_img, contours, -1, (0, 0, 255), 2)  # 绘制所有轮廓
draw_img = img.copy()
res2 = cv2.drawContours(draw_img, contours, 0, (0, 0, 255), 2)  # 只绘制第一个轮廓
# 显示结果
plt.subplot(131), plt.imshow(thresh, cmap='gray'), plt.title('Original Image with Contours')
plt.subplot(132), plt.imshow(cv2.cvtColor(res, cv2.COLOR_BGR2RGB)), plt.title('All Contours')
plt.subplot(133), plt.imshow(cv2.cvtColor(res2, cv2.COLOR_BGR2RGB)), plt.title('First Contour')
plt.show()

模板匹配

模板匹配是一种在图像处理中常用的技术,用于在一幅图像中寻找与给定模板图像最相似的区域。

import cv2
import numpy as np
# 读取原始图像和模板图像
img_rgb = cv2.imread('mario.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('mario_coin.jpg', 0)
# 获得模板图像的高度和宽度
h, w = template.shape[:2]
# 应用模板匹配
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
# 设置匹配的阈值
threshold = 0.8
# 取匹配程度大于阈值的坐标
loc = np.where(res >= threshold)
# 在原始图像上绘制匹配的矩形框
for pt in zip(*loc[::-1]):
    bottom_right = (pt[0] + w, pt[1] + h)
    cv2.rectangle(img_rgb, pt, bottom_right, (0, 0, 255), 2)
# 显示结果图像
cv2.imshow('img_rgb', img_rgb)
cv2.waitKey(0)

图像直方图与傅里叶变换

直方图 

图像直方图是对图像像素的统计分布的可视化表示。它将图像中每个像素值的频数或概率可视化为一个直方图,用于展现图像的亮度、对比度和色彩分布等信息。

cv2.calcHist(images,channels,mask,histSize,ranges)

  • images: 原图像图像格式为 uint8 或 float32。当传入函数时应 用中括号 [] 括来例如[img]
  • channels: 同样用中括号括来它会告函数我们统幅图 像的直方图。如果入图像是灰度图它的值就是 [0]如果是彩色图像 的传入的参数可以是 [0][1][2] 它们分别对应着 BGR。
  • mask: 掩模图像。统整幅图像的直方图就把它为 None。但是如 果你想统图像某一分的直方图的你就制作一个掩模图像并 使用它。
  • histSize:BIN 的数目。也应用中括号括来
  • ranges: 像素值范围常为 [0256]
# 读取灰度图像
img = cv2.imread('cat.jpg', 0)  # 0表示灰度图
# 使用cv2.calcHist计算直方图
hist = cv2.calcHist([img], [0], None, [256], [0, 256])
# 绘制直方图
plt.hist(img.ravel(), 256, [0, 256])
plt.show()

查看RGB直方图

# 读取彩色图像
img = cv2.imread('cat.jpg')
# 定义颜色
color = ('b', 'g', 'r')
# 针对每个颜色通道计算直方图并绘制
for i, col in enumerate(color):
    histr = cv2.calcHist([img], [i], None, [256], [0, 256])
    plt.plot(histr, color=col)
# 设置 x 轴的范围
plt.xlim([0, 256])
# 显示直方图
plt.show()

mask操作:直方图的mask操作指的是在计算直方图时使用掩码(mask)来限制统计的像素范围。通过使用掩码,我们可以选择性地统计图像中的某些区域或像素值范围。

# 创建mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255  # 在图像中定义一个矩形区域作为mask
# 读取图像
img = cv2.imread('cat.jpg', 0)
# 对图像应用mask
masked_img = cv2.bitwise_and(img, img, mask=mask)  # 对图像应用mask进行与操作
# 计算完整图像的直方图
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
# 计算mask应用后的直方图
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
# 绘制原始图像、mask、应用mask后的图像以及对应的直方图
plt.subplot(221), plt.imshow(img, 'gray')
plt.title('Original Image')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.title('Mask')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.title('Masked Image')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.title('Histogram')
plt.xlim([0, 256])
plt.show()

 直方图均衡化:直方图均衡化是一种用于增强图像对比度的方法,通过重新分布图像的像素值,使得整个灰度范围内的像素值分布更加均匀,从而提高图像的视觉效果。

# 读取灰度图像
img = cv2.imread('clahe.jpg', 0)  # 0表示灰度图
# 绘制原始图像直方图
plt.subplot(211)
plt.hist(cv2.imread('clahe.jpg', 0).ravel(), 256)
plt.title('Original Histogram')
# 进行直方图均衡化
equ = cv2.equalizeHist(img)
# 绘制均衡化后的图像直方图
plt.subplot(212)
plt.hist(equ.ravel(), 256)
plt.title('Equalized Histogram')
plt.show()
res = np.hstack((img,equ))
cv_show(res,'res')

 均衡化之后的效果

自适应直方图均衡化:自适应直方图均衡化(Adaptive Histogram Equalization,AHE)是一种能够根据图像局部特征进行直方图均衡化的方法,在 OpenCV 中可以通过 cv2.createCLAHE 函数实现。

# 读取灰度图像
img = cv2.imread('clahe.jpg', 0)
# 创建自适应直方图均衡化对象
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
# 应用自适应直方图均衡化
res_clahe = clahe.apply(img)
# 生成图像拼接
res = np.hstack((img, equ, res_clahe))
# 显示结果图像
cv_show(res, 'res')

傅里叶变换 

图像的傅里叶变换(Fourier Transform)是一种将图像从空间域转换到频域的方法,可以用来分析图像中的频率信息。通过傅里叶变换,可以得到图像在不同频率上的分量信息。这有助于分析图像中的频率成分,了解图像的结构、纹理和边缘等特征。

高频成分:高频成分捕捉到图像中的细节和高频变化,对应于图像中的边缘、纹理和细节等特征。利用高频成分,我们可以进行边缘检测、纹理分析、图像细节增强等操作。同时,高频成分还可以用于图像压缩和去噪,通过滤除高频噪声来提高图像质量。

低频成分:低频成分表示图像中的整体结构和低频变化,对应于图像中的平滑区域、背景和全局亮度变化等。低频成分常用于图像去噪、图像增强和图像重建等操作。通过增强低频成分,可以增加图像的对比度和亮度范围,改善图像的视觉效果。

import numpy as np
import cv2
from matplotlib import pyplot as plt
# 读取图像
img = cv2.imread('lena.jpg',0)
# 将图像从uint8转换为float32类型
img_float32 = np.float32(img)
# 执行傅里叶变换
dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)
# 获取图像的行数和列数
rows, cols = img.shape
# 获取图像的中心位置
crow, ccol = int(rows/2) , int(cols/2)

# 构建低通滤波器掩膜
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1

# 构建高通滤波器掩膜
#mask = np.ones((rows, cols, 2), np.uint8)
#mask[crow-30:crow+30, ccol-30:ccol+30] = 0

# 对频谱进行滤波操作
fshift = dft_shift*mask
f_ishift = np.fft.ifftshift(fshift)
# 执行傅里叶逆变换
img_back = cv2.idft(f_ishift)
# 计算逆变换结果的幅度谱
img_back = cv2.magnitude(img_back[:,:,0],img_back[:,:,1])
# 显示原始图像和滤波结果
plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(img_back, cmap = 'gray')
plt.title('Result'), plt.xticks([]), plt.yticks([])
plt.show()

低通滤波结果:

高通滤波结果:

案例1——信用卡卡号识别

介绍

问题描述:给定一张信用卡,要求识别出上面的卡号。

问题思路:首先准备一张图片,包含1-9的每个数字,对其预先处理后计算每个数字的边缘和轮廓,然后对信用卡图片进行预处理,保留包含卡号的四个区域,对每个区域的四个数字分别边缘检测后与模板的十个数字的边缘轮廓进行匹配,取匹配得分最高的数字为识别后的数字。

模版图像处理

# 导入工具包
from imutils import contours
import numpy as np
import cv2
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
    boundingBoxes = [cv2.boundingRect(c) for c in cnts] #用一个最小的矩形,把找到的形状包起来x,y,h,w
    (cnts, boundingBoxes) = zip(*sorted(zip(cnts, boundingBoxes),
                                        key=lambda b: b[1][i], reverse=reverse))
    return cnts, boundingBoxes
def resize(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
# # 设置参数
picture_moban = 'ocr_a_reference.png'
picture_shibie = 'images/credit_card_02.png'
# 绘图展示
def cv_show(name,img):
	cv2.imshow(name, img)
	cv2.waitKey(0)
	cv2.destroyAllWindows()
# 读取一个模板图像
img = cv2.imread(picture_moban)
cv_show('img',img)
# 灰度图
ref = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv_show('ref',ref)
# 二值图像
ref = cv2.threshold(ref, 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)
cv2.drawContours(img,refCnts,-1,(0,0,255),3) 
cv_show('img',img)
# print (np.array(refCnts).shape)
refCnts = sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下
digits = {}
# 遍历每一个轮廓
for (i, c) in enumerate(refCnts):
	# 计算外接矩形并且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

代码结果如下:

上图分别对应模版图像、灰度图、二值图和轮廓图。

信用卡图像处理

# 初始化卷积核
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))
#读取输入图像,预处理
image = cv2.imread(picture_shibie)
cv_show('image',image)
image = resize(image, width=300)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv_show('gray',gray)

#礼帽操作,突出更明亮的区域
tophat = cv2.morphologyEx(gray, cv2.MORPH_TOPHAT, rectKernel) 
cv_show('tophat',tophat) 
# 
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, #ksize=-1相当于用3*3的
	ksize=-1)


gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
gradX = (255 * ((gradX - minVal) / (maxVal - minVal)))
gradX = gradX.astype("uint8")

print (np.array(gradX).shape)
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',thresh)

# 计算轮廓

threshCnts, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,
	cv2.CHAIN_APPROX_SIMPLE)

cnts = threshCnts
cur_img = image.copy()
cv2.drawContours(cur_img,cnts,-1,(0,0,255),3) 
cv_show('img',cur_img)
locs = []

# 遍历轮廓
for (i, c) in enumerate(cnts):
	# 计算矩形
	(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])

运行结果如下:

 

 上面对应信用卡原图、灰度图、礼帽操作后的图(突出显示明亮区域)以及经过Sobel算子计算后的图。

上图分别对应第一个闭操作,二值图、再次闭操作(数字区域存在小黑块,再次闭操作,用白色填充)以及最后的边缘检测图。并且最后是根据轮廓特征只保留了数字区域的列表。

循环匹配

output = []

# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):
	# initialize the list of group digits
	groupOutput = []

	# 根据坐标提取每一个组
	group = gray[gY - 5:gY + gH + 5, gX - 5:gX + gW + 5]
	cv_show('group',group)
	# 预处理
	group = cv2.threshold(group, 0, 255,
		cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
	cv_show('group',group)
	# 计算每一组的轮廓
	digitCnts,hierarchy = cv2.findContours(group.copy(), cv2.RETR_EXTERNAL,
		cv2.CHAIN_APPROX_SIMPLE)
	digitCnts = contours.sort_contours(digitCnts,
		method="left-to-right")[0]

	# 计算每一组中的每一个数值
	for c in digitCnts:
		# 找到当前数值的轮廓,resize成合适的的大小
		(x, y, w, h) = cv2.boundingRect(c)
		roi = group[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(image, (gX - 5, gY - 5),
		(gX + gW + 5, gY + gH + 5), (0, 0, 255), 1)
	cv2.putText(image, "".join(groupOutput), (gX, gY - 15),
		cv2.FONT_HERSHEY_SIMPLEX, 0.65, (0, 0, 255), 2)

	# 得到结果
	output.extend(groupOutput)
# 指定信用卡类型
FIRST_NUMBER = {
	"3": "American Express",
	"4": "Visa",
	"5": "MasterCard",
	"6": "Discover Card"
}
# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

 第一组数字的循环识别结果

这样在识别完四组之后,最终结果画到原图上,

可以发现正确识别,并且控制台输出相应的号码以及卡片的类别。

案例2——文档扫描OCR识别

问题描述:给定带有文字的不规整图片,进行边缘检测和平移翻转之后矫正,然后再进行文字识别等工作。

边缘检测

# 导入工具包
import numpy as np
import cv2
def resize(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
# 读取输入
image = cv2.imread('images/page.jpg')
#坐标也会相同变化
ratio = image.shape[0] / 500.0
orig = image.copy()
image = resize(orig, height = 500)
# 预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (5, 5), 0)
edged = cv2.Canny(gray, 75, 200)
# 展示预处理结果
print("STEP 1: 边缘检测")
cv2.imshow("Image", image)
cv2.imshow("Edged", edged)
cv2.waitKey(0)
cv2.destroyAllWindows()

resize 函数用于按照指定的宽度和高度或者根据预设的插值方法对图像进行大小调整。然后使用该函数加载了名为 ‘images/page.jpg’ 的图像,并按比例缩放到高度为 500 像素。

然后进行了图像的预处理操作,包括将图像转换为灰度图、应用高斯模糊(高斯模糊可以消除图像中的噪声,并平滑图像。)和边缘检测。同时,还展示了原始图像和边缘检测结果。

边缘检测结果如下:

轮廓提取

# 轮廓检测
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[0]
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:5]

# 遍历轮廓
for c in cnts:
	# 计算轮廓近似
	peri = cv2.arcLength(c, True)
	# C表示输入的点集
	# epsilon表示从原始轮廓到近似轮廓的最大距离,它是一个准确度参数
	# True表示封闭的
	approx = cv2.approxPolyDP(c, 0.02 * peri, True)

	# 4个点的时候就拿出来
	if len(approx) == 4:
		screenCnt = approx
		break

# 展示结果
print("STEP 2: 获取轮廓")
cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
cv2.imshow("Outline", image)
cv2.waitKey(0)
cv2.destroyAllWindows()

检测出的轮廓有很多,只需要最外面的轮廓,也就是面积最大的轮廓。首先计算轮廓的周长。然后对轮廓进行近似处理,其中第二个参数 0.02*peri 表示近似精度。如果近似后的轮廓为4个点(矩形),则将其赋值给变量,跳出循环。

透视变换

def order_points(pts):
	# 一共4个坐标点
	rect = np.zeros((4, 2), dtype = "float32")
	# 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
	# 计算左上,右下
	s = pts.sum(axis = 1)
	rect[0] = pts[np.argmin(s)]
	rect[2] = pts[np.argmax(s)]
	# 计算右上和左下
	diff = np.diff(pts, axis = 1)
	rect[1] = pts[np.argmin(diff)]
	rect[3] = pts[np.argmax(diff)]
	return rect
def four_point_transform(image, pts):
	# 获取输入坐标点
	rect = order_points(pts)
	(tl, tr, br, bl) = rect
	# 计算输入的w和h值
	widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
	widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
	maxWidth = max(int(widthA), int(widthB))
	heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
	heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
	maxHeight = max(int(heightA), int(heightB))
	# 变换后对应坐标位置
	dst = np.array([
		[0, 0],
		[maxWidth - 1, 0],
		[maxWidth - 1, maxHeight - 1],
		[0, maxHeight - 1]], dtype = "float32")
	# 计算变换矩阵
	M = cv2.getPerspectiveTransform(rect, dst)
	warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))
	# 返回变换后结果
	return warped
# 透视变换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio)
# 二值处理
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
ref = cv2.threshold(warped, 100, 255, cv2.THRESH_BINARY)[1]
cv2.imwrite('scan.jpg', ref)
# 展示结果
print("STEP 3: 变换")
cv2.imshow("Original", resize(orig, height = 650))
cv2.imshow("Scanned", resize(ref, height = 650))
cv2.waitKey(0)

这段代码的主要作用是定义了两个函数,分别用于对坐标点进行排序和实现图像的透视变换,然后进行了透视变换和二值化处理,最终通过展示结果来展示处理的效果。M矩阵就是变换矩阵,通过变换前后的四个坐标点计算出来,然后再对整个图像用变换矩阵进行变换,图像就完成了透视变换。

OCR识别

OCR识别需要安装相关软件包,不再赘述,可参考Tesseract-OCR下载和安装,Python-OCR使用_tesseract-ocr python 下载-CSDN博客

安装后对图片进行识别

from PIL import Image
import pytesseract
import cv2
import os
preprocess = 'blur' #thresh
image = cv2.imread('scan.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
if preprocess == "thresh":
    gray = cv2.threshold(gray, 0, 255,cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1]
if preprocess == "blur":
    gray = cv2.medianBlur(gray, 3)
filename = "{}.png".format(os.getpid())
cv2.imwrite(filename, gray)
text = pytesseract.image_to_string(Image.open(filename))
print(text)
os.remove(filename)
# 调整图像大小
resized_image = cv2.resize(image, (600, 1000))
resized_gray = cv2.resize(gray, (600, 1000))
# 展示调整后的图像
cv2.imshow("Image", resized_image)
cv2.imshow("Output", resized_gray)
cv2.waitKey(0)

 发现报错

就是找不到tesseract,然后在读取图片的代码前面加上如下代码指定文件位置,就是你安装的tesseract位置。

pytesseract.pytesseract.tesseract_cmd = r'D:\下载组件\OCR\Tesseract-OCR\tesseract.exe'

识别对比结果如下:

 

可以看出识别效果还是不错的。

终于完结了第一篇笔记。感觉学到了很多,要长脑子了!

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python open-cv是一个流行的计算机视觉库,可以用于图像处理和计算机视觉任务。安装open-cv包可以让我们使用该库,并在我们的项目中实现高级计算机视觉处理。 安装Python open-cv包的步骤如下: 1. 安装Python:首先需要安装Python解释器并配置环境变量。Python安装包可以从Python官网下载并安装。 2. 安装pip:pip是Python的包管理器,可以用于安装Python的第三方库。在Python安装完毕后,pip一般会自动安装。可以通过输入pip -V命令来检查pip是否安装成功。 3. 安装numpy包:在安装Python open-cv之前,需要先安装numpy包。numpy是Python数值计算库,用于处理多维数组和矩阵。可以使用pip install numpy来安装该包。 4. 下载Python open-cv安装包:可以从open-cv官网下载安装包。选择适合自己系统版本的安装包进行下载。 5. 安装open-cv包:将下载的open-cv安装包解压后,通过命令行进入解压后的文件夹,使用pip install命令进行安装。例如:pip install opencv_python-4.5.3-cp38-cp38-win_amd64.whl。 6. 验证安装:在安装open-cv包后,可以通过在Python解释器环境中运行import cv2命令来验证安装是否成功。如果没有报错,说明open-cv安装成功。 总之,安装Python open-cv包需要先安装Python解释器和pip包管理器,然后安装numpy包,再下载open-cv安装包并进行安装。安装成功后,就可以使用open-cv库进行图像处理和计算机视觉任务。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值