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

《零基础入门AI: 从轮廓查找到形态学变换(OpenCV图像预处理)》

本文针对图像处理初学者,详细解析OpenCV核心预处理技术,包含概念解释、可视化示例和关键代码片段,帮助您建立系统的图像处理知识体系。


一、图像轮廓特征查找:对象的几何描述

轮廓是连接图像中所有连续边界点的曲线,用于描述物体的形状特征。在OpenCV中,轮廓查找通常需要先进行二值化处理:

import cv2
import numpy as np# 读取图像并转为灰度图
img = cv2.imread('object.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 二值化处理
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)# 查找轮廓
contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
  1. 外接矩形(Bounding Rect)

    • 能完全包围轮廓的正矩形,边与坐标轴平行(不旋转)
    • 特点:计算简单快速
    • 应用:物体定位、碰撞检测
    • cv2.boundingRect()返回矩形左上角坐标和宽高
    • 数学表示:(x, y, width, height)
    x, y, w, h = cv2.boundingRect(contour)
    cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2)
    

    Bounding Rect

  2. 最小外接矩形(Rotated Rect)

    • 可旋转的矩形,面积最小且完全包围轮廓
    • 特点:更紧密包围对象
    • 应用:物体方向检测、精密测量
    • cv2.minAreaRect()返回中心点、尺寸和旋转角度
    • 数学表示:(center_x, center_y), (width, height), angle
    rect = cv2.minAreaRect(contour)
    box = cv2.boxPoints(rect)  # 获取四个顶点
    box = np.int0(box)
    cv2.drawContours(img, [box], 0, (0, 0, 255), 2)
    

    Rotated Rect

    • 完整示例
    import cv2
    import numpy as np# 读图
    num = cv2.imread('../images/num.png')# 转灰度
    gray = cv2.cvtColor(num, cv2.COLOR_BGR2GRAY)# 二值化处理
    _,thresh = cv2.threshold(gray,200,255,cv2.THRESH_BINARY_INV)# 查找轮廓
    contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# 绘制轮廓
    cv2.drawContours(num,contours,-1,(255,0,0),2)# 循环遍历每一条轮廓
    for cnt in contours:# 图像轮廓特征查找:最小外接矩形  (x , y)  w,h  angleresult = cv2.minAreaRect(cnt)# print(result)# 处理点坐标points = cv2.boxPoints(result).astype(np.int32)# 绘制最小外接矩形cv2.drawContours(num,[points],0,(0,0,255),2)# 绘制外接矩形x,y,w,h = cv2.boundingRect(cnt)cv2.rectangle(num,(x,y),(x+w,y+h),(0,255,0),2)# 显示
    cv2.imshow('num',num)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  3. 最小外接圆(Min Enclosing Circle)

    • 完全包围轮廓的最小圆形
    • 特点:对不规则形状更有效
    • 应用:细胞分析、粒子检测
    • cv2.minEnclosingCircle()返回圆心和半径
    • 数学表示:(center_x, center_y), radius
    (x, y), radius = cv2.minEnclosingCircle(contour)
    center = (int(x), int(y))
    radius = int(radius)
    cv2.circle(img, center, radius, (255, 0, 0), 2)
    
    • 完整示例
    import cv2
    import numpy as np# 读图
    num = cv2.imread('../images/num.png')# 转灰度
    gray = cv2.cvtColor(num, cv2.COLOR_BGR2GRAY)# 二值化
    _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY_INV)# 轮廓查找
    contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)# 循环遍历轮廓
    for cnt in contours:# 获取最小外接圆(x,y),radius = cv2.minEnclosingCircle(cnt)# 处理点坐标(x, y) = np.int32((x,y))radius = np.int32(radius)# 绘制圆cv2.circle(num,(x,y),radius,(0,0,255),2)# 显示
    cv2.imshow('num',num)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

应用场景:物体尺寸测量、方向识别、目标定位


二、直方图均衡化:提升图像对比度

