OpenCV基本操作

参考:《计算机视觉开发实践》朱文伟 李建英 著

3.1 OpenCV 架构概览

opencv-python 的架构与 C/C++ 版本一致,Python 层仅提供函数封装。常用模块:

  • core:核心功能,包含基本数据结构、绘图函数、数组操作和动态数据结构。
  • dnn:深度学习模块(OpenCV 4 特性),支持加载序列化模型与前向推理(不支持训练)。
  • features2d:特征点相关(检测、描述、匹配)。
  • flann:高维近似最近邻搜索与聚类。
  • gapi:图像处理加速框架。
  • highgui:图形界面(窗口、鼠标、键盘、可视化交互)。
  • imgcodecs:图像文件读写。
  • imgproc:图像处理(滤波、几何变换、直方图、特征/目标检测等)。
  • ml:机器学习(分类、回归、聚类)。
  • objdetect:目标检测(如 Haar 特征)。
  • photo:计算摄影(修复、去噪)。
  • stitching:图像拼接(匹配、旋转估计、自动校准、接缝估计等)。
  • video:视频分析(运动估计、背景分离、跟踪)。
  • videoio:视频/图像序列 读写。

3.2 图像输入输出模块 imgcodecs

3.2.1 读取图像(cv2.imread)

函数用于从文件读取图像:

cv2.imread(filename[, flags]) -> retval
  • flags 取值:
  • cv.IMREAD_ANYDEPTH(2):若原图为 16/32 位深度,则按对应深度返回,否则转 8 位。
  • cv.IMREAD_COLOR(1):读取为彩色 BGR(3 通道)。
  • cv.IMREAD_GRAYSCALE(0):读取为灰度(1 通道)。
  • cv.IMREAD_UNCHANGED(-1):不做改变,按原图返回(含 alpha 时保留)。
  • 返回:读取成功返回包含像素数据的 NumPy 数组;失败返回 None(如文件不存在/权限问题/格式不支持)。

示例:

import sys
import cv2 as cv

img = cv.imread("p1.jpg")
if img is None:
    sys.exit("Could not read the p1.jpg.")
  • 提示:imread 根据文件内容判断格式,而非扩展名。
  • 中文路径示例(使用 imdecode):
import cv2 as cv
import numpy as np

imgpath = r"中文路径/图片.png"
img = cv.imdecode(np.fromfile(imgpath, dtype=np.uint8), -1)

3.2.2 图像尺寸与通道(shape)

读取成功后得到的是 NumPy 数组,可使用 shape 获取尺寸:

  • shape[0]:行数(高度 height)
  • shape[1]:列数(宽度 width)
  • shape[2]:通道数(如 3 表示 BGR)

3.2.3 保存图像(cv2.imwrite)

保存图像到文件:

cv2.imwrite(filename, img[, params]) -> retval
  • filename:目标文件名(含后缀,如 123.png)。
  • img:要保存的图像(NumPy 数组)。
  • params:特定格式保存参数(可选)。

常见支持:

  • 8 位单通道或 BGR 三通道图像。
  • 16 位无符号(CV_16U):可保存为 PNG/JPEG/TIFF。
  • 32 位浮点(CV_32F):可保存为 PFM/TIFF/OpenEXR/Radiance HDR。
  • 3 通道(CV_32FC3)TIFF 使用 LogLuv 高动态范围编码。
  • 支持带 alpha 的 PNG:使用 8/16 位的 BGRA(alpha 在最后,0 透明,255/65535 不透明)。
  • 如格式/深度/通道顺序不同,保存前可用 convertTo/cvtColor 转换;或使用通用存储(XML/YAML)。

3.3 OpenCV 界面编程(HighGUI)

HighGUI 提供窗口、控件与事件处理(鼠标、键盘、滑动条等),便于图像可视化与交互。

3.3.1 新建窗口(cv2.namedWindow)

cv2.namedWindow(winname[, flags]) -> None
  • flags:
  • cv.WINDOW_AUTOSIZE:窗口大小自适应图片,且不可手动更改。
  • cv.WINDOW_NORMAL:允许用户改变窗口大小。
  • cv.WINDOW_OPENGL:创建支持 OpenGL 的窗口。

3.3.2 显示图像(cv2.imshow)与显示规则

cv2.imshow(winname, mat) -> None

不同深度的显示规则:

  • 8U:直接显示。
  • 16U/32S:内部将像素值除以 256 映射到 [0, 255]。
  • 32F/64F:内部将像素值乘以 255 映射到 [0, 255](需先归一化到 [0, 1])。

3.3.3 驻留屏幕与键盘监听(cv2.waitKey)

cv2.waitKey([delay]) -> retval
  • delay <= 0:无限等待键盘事件;> 0:等待 delay 毫秒。
  • imshow 之后应调用 waitKey,否则窗口可能瞬间关闭。
  • 返回值为按键 ASCII 码,超时未按键返回 -1。

3.3.4 单窗口显示多幅图像(numpy.hstack/vstack)

  • numpy.hstack(tup):将行数相同的数组/矩阵按列方向水平拼接。
  • 在多路视频/图像预览时,可先统一尺寸后水平/垂直拼接到一个窗口中显示。

