3、Python基础回顾:Python环境配置、NumPy与OpenCV基础、图像读写与预处理

各位同学,欢迎来到第三讲。说实话,很多搞算法的朋友,模型写得飞起,但一到部署就卡壳。为啥?环境没配好,或者图像读写那几步出了幺蛾子。今天这堂课,我就带大家把Python部署环境、NumPy和OpenCV的基础操作,以及图像预处理这些“地基”给夯实了。

我个人习惯,在开始任何项目前,先把环境理清楚。你想想看,如果连图像都读不对,后面的模型推理再牛也是白搭。所以,咱们从最基础的开始。

3.1 Python环境配置:别让环境坑了你

做工业部署,我强烈建议你用 Anaconda 或者 Miniconda 来管理环境。为什么?因为工业现场的环境往往很“脏”,各种依赖版本打架是常事。Conda能帮你隔离出一个干净的空间。

我的小建议: 别用系统自带的Python。我曾经在一个产线电脑上直接装了OpenCV,结果把系统自带的Python搞崩了,重装系统花了半天。从那以后,我所有项目都用虚拟环境。

创建一个新环境,很简单:

# 创建一个名为 defect_detection 的环境,指定Python版本为3.8
conda create -n defect_detection python=3.8

# 激活环境
conda activate defect_detection

# 安装核心库
pip install numpy opencv-python matplotlib

这里要注意,opencv-python 是基础包,包含了大部分常用功能。如果你需要用到一些扩展模块(比如SIFT算法),可能需要安装 opencv-contrib-python。嗯,这里要提醒一下,工业场景下,尽量用稳定版本,别追新。

3.2 NumPy基础:图像的“骨架”

在OpenCV里,图像本质上就是一个NumPy数组。你想想看,一张640x480的彩色图,就是一个形状为 (480, 640, 3) 的三维数组。所以,搞懂NumPy,你就搞懂了图像的数据结构。

我给大家列几个最常用的操作,这些在预处理中天天见:

操作 代码示例 说明
创建数组 np.zeros((480, 640, 3), dtype=np.uint8) 创建一张全黑的图像
切片操作 roi = img[100:200, 150:250] 提取感兴趣区域(ROI),这是缺陷定位的基础
数据类型转换 img_float = img.astype(np.float32) / 255.0 模型输入前,通常要归一化到0~1
维度变换 img_transposed = np.transpose(img, (2, 0, 1)) 将HWC转为CHW,很多深度学习框架需要这种格式
避坑指南: 我曾经在部署一个模型时,发现推理结果全是黑的。排查了半天,原来是图像读进来是 uint8 类型,我直接除以255后变成了整数0。正确的做法是先转成 float32 再除。

3.3 OpenCV基础:图像读写的“瑞士军刀”

OpenCV在工业视觉中的地位,不用我多说。它稳定、高效,而且接口非常统一。咱们先看最核心的读写操作。

3.3.1 读取与显示图像

import cv2

# 读取图像,默认是BGR格式
img = cv2.imread('defect_sample.jpg')

# 检查是否读取成功
if img is None:
    print("图像读取失败,请检查路径")
    exit()

# 显示图像
cv2.imshow('Defect Image', img)
cv2.waitKey(0)  # 等待按键
cv2.destroyAllWindows()

这里有个坑,大家一定要注意:OpenCV默认读取的是BGR格式,而不是我们常见的RGB。很多同学第一次用的时候,直接用matplotlib显示,结果颜色全乱了。为什么会这样?因为matplotlib用的是RGB。

重要提醒: 如果你要把图像传给模型(比如PyTorch),记得做颜色空间转换:img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)。否则模型学到的特征全是错的。

3.3.2 保存图像

# 保存处理后的图像
cv2.imwrite('result.jpg', img_processed)

保存时,OpenCV会根据文件后缀名自动选择编码格式。我个人习惯用PNG格式保存中间结果,因为它是无损压缩,不会丢失细节。

3.4 图像预处理:模型推理前的“必修课”

模型不是万能的,它需要输入特定尺寸、特定范围的数值。预处理的目的,就是把原始图像“喂”成模型喜欢的样子。

3.4.1 尺寸调整

工业相机拍出来的图,分辨率可能很高(比如500万像素)。但模型输入通常是固定的,比如224x224或416x416。所以第一步就是缩放。

# 缩放到模型输入尺寸
resized_img = cv2.resize(img, (224, 224), interpolation=cv2.INTER_LINEAR)

插值方法怎么选?我一般这样用:

  • 缩小图像:用 cv2.INTER_AREA,能避免出现摩尔纹。
  • 放大图像:用 cv2.INTER_LINEARcv2.INTER_CUBIC,效果更平滑。

3.4.2 归一化

归一化是让模型训练和推理更稳定的关键。说白了,就是把像素值从0~255映射到0~1或者-1~1。

# 简单的归一化到 [0, 1]
normalized_img = img.astype(np.float32) / 255.0

# 更常用的方式:减去均值,除以标准差
mean = [0.485, 0.456, 0.406]  # ImageNet的均值
std = [0.229, 0.224, 0.225]   # ImageNet的标准差
normalized_img = (normalized_img - mean) / std
我的经验: 如果你用的是自己训练的模型,归一化参数一定要和训练时保持一致。我曾经帮一个客户排查问题,发现他部署时用了不同的均值,结果准确率从95%掉到了60%。嗯,这种低级错误,犯一次就记住了。

3.4.3 数据增强(推理时也要做)

很多人以为数据增强只在训练时用。其实在推理时,为了提升鲁棒性,我们也会做一些简单的预处理。比如:

  • 亮度调整:模拟产线上光照变化。
  • 高斯模糊:模拟镜头轻微失焦。
  • 仿射变换:校正拍摄角度偏差。
# 高斯模糊,去除传感器噪声
blurred = cv2.GaussianBlur(img, (5, 5), 1.5)

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

这些操作看似简单,但在实际项目中,往往能带来几个点的准确率提升。你想想看,产线上的工件位置、光照不可能完全一致,预处理就是帮模型“抗干扰”。

3.5 一个完整的预处理流水线

最后,我给大家展示一个我在项目中常用的预处理函数。它把上面讲的内容串起来了:

def preprocess_for_inference(image_path, input_size=(224, 224)):
    """
    工业缺陷检测模型的标准预处理流程
    """
    # 1. 读取图像
    img = cv2.imread(image_path)
    if img is None:
        raise ValueError(f"无法读取图像: {image_path}")

    # 2. 转为RGB
    img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

    # 3. 缩放
    img_resized = cv2.resize(img_rgb, input_size, interpolation=cv2.INTER_AREA)

    # 4. 归一化
    img_normalized = img_resized.astype(np.float32) / 255.0

    # 5. 标准化(使用ImageNet参数)
    mean = np.array([0.485, 0.456, 0.406])
    std = np.array([0.229, 0.224, 0.225])
    img_standardized = (img_normalized - mean) / std

    # 6. 转为CHW格式并增加batch维度
    img_chw = np.transpose(img_standardized, (2, 0, 1))
    img_batch = np.expand_dims(img_chw, axis=0)

    return img_batch

这个函数,你直接复制到你的部署代码里,稍微改改路径和尺寸就能用。我每次做新项目,都是从这个模板开始的。

好了,这一讲的内容就到这里。环境配好了,图像能读能写了,预处理也搞定了。下一讲,咱们就要开始真正接触模型了——把训练好的模型加载进来,跑一次推理试试看。