直方图是图像像素强度的统计分布图:

  • X轴:像素强度值(0-255)
  • Y轴:具有该强度的像素数量
  • 应用:分析图像亮度分布、指导增强处理

直方图示例

  • 完整示例(绘制直方图)
import cv2
import numpy as np# 读图
bg = cv2.imread('../images/bg.png')# 创建一个黑色背景图,绘制直方图
black = np.zeros((256,256,3), np.uint8)# 统计像素
hist = cv2.calcHist([bg], [1], None, [256], [0, 256])
# print(hist)
# print(hist[241,0])# 获取直方图的最小值、最大值及其对应的最小值的位置索引、最大值的位置索引
minval, maxval, minloc, maxloc = cv2.minMaxLoc(hist)
# print(minval, maxval, minloc, maxloc)# 定义直方图的高
h_hist = np.int32(256)# 循环拿像素的个数
for i in range(256):   # [num]l = np.int32(hist[i].item() * h_hist / maxval)point1 = (i,h_hist - l)point2 = (i,h_hist)cv2.line(black, point1, point2, (0,255,0), 2)# 显示
cv2.imshow('black', black)
cv2.waitKey(0)
cv2.destroyAllWindows()
  1. 直方图均衡化(HE)

    直方图均衡化通过重新分布像素强度来增强对比度:

    • 原理:将集中分布的强度值扩展到整个范围
    • 效果:增强图像细节,特别是暗区和亮区,增强整体对比度但可能放大噪声
    • 数学基础:累积分布函数(CDF)
    import cv2# 读图   读为灰度图
    img = cv2.imread('../images/zhifang.png',cv2.IMREAD_GRAYSCALE)# 直方图均衡化处理
    dst = cv2.equalizeHist(img)cv2.imshow('img',img)
    cv2.imshow('dst',dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  2. 自适应直方图均衡化(AHE)

    • 将图像分块后单独均衡化
    • 增强局部对比度但放大噪声
  3. 对比度受限自适应均衡化(CLAHE)

    • 限制局部对比度增强幅度
    • 最佳效果:增强细节同时抑制噪声
    import cv2# 读图
    img = cv2.imread('../images/zhifang.png',cv2.IMREAD_GRAYSCALE)# 创建一个clahe对象
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))# 使用clahe 对象去调用apply()方法
    cl1 = clahe.apply(img)cv2.imshow('img', img)
    cv2.imshow('cl1', cl1)cv2.waitKey(0)
    cv2.destroyAllWindows()
    

三、模板匹配:在图像中查找特定模式

在输入图像中滑动搜索与模板最相似的区域:

  • 原理:滑动模板图像,计算相似度
  • 应用:目标检测、工业质检、OCR预处理
import cv2
import numpy as np# 读图
img = cv2.imread('../images/game.png')
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
temp = cv2.imread('../images/temp.png')
gray_temp = cv2.cvtColor(temp, cv2.COLOR_BGR2GRAY)print(gray_img.shape)
print(gray_temp.shape)# 获取 temp 的 h, w
h, w = gray_temp.shape# 模板匹配  拿到匹配结果矩阵
#
res = cv2.matchTemplate(gray_img,gray_temp,cv2.TM_CCOEFF_NORMED)
# print(res.shape)# 设置阈值
threshold = 0.8# 获取匹配上的结果的索引
loc = np.where(res >= threshold)
# print(loc)# 解包,拿到成对的(x, y) 的索引
# print(zip(*loc))
for i in zip(*loc):# print(i)x,y = i[1],i[0]# print(x,y)cv2.rectangle(img,(x,y),(x+w,y+h),(0,255,0),1)# 显示
cv2.imshow('temp',temp)
cv2.imshow('img',img)
cv2.waitKey(0)
cv2.destroyAllWindows()

匹配方法对比

六种主要相似度计算方法:

方法公式特点适用场景最佳匹配位置
平方差匹配(TM_SQDIFF)R=∑(T−I)2R = \sum(T-I)^2R=(TI)2值越小越匹配精确匹配最小值
归一化平方差(TM_SQDIFF_NORMED)R=∑(T−I)2∑T2∑I2R = \frac{\sum(T-I)^2}{\sqrt{\sum T^2 \sum I^2}}R=T2I2(TI)2不受亮度影响光照变化最小值
相关匹配(TM_CCORR)R=∑(T×I)R = \sum(T \times I)R=(T×I)值越大越匹配快速匹配最大值
归一化相关(TM_CCORR_NORMED)R=∑(T×I)∑T2∑I2R = \frac{\sum(T \times I)}{\sqrt{\sum T^2 \sum I^2}}R=T2I2(T×I)最常用通用场景最大值
相关系数(TM_CCOEFF)R=∑(T′×I′)∑T′2∑I′2R = \frac{\sum(T' \times I')}{\sqrt{\sum T'^2 \sum I'^2}}R=T′2I′2(T×I)消除均值影响纹理匹配最大值
归一化相关系数TM_CCOEFF_NORMED同上,归一化最鲁棒复杂场景最大值

四、霍夫变换:检测几何形状
  1. 霍夫变换原理

    霍夫变换将图像空间中的形状映射到参数空间:

    • 核心思想:“投票机制” - 图像中的点投票支持可能的形状
    • 优势:对噪声和部分遮挡鲁棒
  2. 霍夫直线变换

    检测图像中的直线(计算量大):

    • 参数空间(ρ,θ)(\rho, \theta)(ρ,θ)
      • ρ\rhoρ:原点到直线的距离
      • θ\thetaθ:直线与x轴的夹角
    import cv2
    import numpy as np# 读图
    img = cv2.imread('../images/huofu.png')# 灰度化
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 二值化
    _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)# 边缘检测
    edges = cv2.Canny(thresh,100,200)# 霍夫变换   返回的是[r, theta]
    lines = cv2.HoughLines(edges,0.8,np.pi/180,90)
    # print(lines)for line in lines:r, theta = line[0]sin_theta = np.sin(theta)cos_theta = np.cos(theta)x1,x2 = 0, img.shape[1]y1 = int((r - x1 *cos_theta) / sin_theta)y2 = int((r - x2 *cos_theta) / sin_theta)cv2.line(img,(x1,y1),(x2,y2),(255,0,0),2)cv2.imshow('img',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  3. 统计概率霍夫变换(HoughLinesP)

    改进版直线检测:

    • 优点:只检测线段,直接返回线段端点
    • 效率:比标准霍夫变换更快
    import cv2
    import numpy as np# 读图
    img = cv2.imread('../images/huofu.png')# 灰度化
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 二值化
    _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)# 边缘检测
    edges = cv2.Canny(thresh,100,200)lines = cv2.HoughLinesP(edges,0.8,np.pi/180,90,minLineLength =50,maxLineGap=100)
    print(lines)for line in lines:x1,y1,x2,y2 = line[0]cv2.line(img,(x1,y1),(x2,y2),(255,0,0),2)cv2.imshow('img',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  4. 霍夫圆变换

    检测图像中的圆形:

    • 参数空间(x,y,r)(x, y, r)(x,y,r)
    • 原理:三维累加器投票
    import cv2
    import numpy as np# 读图
    img = cv2.imread('../images/huofu.png')# 灰度化
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)# 二值化
    _,thresh = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)# 边缘检测
    edges = cv2.Canny(thresh,100,200)# 霍夫圆变换
    circles = cv2.HoughCircles(edges,cv2.HOUGH_GRADIENT,1,10,param2 = 20)
    # print(circles)for circle in circles:x,y,radius = np.int32(circle[0])# print(x,y,radius)cv2.circle(img,(x,y),radius,(0,255,0),2)cv2.imshow('img',img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    

