4、图像数据读取与预处理:使用OpenCV读取图像、图像缩放与裁剪、色彩空间转换、直方图均衡化

各位同学,欢迎来到第四讲。今天咱们聊聊无人机巡检中最基础、也最绕不开的一步——图像数据的读取与预处理。

说实话,我刚开始做无人机巡检项目时,拿到一堆航拍图像,第一反应就是直接扔进算法里跑。结果呢?不是内存爆了,就是识别效果惨不忍睹。后来我才明白,预处理做得好,后续分析事半功倍;预处理偷了懒,后面全是坑

这一章,我会带着大家把OpenCV这套工具用熟。你想想看,无人机一天飞下来,少说几百张图,每张图都是几千万像素。不先做点手脚,你的电脑分分钟罢工。

核心知识点速览

  • OpenCV读取图像:支持格式、读取模式、常见坑点
  • 图像缩放与裁剪:保持比例、ROI提取、性能考量
  • 色彩空间转换:BGR↔RGB、BGR↔HSV、BGR↔灰度
  • 直方图均衡化:增强对比度、CLAHE自适应方法
图像读取与预处理 读取图像 imread() / imdecode() 缩放与裁剪 resize() / ROI切片 色彩空间转换 cvtColor() 直方图均衡化 equalizeHist() / CLAHE 四个步骤环环相扣,缺一不可

4.1 使用OpenCV读取图像

读取图像,听起来简单吧?就一行代码的事。但这里面的门道,我踩过的坑可不少。

OpenCV的imread()函数,默认读进来的是BGR格式。注意,不是RGB!我第一次用OpenCV显示图片时,发现颜色怪怪的,人脸变成了阿凡达。后来才反应过来——OpenCV用的是BGR通道顺序,而matplotlib显示用的是RGB。

import cv2
import matplotlib.pyplot as plt

# 读取图像,默认BGR
img = cv2.imread('drone_capture.jpg')

# 显示原始BGR图像(颜色会偏蓝)
plt.imshow(img)
plt.title('BGR图像(颜色异常)')
plt.show()

# 转换为RGB再显示
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
plt.imshow(img_rgb)
plt.title('RGB图像(颜色正常)')
plt.show()

我的小习惯:每次读取图像后,我第一件事就是检查通道顺序。尤其是在做无人机巡检时,很多后续算法(比如颜色分析、缺陷检测)都默认输入是RGB。提前转好,省得后面出幺蛾子。

另外,imread()的第二个参数也很关键。我常用的几个模式:

参数 含义 适用场景
cv2.IMREAD_COLOR 加载彩色图像,忽略透明度 大多数巡检场景
cv2.IMREAD_GRAYSCALE 加载为灰度图 纹理分析、边缘检测
cv2.IMREAD_UNCHANGED 保留所有通道(含Alpha) 需要透明通道时

注意:我曾经在批量处理无人机图像时,发现有些图片路径包含中文字符,imread()直接返回None。这是因为OpenCV底层用的是C++的fopen,不支持中文路径。解决方案是用cv2.imdecode()配合np.fromfile()来读取。

import numpy as np

# 解决中文路径问题
def cv_imread(path):
    img_array = np.fromfile(path, dtype=np.uint8)
    img = cv2.imdecode(img_array, cv2.IMREAD_COLOR)
    return img

img = cv_imread('无人机巡检/2024-01-15/照片_01.jpg')

4.2 图像缩放与裁剪

无人机拍的照片,分辨率动不动就是4000×3000。直接处理?你的GPU会哭的。所以缩放和裁剪是预处理的重头戏。

缩放,我一般用cv2.resize()。这里有个细节:插值方法的选择。

  • 缩小图像:用cv2.INTER_AREA,效果最好,不会出现锯齿
  • 放大图像:用cv2.INTER_LINEARcv2.INTER_CUBIC,平滑度更高
  • 追求速度:用cv2.INTER_NEAREST,但质量最差
# 缩放图像到指定尺寸
img_resized = cv2.resize(img, (640, 480), interpolation=cv2.INTER_AREA)

# 按比例缩放(保持宽高比)
height, width = img.shape[:2]
scale = 0.5  # 缩小一半
new_width = int(width * scale)
new_height = int(height * scale)
img_scaled = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_AREA)

