当前位置: 首页 > news >正文

OpenCV4-直方图与傅里叶变换-项目实战-信用卡数字识别

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 1. 直方图与傅里叶变换
    • 1.1 直方图定义
    • 1.2 mask操作
    • 1.3 直方图均衡化
    • 1.4 自适应直方图均衡化
    • 1.5 傅里叶变换
    • 1.6 频域变换结果
    • 1.7 低通和高通滤波
  • 2. 项目实战-信用卡数字识别
    • 2.1 代码
  • 总结


前言

1. 直方图与傅里叶变换

1.1 直方图定义

在这里插入图片描述

就是统计每一个像素值有多少个点

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

在这里插入图片描述
256表示有256个取值,其实就是0~255的每个值
1表示每个值出现个数

plt.hist(img.ravel(),256); 
plt.show()

在这里插入图片描述

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) plt.xlim([0,256]) 

i就是0,1,2,col就是bgr

1.2 mask操作

在这里插入图片描述
这个就是掩码的作用

# 创建mast
mask = np.zeros(img.shape[:2], np.uint8)
print (mask.shape)
mask[100:300, 100:400] = 255
cv_show(mask,'mask')

在这里插入图片描述
mask的形状要和img的形状一样

img = cv2.imread('./cat.jpg', 0)
cv_show(img,'img')

在这里插入图片描述

masked_img = cv2.bitwise_and(img, img, mask=mask)#与操作
cv_show(masked_img,'masked_img')

and0的话,那么就是0,就是黑色了

在这里插入图片描述

hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()

在这里插入图片描述
黄色的线是hist_mask,因为要矮一点

1.3 直方图均衡化

在这里插入图片描述

这个就是不均衡

在这里插入图片描述

这样就变均衡了

在这里插入图片描述
就是这样计算就变均衡了

img = cv2.imread('./cat.jpg',0) #0表示灰度图 #clahe
plt.hist(img.ravel(),256); 
plt.show()

在这里插入图片描述

equ = cv2.equalizeHist(img) 
plt.hist(equ.ravel(),256)
plt.show()

在这里插入图片描述

变胖了

res = np.hstack((img,equ))
cv_show(res,'res')

在这里插入图片描述

感觉均衡化之后,就变黑一点了
在这里插入图片描述
感觉变得更显眼了
在这里插入图片描述
感觉变亮了,也变模糊了

比如人脸就变模糊了,那么如果分模块均衡化呢,这样就不会把脸的像素均衡化到其他地方了

1.4 自适应直方图均衡化

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

在这里插入图片描述

最后一个图就是局部均衡化

第二个是全局均衡化

但是局部均衡化的话,边界感比较明显

1.5 傅里叶变换

我们生活在时间的世界中,早上7:00起来吃早饭,8:00去挤地铁,9:00开始上班。。。以时间为参照就是时域分析。

但是在频域中一切都是静止的!

在这里插入图片描述
按照频域描述就是每隔一分钟得一个三分,每隔一分钟得一个两分

任何一个周期函数都可以用很多个正弦波堆积出来

在这里插入图片描述
在这里插入图片描述

1.6 频域变换结果

高频:变化剧烈的灰度分量,例如边界
在这里插入图片描述

低频:变化缓慢的灰度分量,例如一片大海
在这里插入图片描述

滤波
低通滤波器:只保留低频,没有边界了,会使得图像模糊

高通滤波器:只保留高频,保留所有边界,边界锐化,会使得图像细节增强

opencv中主要就是cv2.dft()和cv2.idft(),输入图像需要先转换成np.float32 格式,得到的结果中频率为0的部分会在左上角,通常要转换到中心位置,通过shift变换