五、图像亮度变换:像素值调整
  1. 亮度变换基础

    调整图像整体或局部区域的亮度:

    • 全局调整:影响整个图像
    • 局部调整:影响特定区域
  2. 线性变换

    最基础的亮度调整方法:

    • 公式g(x,y)=α⋅f(x,y)+βg(x,y) = \alpha \cdot f(x,y) + \betag(x,y)=αf(x,y)+β
    • α\alphaα:控制对比度(>1增加,<1减小)
    • β\betaβ:控制亮度(正数变亮,负数变暗)
    import cv2# 读图
    cat = cv2.imread('../images/cat1.png')# 线性变换
    alpha = 1.5  # 对比度增益
    beta = 1    # 亮度增量
    dst = cv2.addWeighted(cat, alpha, cat, beta, 0)# 显示
    cv2.imshow('cat', cat)
    cv2.imshow('dst', dst)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
  3. 直接像素修改

    通过遍历像素直接修改值:

    • 应用:创建自定义滤镜、选择性调整
    • 注意:效率较低,应谨慎使用
    bright_img = np.zeros_like(img)
    for y in range(img.shape[0]):for x in range(img.shape[1]):bright_img[y,x] = min(255, img[y,x] + 30)  # 增加亮度
    
  4. 滑条控制亮度

import cv2
import numpy as np# 读图
cat = cv2.imread('../images/cat1.png')#创建窗口  用于实现滑条
window_name = "slide"
cv2.namedWindow(window_name,cv2.WINDOW_AUTOSIZE)\img = cv2.imread('../images/cat1.png')def change(p):x = p/256*511-255dst = np.uint8(np.clip(img + x,0,255))cv2.imshow('dst',dst)print(x)# 创建一个滑条
initial_value = 100
cv2.createTrackbar("add_p","slide",initial_value,255,change)cv2.waitKey(0)
cv2.destroyAllWindows()

实际应用:使用向量化操作提高效率
bright_img = cv2.add(img, 30)


六、形态学变换:形状处理的基础
  1. 结构元素(核)

    形态学操作的核心组件:

    • 作用:定义邻域大小和形状
    • 类型:矩形、椭圆、十字形
    • 大小:奇数尺寸(3×3、5×5等)

    基于结构元素(核) 的形状操作:

    kernel = np.ones((5,5), np.uint8)  # 5x5正方形核
    
  2. 基本操作

操作函数效果可视化原理应用场景
腐蚀cv2.erode()缩小物体边界,消除小点Erosion用核的最小值替换锚点去除噪声
膨胀cv2.dilate()扩大物体边界,填补空洞Dilation用核的最大值替换锚点连接断裂
开运算cv2.morphologyEx(MORPH_OPEN)先腐蚀后膨胀 → 去噪Opening先腐蚀后膨胀背景分割
闭运算cv2.morphologyEx(MORPH_CLOSE)先膨胀后腐蚀 → 补洞Closing先膨胀后腐蚀前景完整
礼帽cv2.morphologyEx(MORPH_TOPHAT)原图 - 开运算 → 突出亮区域Top Hat原图 - 开运算背景均匀的亮特征
黑帽cv2.morphologyEx(MORPH_BLACKHAT)闭运算 - 原图 → 突出暗区域Black Hat闭运算 - 原图背景均匀的暗特征
形态学梯度cv2.morphologyEx(MORPH_GRADIENT)膨胀图 - 腐蚀图 → 提取边缘Gradient膨胀 - 腐蚀边缘检测
import cv2
import numpy as np# 读图
car = cv2.imread('../images/car.png',cv2.IMREAD_GRAYSCALE)# 定义核
kernel = np.ones((5,5),np.uint8)# 腐蚀
erosion = cv2.erode(car,kernel,iterations = 1)# 膨胀
dilation = cv2.dilate(car,kernel,iterations = 1)# 开运算
opening = cv2.morphologyEx(car,cv2.MORPH_OPEN,kernel)# 闭运算
close = cv2.morphologyEx(car,cv2.MORPH_CLOSE,kernel)# 礼帽运算
tophat = cv2.morphologyEx(car,cv2.MORPH_TOPHAT,kernel)# 黑帽运算
blackhat = cv2.morphologyEx(car,cv2.MORPH_BLACKHAT,kernel)# 形态学梯度 膨胀和腐蚀差
gradient = cv2.morphologyEx(car,cv2.MORPH_GRADIENT,kernel)cv2.imshow('car',car)
cv2.imshow('erosion',erosion)
cv2.imshow('dilation',dilation)
cv2.imshow('opening',opening)
cv2.imshow('close',close)
cv2.imshow('tophat',tophat)
cv2.imshow('blackhat',blackhat)
cv2.imshow('gradient',gradient)cv2.waitKey(0)
cv2.destroyAllWindows()