裁剪就更直接了。OpenCV里裁剪就是数组切片操作。我在项目中经常需要从大图中提取感兴趣区域(ROI),比如只保留输电塔的部分。

# 裁剪ROI:y1:y2, x1:x2
roi = img[200:800, 300:1000]  # 提取从(300,200)到(1000,800)的区域

# 显示裁剪结果
cv2.imshow('ROI', roi)
cv2.waitKey(0)

避坑指南:我曾经在裁剪时忘了检查边界,结果程序直接崩了。建议裁剪前先判断坐标是否在图像范围内。另外,裁剪后的ROI尺寸最好能被后续算法整除(比如32的倍数),这样在送入神经网络时不会报错。

4.3 色彩空间转换

色彩空间转换,说白了就是把图像从一种颜色表示方式变成另一种。为什么需要这个?因为不同的任务对颜色敏感度不一样。

举个例子:无人机巡检中检测绝缘子表面的污秽,用BGR空间很难区分,但转到HSV空间后,色调和饱和度信息就能把污秽区域凸显出来。

常用的转换:

# BGR转灰度
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# BGR转HSV
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

# BGR转LAB(用于颜色分析)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)

我个人最常用的是HSV空间。为什么?因为H(色调)通道对光照变化不敏感。无人机在不同时间、不同角度拍摄,光照条件千差万别。用HSV做颜色阈值分割,比BGR稳定得多。

# 在HSV空间中提取绿色植被区域
lower_green = np.array([35, 40, 40])
upper_green = np.array([85, 255, 255])
mask = cv2.inRange(hsv, lower_green, upper_green)

# 应用掩码
result = cv2.bitwise_and(img, img, mask=mask)

经验之谈:做颜色阈值时,别死记硬背数值。我习惯先用一个滑动条调试工具,实时调整HSV的上下限,找到最佳阈值后再写进代码。这样效率高得多。

4.4 直方图均衡化

直方图均衡化,名字听着高大上,其实原理很简单——把图像的灰度分布拉均匀,让暗的地方变亮,亮的地方变暗,整体对比度提升。

无人机在阴天或逆光条件下拍摄,图像往往偏暗、细节丢失。这时候直方图均衡化就派上用场了。

# 灰度图直方图均衡化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
equalized = cv2.equalizeHist(gray)

# 对比显示
cv2.imshow('Original Gray', gray)
cv2.imshow('Equalized', equalized)
cv2.waitKey(0)

但普通的直方图均衡化有个问题——它会放大噪声。你想想看,原本平坦的区域(比如天空),均衡化后可能会出现块状伪影。

所以我更推荐用CLAHE(对比度受限自适应直方图均衡化)。它把图像分成小块,每块单独做均衡化,而且限制了对比度的放大倍数,效果自然得多。

# CLAHE自适应直方图均衡化
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
clahe_result = clahe.apply(gray)

# 对比三种效果
cv2.imshow('Original', gray)
cv2.imshow('Hist Equalization', equalized)
cv2.imshow('CLAHE', clahe_result)
cv2.waitKey(0)

参数调优建议:clipLimit控制对比度限制,值越大增强效果越明显,但噪声也越明显。我一般从2.0开始调。tileGridSize是分块大小,8×8适合大多数场景。如果图像纹理细腻,可以试试16×16。

对于彩色图像,我通常的做法是:转到LAB空间,只对L通道(亮度)做CLAHE,然后合并回彩色图像。这样既增强了对比度,又不会改变颜色。

# 彩色图像CLAHE(只增强亮度通道)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)

clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
l_enhanced = clahe.apply(l)

lab_enhanced = cv2.merge([l_enhanced, a, b])
img_enhanced = cv2.cvtColor(lab_enhanced, cv2.COLOR_LAB2BGR)

嗯,到这里,图像读取与预处理的四个核心操作就讲完了。你可能会问:这些操作要按什么顺序做?我的建议是:先读取→再裁剪→然后缩放→最后做色彩和对比度增强。先裁剪再缩放,能减少计算量,而且裁剪后的ROI做直方图均衡化效果更精准。

下一章我们会把这些预处理操作串成一个完整的pipeline,配合批量处理脚本,实现无人机巡检图像的自动化预处理。到时候你就知道,前期这些基础工作做得扎实,后面跑算法有多顺畅。


专注资料整理