3.3.5 销毁窗口

  • cv2.destroyWindow(winname) -> None:销毁指定窗口。
  • cv2.destroyAllWindows() -> None:销毁所有窗口。

3.3.6 调整窗口大小(cv2.resizeWindow)

cv2.resizeWindow(winname, width, height) -> None
  • 需先以 cv.WINDOW_NORMAL 创建窗口,方可调整大小。

3.3.7 鼠标事件回调(cv2.setMouseCallback)

cv2.setMouseCallback(windowName, onMouse, param=None) -> None

回调签名:

def MouseCallback(event, x, y, flags, param):
    ...
  • event:鼠标事件类型;x/y:坐标;flags:事件标志;param:用户参数。

3.3.8 键盘回调(基于 waitKey)

  • 通过 cv2.waitKey 的返回值判断按键并编写响应逻辑。
  • 常与 cv2.imshow 搭配用于交互式处理流程。

3.3.9 滑动条(cv2.createTrackbar / getTrackbarPos / setTrackbarPos)

创建滑动条:

cv2.createTrackbar(trackbarName, windowName, value, count, onChange) -> None
  • trackbarName:滑动条名称;windowName:父窗口名称。
  • value:初始值(整数,可变);count:最大值;onChange:回调函数。

回调签名:

def TrackbarCallback(pos):
    ...
  • pos:滚动块当前位置。
  • 若不需要回调,可传 None,此时仅通过变量值变化响应。

读取/设置滑动条:

  • cv2.getTrackbarPos(trackbarName, windowName) -> int
  • cv2.setTrackbarPos(trackbarName, windowName, pos) -> None

