欢迎光临
我们一直在努力

【OpenCV 图像处理实战:从直方图到透视变换全攻略】

本文将系统梳理 OpenCV 中直方图统计、Mask 掩模、直方图均衡化、图像透视变换四大核心技术,结合完整代码与详细解析,带你从基础到进阶掌握图像处理实战技能。

一、图像直方图:像素分布的可视化

直方图是图像像素灰度级分布的直观表达,能帮我们分析图像的亮度、对比度与色彩分布。

1.1 核心 API:cv2.calcHist

# cv2.calcHist(images, channels, mask, histSize, ranges)
# images: 输入图像(需用[]包裹,如[img]),格式为uint8/float32
# channels: 通道索引,灰度图为[0],彩色图BGR对应[0]/[1]/[2]
# mask: 掩模图像,统计全图填None,统计局部需传入自定义掩模
# histSize: BINS数量(直方图柱子数),需用[]包裹,如[256]
# ranges: 像素值范围,通常为[0,256]

1.2 灰度图直方图绘制

import cv2
import matplotlib.pyplot as plt

# 1. 读取灰度图
phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)

# 方式1:matplotlib直接绘制精细粒度直方图
a = phone.ravel() # 将二维图像拉成一维数组
plt.hist(a, bins=256) # bins=256表示每个灰度级对应一根柱子
plt.show()

# 方式2:OpenCV统计+matplotlib绘制曲线直方图
phone_hist = cv2.calcHist([phone], [0], None, [16], [0,256]) # histSize=16:将0-255拆为16个区间
plt.plot(phone_hist)
plt.show()

1.3 彩色图三通道直方图

img = cv2.imread('phone.png')
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.show()

运行结果:

二、Mask 掩模:精准提取图像局部区域

Mask(掩模)是一张黑白二值图,用于指定只处理图像的局部区域,避免修改无关像素。

2.1 Mask 的创建与应用

import numpy as np

# 1. 读取灰度图
phone = cv2.imread('phone.png', cv2.IMREAD_GRAYSCALE)
cv2.imshow('phone', phone)
cv2.waitKey(0)

# 2. 创建Mask:与原图像同尺寸的全黑图像
mask = np.zeros(phone.shape[:2], np.uint8)
mask[50:350, 100:470] = 255 # 将矩形区域设为白色(感兴趣区域)
cv2.imshow('mask', mask)
cv2.waitKey(0)

# 3. 应用Mask:提取局部图像
Phone_mask = cv2.bitwise_and(phone, phone, mask=mask) # 仅保留Mask白色区域
cv2.imshow('phone_mask', Phone_mask)
cv2.waitKey(0)

2.2 Mask 在直方图统计中的应用

# 仅统计Mask白色区域的直方图
phone_hist_mask = cv2.calcHist([phone], [0], mask, [256], [0,256])
plt.plot(phone_hist_mask)
plt.show()

三、直方图均衡化:增强图像对比度

直方图均衡化通过拉伸像素分布,改善偏暗 / 偏亮图像的对比度,让细节更清晰。

3.1 全局直方图均衡化:cv2.equalizeHist

# 读取偏暗灰度图
black = cv2.imread('black.jpg', cv2.IMREAD_GRAYSCALE)

# 绘制原始直方图
plt.hist(black.ravel(), bins=256)
plt.show()

# 全局均衡化
black_equalize = cv2.equalizeHist(black)

# 绘制均衡化后直方图
plt.hist(black_equalize.ravel(), bins=256)
plt.show()

# 横向拼接对比效果
res = np.hstack((black, black_equalize)) # hstack:水平拼接图像
cv2.imshow('black_equalize', res)
cv2.waitKey(0)

3.2 自适应直方图均衡化(CLAHE):保留局部细节

全局均衡化易导致过曝,CLAHE 通过局部区域均衡化解决此问题:

# 创建CLAHE对象
clahe = cv2.createCLAHE(clipLimit=10, tileGridSize=(8,8))
# clipLimit: 对比度阈值,默认8;tileGridSize: 局部区域大小,默认(8,8)
black_clahe = clahe.apply(black)

# 横向拼接:原图 + 全局均衡化 + CLAHE均衡化
res = np.hstack((black, black_equalize, black_clahe))
cv2.imshow('black_clahe', res)
cv2.waitKey(0)

四、图像透视变换:矫正倾斜 / 畸变图像

透视变换能将倾斜、畸变的图像(如倾斜的发票、身份证)矫正为正视图。

4.1 核心工具函数

import numpy as np
import cv2

def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)

def order_points(pts):
# 对4个顶点排序:左上→右上→右下→左下
rect = np.zeros((4, 2), dtype="float32")
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)] # 左上:x+y最小
rect[2] = pts[np.argmax(s)] # 右下:x+y最大
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)] # 右上:y-x最小
rect[3] = pts[np.argmax(diff)] # 左下:y-x最大
return rect

def four_point_transform(image, pts):
# 获取排序后的顶点
rect = order_points(pts)
(tl, tr, br, bl) = rect

# 计算目标图像宽高
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

def resize(image, width=None, height=None, inter=cv2.INTER_AREA):
# 保持宽高比缩放图像
(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

4.2 完整透视变换流程(以发票矫正为例)

# 1. 读取并缩放图像
image = cv2.imread('fapiao.jpg')
cv_show('image', image)
ratio = image.shape[0] / 500.0 # 计算缩放比例
orig = image.copy()
image = resize(orig, height=500) # 按高度500缩放
cv_show('1',image)

# 2. 轮廓检测
print("STEP 1: 轮廓检测")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
edged = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)[1] # 自动阈值二值化
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)[-2]
image_contours = cv2.drawContours(image.copy(), cnts, -1, (0,0,255), 1)
cv_show('image_contours', image_contours)

# 3. 获取最大轮廓并近似为四边形
print("STEP 2: 获取最大轮廓")
screenCnt = sorted(cnts, key=cv2.contourArea, reverse=True)[0] # 取面积最大的轮廓
peri = cv2.arcLength(screenCnt, True) # 计算轮廓周长
screenCnt = cv2.approxPolyDP(screenCnt, 0.05 * peri, True) # 轮廓近似为四边形
image_contour = cv2.drawContours(image.copy(), [screenCnt], -1, (0,255,0), 2)
cv2.imshow('image_contour', image_contour)
cv2.waitKey(0)

# 4. 执行透视变换
warped = four_point_transform(orig, screenCnt.reshape(4, 2) * ratio) # 还原缩放比例
cv2.imwrite('invoice_new.jpg', warped) # 保存矫正后的图像
cv2.imshow('xx', warped)
cv2.waitKey(0)

运行结果

技术核心作用关键函数
直方图统计 分析图像像素分布 cv2.calcHist()、plt.hist()
Mask 掩模 精准提取局部区域 cv2.bitwise_and()、cv2.calcHist(…, mask=mask)
全局直方图均衡化 增强整体对比度 cv2.equalizeHist()
CLAHE 均衡化 局部增强对比度,保留细节 cv2.createCLAHE()、clahe.apply()
透视变换 矫正倾斜 / 畸变图像 cv2.getPerspectiveTransform()、cv2.warpPerspective()
赞(0)
未经允许不得转载:171主机测评 » 【OpenCV 图像处理实战:从直方图到透视变换全攻略】
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址