核形状选择

  • cv2.MORPH_RECT 矩形核(默认)
  • cv2.MORPH_ELLIPSE 椭圆核
  • cv2.MORPH_CROSS 十字形核

形态学操作可视化

原始图像: [1 1 1 0 0][1 1 1 0 0][1 1 1 0 0][0 0 0 0 0]腐蚀后:   [1 1 0 0 0][1 1 0 0 0][0 0 0 0 0][0 0 0 0 0]膨胀后:   [1 1 1 1 0][1 1 1 1 0][1 1 1 0 0][0 0 0 0 0]
技术组合应用示例
# 完整的预处理流程
img = cv2.imread('industrial_part.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# 1. 亮度调整
adjusted = cv2.convertScaleAbs(gray, alpha=1.2, beta=20)# 2. CLAHE增强对比度
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(adjusted)# 3. 中值滤波去噪
blurred = cv2.medianBlur(clahe_img, 5)# 4. 形态学开运算去噪
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5,5))
opened = cv2.morphologyEx(blurred, cv2.MORPH_OPEN, kernel)# 5. Canny边缘检测
edges = cv2.Canny(opened, 50, 150)# 6. 查找轮廓并绘制最小外接矩形
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:rect = cv2.minAreaRect(cnt)box = cv2.boxPoints(rect)box = np.int0(box)cv2.drawContours(img, [box], 0, (0,0,255), 2)cv2.imshow('Result', img)
cv2.waitKey(0)

http://www.lryc.cn/news/602026.html

相关文章:

  • 【深度学习新浪潮】基于文字生成3D城市景观的算法有哪些?
  • Leaflet 综合案例-聚类图层控制
  • Python快速入门(2025版):输入
  • 婚纱摄影管理系统(发送邮箱、腾讯地图API、物流API、webSocket实时聊天、协同过滤算法、Echarts图形化分析)
  • C++ list 容器全解析:从构造到模拟实现的深度探索----《Hello C++ Wrold!》(16)--(C/C++)
  • 数值计算 | 图解基于龙格库塔法的微分方程计算与连续系统离散化(附Python实现)
  • C primer plus (第六版)第九章 编程练习第6题
  • 【Rust异步】async和await异步编程实战:高并发任务处理全解析
  • Java 排序
  • 股指期货周度想法
  • RWA 正当红,是 DeFi 的终点、拐点,还是新起点?
  • 【C++】手搓一个STL风格的vector容器
  • 7.DRF 过滤、排序、分页
  • 开发指南125-HTML DOM事件
  • 【Linux篇章】穿越数据迷雾:HTTPS构筑网络安全的量子级护盾,重塑数字信任帝国!
  • Kafka——请求是怎么被处理的?
  • 云原生MySQL Operator开发实战(三):高级特性与生产就绪功能
  • RabbitMQ+内网穿透远程访问教程:实现异地AMQP通信+Web管理
  • MongoDB索引及其原理
  • Java#包管理器来时的路
  • k8s的权限
  • Windows|CUDA和cuDNN下载和安装,默认安装在C盘和不安装在C盘的两种方法
  • C++ 中实现 `Task::WhenAll` 和 `Task::WhenAny` 的两种方案
  • Android启动时间优化大全
  • i节点学习
  • JavaScript核心概念全解析
  • Flutter中 Provider 的基础用法超详细讲解(二)之ChangeNotifierProvider
  • Vim 编辑器工作模式及操作指南
  • Spring AI 项目实战(二十一):Spring Boot + AI +DeepSeek驱动的智能题库系统(附完整源码)
  • zabbix-agent静默安装