import numpy as np
import cv2
from matplotlib import pyplot as pltimg = cv2.imread('../img/lena.jpg',0)img_float32 = np.float32(img)dft = cv2.dft(img_float32, flags = cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)magnitude_spectrum = 20*np.log(cv2.magnitude(dft_shift[:,:,0],dft_shift[:,:,1]))plt.subplot(121),plt.imshow(img, cmap = 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(magnitude_spectrum, cmap = 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

dft表示执行傅里叶变换
fftshift表示把低频值放在数组中间
cv2.dft() 是 OpenCV 中实现离散傅里叶变换的函数。
flags=cv2.DFT_COMPLEX_OUTPUT 表示输出结果为复数形式(傅里叶变换的结果是复数,包含实部和虚部)。
输出 dft 是一个三维数组,形状为 (高度, 宽度, 2),其中最后一个维度的两个元素分别对应复数的实部和虚部。

傅里叶变换的原始结果中,低频分量(对应图像的平滑区域)分布在频谱的角落,高频分量(对应边缘、噪声等细节)分布在四周。
np.fft.fftshift() 可以将低频分量移至频谱的中心,方便后续观察和处理(如滤波)。
在这里插入图片描述
在这里插入图片描述
最终显示的效果是:左图为原始灰度图,右图为傅里叶变换后的幅度谱(中心亮斑为低频分量,越亮表示该频率成分越强)。

在这里插入图片描述
中间比较亮—》低频,越往外,越高频,越暗

1.7 低通和高通滤波

import numpy as np
import cv2
from matplotlib import pyplot as pltimg = cv2.imread('lena.jpg',0)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# IDFT
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()        

rows, cols = img.shape # 获取图像尺寸(高度、宽度)
crow, ccol = int(rows/2), int(cols/2) # 计算频谱中心坐标(整数)

#创建低通滤波器掩码(mask)
mask = np.zeros((rows, cols, 2), np.uint8) # 初始化全0掩码,形状与dft_shift一致(最后一维为2,对应复数的实部和虚部)
mask[crow-30:crow+30, ccol-30:ccol+30] = 1 # 将中心30x30区域设为1(保留低频)
这个mask就只有中心区域为1,其他为0

# 应用滤波器:只保留掩码为1的频率成分
fshift = dft_shift * mask  # 逐元素相乘,过滤高频,全部为0了# 将频谱移回原始位置(与fftshift相反)
f_ishift = np.fft.ifftshift(fshift)# 执行逆傅里叶变换(从频率域转回空间域)
img_back = cv2.idft(f_ishift)# 计算逆变换结果的幅度(复数转实数)
img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
低通滤波—》变模糊了

img = cv2.imread('../img/lena.jpg',0)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.ones((rows, cols, 2), np.uint8)
mask[crow-30:crow+30, ccol-30:ccol+30] = 0# IDFT
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()    

高通滤波就是把低通变为0,高通*1不变了

在这里插入图片描述
只留下边界了

2. 项目实战-信用卡数字识别

在这里插入图片描述
左边的是模版,挨个挨个判断检测就可以了
轮廓检测—》内轮廓,外轮廓(要这个)——》,轮廓外接矩形

预处理—》灰度–》过滤数字----》轮廓长宽比例判断,先框4000,在细致操作

2.1 代码

# 导入工具包
from imutils import contours
import numpy as np
import cv2
import myutils# 指定信用卡类型
FIRST_NUMBER = {"3": "American Express","4": "Visa","5": "MasterCard","6": "Discover Card"
}
# 绘图展示
def cv_show(name,img):cv2.imshow(name, img)cv2.waitKey(0)cv2.destroyAllWindows()
# 读取一个模板图像
img = cv2.imread("./images/ocr_a_reference.png")
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]
# 10:阈值。所有灰度值小于10的像素将被设置为0(黑色),所有灰度值大于或等于10的像素将被设置为255(白色)。
# 255:最大值。这里设置为255,表示白色。
# cv2.THRESH_BINARY_INV:阈值类型。THRESH_BINARY_INV 表示反转二值化,即小于阈值的像素设置为255,大于或等于阈值的像素设置为0。这通常用于将背景设置为白色,目标对象设置为黑色。
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)
# ref.copy():对二值图像 ref 进行拷贝,因为 cv2.findContours 会修改输入图像。
# cv2.RETR_EXTERNAL:轮廓检索模式,表示只检索最外层的轮廓。
# cv2.CHAIN_APPROX_SIMPLE:轮廓近似方法,表示只保留轮廓的端点,减少数据量。
# refCnts:检测到的轮廓列表。
# hierarchy:轮廓的层次结构信息。
cv2.drawContours(img,refCnts,-1,(0,0,255),3)
# refCnts:轮廓列表。
# -1:表示绘制所有轮廓。
# (0, 0, 255):轮廓的颜色,这里设置为红色。
# 3:轮廓的线条宽度。
cv_show('img',img)
print (len(refCnts))
refCnts = myutils.sort_contours(refCnts, method="left-to-right")[0] #排序,从左到右,从上到下,返回的是排完序的轮廓
digits = {}# 遍历每一个轮廓,i 是索引,c 是当前轮廓。
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# 初始化卷积核,卷积核通常用于图像形态学操作,如膨胀(dilation)、腐蚀(erosion)、开运算(opening)和闭运算(closing)
rectKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (9, 3))
sqKernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))#读取输入图像,预处理
image = cv2.imread("./images/credit_card_01.png")
cv_show('image',image)
image = myutils.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:计算图像的梯度。
gradX = cv2.Sobel(tophat, ddepth=cv2.CV_32F, dx=1, dy=0, #ksize=-1相当于用3*3的ksize=-1)
# dx=1:计算水平方向的梯度(x 方向)。
# dy=0:不计算垂直方向的梯度(y 方向)。
# ksize=-1:使用默认的 3x3 滤波器# 计算梯度的绝对值,确保所有值都是非负的。
gradX = np.absolute(gradX)
(minVal, maxVal) = (np.min(gradX), np.max(gradX))
# np.min(gradX) 和 np.max(gradX):计算梯度图像的最小值和最大值。
# 归一化:将梯度图像的值范围调整到 0255 之间。
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])
output = []# 遍历每一个轮廓中的数字
for (i, (gX, gY, gW, gH)) in enumerate(locs):# initialize the list of group digitsgroupOutput = []# 根据坐标提取每一个组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)# 打印结果
print("Credit Card Type: {}".format(FIRST_NUMBER[output[0]]))
print("Credit Card #: {}".format("".join(output)))
cv2.imshow("Image", image)
cv2.waitKey(0)

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