Demo(直接copy,记得更改图片地址)

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
OpenCV 基本操作演示脚本
- 读取/保存图像:cv2.imread / cv2.imdecode(支持中文路径)/ cv2.imwrite
- 图像尺寸与通道:shape
- HighGUI:namedWindow / imshow / waitKey / destroyAllWindows / resizeWindow
- 单窗口多图:numpy.hstack / numpy.vstack
- 鼠标事件:cv2.setMouseCallback(左键添加点,右键撤销)
- 键盘事件:基于 waitKey(s 保存, r 重置, q/ESC 退出)
- 滑动条:cv2.createTrackbar / getTrackbarPos / setTrackbarPos(亮度、对比、模糊、灰度开关)
运行:python opencv_demo.py --image "你的图片路径(可含中文)"
"""

import os
import sys
import cv2 as cv
import numpy as np
import argparse
from datetime import datetime


def setup_console_utf8():
    """尽量让控制台按 UTF-8 编码,避免中文输出乱码(尤其是 Windows)。"""
    # Python 3.7+ 支持 reconfigure
    for stream_name in ("stdin", "stdout", "stderr"):
        try:
            stream = getattr(sys, stream_name)
            if hasattr(stream, "reconfigure"):
                stream.reconfigure(encoding="utf-8", errors="replace")
        except Exception:
            pass
    # Windows 下设置控制台代码页为 UTF-8
    if os.name == 'nt':
        try:
            import ctypes  # noqa: F401
            ctypes.windll.kernel32.SetConsoleCP(65001)
            ctypes.windll.kernel32.SetConsoleOutputCP(65001)
        except Exception:
            pass


setup_console_utf8()


def imread_smart(path: str, flags: int = cv.IMREAD_UNCHANGED):
    """读取图像,优先使用 imdecode 以支持中文路径。"""
    if path and os.path.exists(path):
        try:
            data = np.fromfile(path, dtype=np.uint8)
            img = cv.imdecode(data, flags)
            return img
        except Exception:
            return cv.imread(path, flags)
    return None


def make_synthetic(w: int = 640, h: int = 360) -> np.ndarray:
    """生成一张合成测试图像。"""
    x = np.linspace(0, 255, w, dtype=np.uint8)
    y = np.linspace(0, 255, h, dtype=np.uint8)
    xv, yv = np.meshgrid(x, y)
    img = np.dstack((xv, yv, ((xv + yv) // 2).astype(np.uint8)))
    cv.circle(img, (w // 2, h // 2), min(w, h) // 4, (0, 255, 255), 4)
    cv.putText(img, 'Synthetic', (20, 40), cv.FONT_HERSHEY_SIMPLEX, 1, (255, 255, 255), 2, cv.LINE_AA)
    return img


def to_bgr(img: np.ndarray) -> np.ndarray:
    """统一转为 BGR 三通道,便于后续处理与拼接。"""
    if img is None:
        return None
    if len(img.shape) == 2:
        return cv.cvtColor(img, cv.COLOR_GRAY2BGR)
    if img.shape[2] == 4:
        return cv.cvtColor(img, cv.COLOR_BGRA2BGR)
    return img


points: list[tuple[int, int]] = []


def on_mouse(event, x, y, flags, param):
    """鼠标事件:左键添加点,右键撤销最近点。"""
    global points
    if event == cv.EVENT_LBUTTONDOWN:
        points.append((x, y))
    elif event == cv.EVENT_RBUTTONDOWN:
        if points:
            points.pop()


def ensure_odd(k: int) -> int:
    return k if k % 2 == 1 else k + 1


def resize_to(img: np.ndarray, size: tuple[int, int]) -> np.ndarray:
    return cv.resize(img, size, interpolation=cv.INTER_AREA)


def annotate(img: np.ndarray, text: str, org=(10, 25)) -> np.ndarray:
    out = img.copy()
    cv.putText(out, text, org, cv.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2, cv.LINE_AA)
    return out


def main():
    parser = argparse.ArgumentParser(description='OpenCV 基本操作演示')
    parser.add_argument('--image', '-i', type=str, default='D:\\Unity\\Python\\OpenCV\\03-opencv基本操作\\test.jpg', help='文件路径')
    parser.add_argument('--width', type=int, default=400, help='1024')
    parser.add_argument('--height', type=int, default=300, help='1024')
    args = parser.parse_args()

    img = imread_smart(args.image) if args.image else None
    if img is None:
        img = make_synthetic()
        print('未提供或无法读取图片,使用合成图像。')
    img = to_bgr(img)

    h, w = img.shape[0], img.shape[1]
    c = img.shape[2] if len(img.shape) == 3 else 1
    print(f'原图尺寸: width={w}, height={h}, channels={c}')

    win = 'OpenCV Demo'
    cv.namedWindow(win, cv.WINDOW_NORMAL)
    cv.resizeWindow(win, 2 * args.width + 60, 2 * args.height + 120)

    # 滑动条:亮度、对比度、模糊、灰度开关
    cv.createTrackbar('Brightness', win, 100, 200, lambda v: None)  # 0..200, beta=v-100
    cv.createTrackbar('Contrast', win, 100, 200, lambda v: None)  # 0..200, alpha=v/100
    cv.createTrackbar('Blur', win, 0, 20, lambda v: None)  # 0..20, kernel=2*v+1
    cv.createTrackbar('Gray', win, 0, 1, lambda v: None)  # 0/1

    # 鼠标事件
    cv.setMouseCallback(win, on_mouse, None)

    while True:
        b = cv.getTrackbarPos('Brightness', win)
        cval = cv.getTrackbarPos('Contrast', win)
        blur = cv.getTrackbarPos('Blur', win)
        grayflag = cv.getTrackbarPos('Gray', win)

        alpha = max(cval, 1) / 100.0
        beta = b - 100
        processed = cv.convertScaleAbs(img, alpha=alpha, beta=beta)

        if blur > 0:
            k = ensure_odd(2 * blur + 1)
            processed = cv.GaussianBlur(processed, (k, k), 0)

        gray = cv.cvtColor(processed, cv.COLOR_BGR2GRAY) if grayflag else cv.cvtColor(img, cv.COLOR_BGR2GRAY)
        gray_bgr = cv.cvtColor(gray, cv.COLOR_GRAY2BGR)

        # 绘制鼠标点
        overlay = processed.copy()
        for i, (px, py) in enumerate(points):
            cv.circle(overlay, (px, py), 5, (0, 0, 255), -1, cv.LINE_AA)
            cv.putText(overlay, f'{i + 1}:({px},{py})', (px + 8, py - 8), cv.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 1,
                       cv.LINE_AA)

        # 准备拼接视图
        tile_w, tile_h = args.width, args.height
        tile1 = resize_to(img, (tile_w, tile_h))
        tile1 = annotate(tile1, f'Original {w}x{h}x{c}')
        tile2 = resize_to(overlay, (tile_w, tile_h))
        tile2 = annotate(tile2, f'Processed a={alpha:.2f} b={beta} blur={blur}')
        tile3 = resize_to(gray_bgr, (tile_w, tile_h))
        tile3 = annotate(tile3, 'Gray' if grayflag else 'Gray (from original)')

        # 辅助图:Canny 边缘
        edges = cv.Canny(gray, 50, 150)
        edges_bgr = cv.cvtColor(edges, cv.COLOR_GRAY2BGR)
        tile4 = resize_to(edges_bgr, (tile_w, tile_h))
        tile4 = annotate(tile4, 'Canny')

        top = np.hstack([tile1, tile2])
        bottom = np.hstack([tile3, tile4])
        canvas = np.vstack([top, bottom])

        cv.imshow(win, canvas)
        key = cv.waitKey(1) & 0xFF

        if key == ord('s'):
            out_name = f'output_{datetime.now().strftime("%Y%m%d_%H%M%S")}.png'
            cv.imwrite(out_name, canvas)
            print(f'已保存: {out_name}')
        elif key == ord('r'):
            # 重置滑动条与标注点
            cv.setTrackbarPos('Brightness', win, 100)
            cv.setTrackbarPos('Contrast', win, 100)
            cv.setTrackbarPos('Blur', win, 0)
            cv.setTrackbarPos('Gray', win, 0)
            points.clear()
            print('已重置参数与标注点。')
        elif key in (27, ord('q')):
            break

    cv.destroyAllWindows()


if __name__ == '__main__':
    main()

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