这个就是最终的结果

总结

http://www.dtcms.com/a/481749.html

相关文章:

  • 医院排班挂号系统小程序
  • 河北建设厅网站打不开是什么原因国际新闻直播
  • C++设计模式_行为型模式_命令模式Command
  • Blender自动化展UV插件 UV Factory 4.3 v1 – Powerful Modular Uv Tools
  • 网络与通信安全课程复习汇总2——信息保密
  • 密码学安全:CIA三元组与三大核心技术
  • 建网站怎么做本地的营销网站建设
  • 短剧分销系统技术拆解:渠道推广码生成、订单归因与实时分账系统实现
  • ​RocketMQ 与 RabbitMQ 全面对比:架构、性能与适用场景解析
  • RabbitMQ 消息可靠投递
  • RabbitMQ全面详解:从核心概念到企业级应用
  • 北京市建设工程第四检测所网站小程序定制开发团队
  • 安徽网站优化flash如何做网页
  • AI文档处理:AI在处理扫描版PDF时准确率低,如何提升?
  • TDengine 数学函数 EXP 用户手册
  • C语言自定义变量类型结构体理论:从初见到精通​​​​​​​(下)
  • 医疗网络功能虚拟化与深度强化学习的动态流量调度优化研究(下)
  • SpringMVC练习:加法计算器与登录
  • 小模型的应用
  • 深度学习进阶(一)——从 LeNet 到 Transformer:卷积的荣光与注意力的崛起
  • QPSK信号载波同步技术---极性Costas 法载波同步
  • 盘多多网盘搜索苏州seo排名公司
  • 国外有趣的网站wordpress小视频主题
  • RTC、UDP、TCP和HTTP以及直播等区别
  • Java面试场景:从Spring Web到Kafka的音视频应用挑战
  • 基于EDBO-ELM(改进蜣螂算法优化极限学习机)数据回归预测
  • gaussdb数据库的集中式和分布式
  • Ubuntu中使用Hadoop的HDFS和MapReduce
  • F024 RNN+Vue+Flask电影推荐可视化系统 python flask mysql 深度学习 echarts
  • Building-GAN模型结构详解