OpenCV 官翻 1 -介绍、安装、功能概览、核心操作

发布于:2025-07-22 ⋅ 阅读:(18) ⋅ 点赞:(0)

文章目录


OpenCV-Python 教程简介

https://docs.opencv.org/4.x/d0/de3/tutorial_py_intro.html


OpenCV

OpenCV由Gary Bradsky于1999年在英特尔公司创立,首个版本于2000年发布。随后Vadim Pisarevsky加入Gary Bradsky共同管理英特尔的俄罗斯软件OpenCV团队。2005年,OpenCV被应用于赢得DARPA Grand Challenge的自动驾驶汽车Stanley上。此后,在Willow Garage的支持下,Gary Bradsky和Vadim Pisarevsky继续领导该项目进行积极开发。如今OpenCV已支持大量计算机视觉与机器学习相关算法,并且功能仍在持续扩展。

OpenCV支持多种编程语言如C++、Python、Java等,并能运行于Windows、Linux、OS X、Android和iOS等不同平台。基于CUDA和OpenCL的高速GPU运算接口也正处于积极开发中。

OpenCV-Python是OpenCV的Python API,它完美融合了OpenCV C++ API的强大功能与Python语言的优雅特性。


OpenCV-Python

OpenCV-Python 是一个专为解决计算机视觉问题而设计的 Python 绑定库。

Python 是由 Guido van Rossum 创建的一种通用编程语言,因其简洁性和代码可读性而迅速流行起来。它让程序员能用更少的代码行数表达想法,同时保持代码可读性。

与 C/C++ 等语言相比,Python 运行速度较慢。但 Python 可以轻松通过 C/C++ 进行扩展,这使得我们能够用 C/C++ 编写计算密集型代码,并创建可作为 Python 模块使用的 Python 封装器。这带来两大优势:首先,代码运行速度与原生的 C/C++ 代码相当(因为实际在后台运行的是 C++ 代码);其次,用 Python 编写代码比 C/C++ 更简单。OpenCV-Python 正是原始 OpenCV C++ 实现的 Python 封装。

OpenCV-Python 利用了 Numpy——这是一个采用 MATLAB 风格语法、高度优化的数值运算库。所有 OpenCV 数组结构都会与 Numpy 数组相互转换。这也使得它更容易与 SciPy 和 Matplotlib 等其他使用 Numpy 的库集成。


OpenCV-Python 教程

OpenCV 推出了一系列新教程,将引导您了解 OpenCV-Python 中的各种功能。本指南主要针对 OpenCV 3.x 版本(尽管大多数教程也适用于 OpenCV 2.x)。

建议您具备 Python 和 Numpy 的基础知识,因为本指南不会涵盖这些内容。要使用 OpenCV-Python 编写优化代码,必须熟练掌握 Numpy。

本教程最初由 Abid Rahman K.Alexander Mordvintsev 的指导下,作为 Google Summer of Code 2013 项目的一部分创建。


OpenCV 需要你!!!

由于 OpenCV 是一个开源项目,我们欢迎所有人对代码库、文档和教程做出贡献。如果你在本教程中发现任何错误(从小的拼写错误到代码或概念中的严重错误),都可以通过克隆 GitHub 上的 OpenCV 并提交拉取请求来修正。OpenCV 开发者会审核你的拉取请求,提供重要反馈,一旦通过审核,你的修改就会被合并到 OpenCV 中。这样,你就成为了一名开源贡献者 😃

随着新模块不断加入 OpenCV-Python,本教程也需要相应扩展。如果你熟悉某个特定算法,并能撰写包含算法基础理论及示例代码的教程,请务必参与进来。

请记住,我们携手才能让这个项目取得巨大成功!!!


贡献者

以下是向 OpenCV-Python 提交教程的贡献者名单:

  1. Alexander Mordvintsev (GSoC-2013 导师)
  2. Abid Rahman K. (GSoC-2013 实习生)

额外资源

1、Python 快速指南 - A Byte of Python

2、Python 初学者指南
3、NumPy 快速入门教程
4、NumPy 参考文档
5、OpenCV 官方文档
6、OpenCV 论坛


生成于 2025年4月30日 星期三 23:08:43,由 doxygen 1.12.0 为 OpenCV 生成



在Windows上安装OpenCV-Python

https://docs.opencv.org/4.x/d5/de5/tutorial_py_setup_in_windows.html


目标

在本教程中:

  • 我们将学习如何在您的Windows系统中设置OpenCV-Python。

以下步骤已在Windows 7 64位系统上通过Visual Studio 2010和Visual Studio 2012测试通过,截图展示的是VS2012环境。


从预编译二进制文件安装OpenCV

1、需要下载并安装以下Python包到默认位置:
1、从这里下载Python 3.x(3.4+)或Python 2.7.x。
2、Numpy包(例如使用pip install numpy命令安装)。
3、Matplotlib(pip install matplotlib)(Matplotlib是可选的,但由于我们在教程中经常使用,因此推荐安装)。

2、将所有软件包安装到默认位置。对于Python 2.7,Python将被安装到C:/Python27/

3、安装完成后,打开Python IDLE。输入import numpy并确认Numpy能正常工作。

4、从GitHubSourceForge网站下载最新版OpenCV,双击解压。

5、进入opencv/build/python/2.7文件夹。

6、将cv2.pyd复制到C:/Python27/lib/site-packages

7、将opencv_world.dll文件复制到C:/Python27/lib/site-packages

8、打开Python IDLE,在Python终端中输入以下代码。

>>> import cv2 as cv
>>> print( cv.__version__ )

如果结果打印无误,恭喜!!!您已成功安装 OpenCV-Python。


从源码构建OpenCV

1、下载并安装Visual Studio和CMake
1、Visual Studio 2012
2、CMake

2、下载必要的Python包并安装到默认位置
1、Python
2、Numpy
注意:这里我们使用32位的Python包二进制文件。但如果需要x64版的OpenCV,则需要安装64位的Python包。问题在于Numpy没有官方64位版本,需自行编译。编译时需使用与Python相同的编译器(启动Python IDLE时会显示编译器信息)。更多参考信息。因此系统中必须安装相同版本的Visual Studio来编译Numpy。

另一种获取64位Python包的方法是使用第三方发行版如AnacondaEnthought等。这些发行版体积较大但包含完整环境,也可下载32位版本。

3、确保Python和Numpy正常运行
4、下载OpenCV源码:可从Sourceforge获取官方发布版,或从Github获取最新源码
5、解压到opencv文件夹,并在其中新建build文件夹
6、打开CMake-gui(开始菜单 > 所有程序 > CMake-gui
7、按以下步骤填写字段(参考下图):
1、点击Browse Source…选择opencv文件夹
2、点击
Browse Build…选择之前创建的build文件夹
3、点击
Configure

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
1、在弹出的编译器选择窗口中选择对应版本(此处选Visual Studio 11),点击Finish
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
1、等待分析完成

8、所有字段将显示为红色。点击WITH字段展开,此处选择需要启用的额外功能(参考下图):
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、展开BUILD字段,前几个选项用于配置构建方式(参考下图):
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、其余字段指定需要构建的模块。由于OpenCV-Python暂不支持GPU模块,可取消勾选以节省时间(如需使用则保留)。参考下图:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、展开ENABLE字段,确保取消勾选ENABLE_SOLUTION_FOLDERS(Visual Studio Express版不支持解决方案文件夹)。参考下图:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、检查PYTHON字段是否全部填写完整(忽略PYTHON_DEBUG_LIBRARY)。参考下图:
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、最后点击Generate按钮
2、进入opencv/build文件夹,用Visual Studio打开OpenCV.sln文件
3、将构建模式设为Release而非Debug
4、在解决方案资源管理器中右键点击Solution(或ALL_BUILD)进行构建,此过程需要较长时间
5、再次右键点击INSTALL进行构建,此时将安装OpenCV-Python
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1、打开Python IDLE输入import cv2 as cv,若无报错则表示安装成功

注意:本教程未包含TBB、Eigen、Qt、文档等附加支持,相关配置较为复杂。后续将补充详细视频教程,您也可自行探索配置方法。


练习

如果你使用的是 Windows 机器,请从源代码编译 OpenCV。尝试各种修改操作。如果遇到任何问题,可以访问 OpenCV 论坛并详细描述你遇到的问题。


本文档由 doxygen 1.12.0 生成于 2025年4月30日 星期三 23:08:43,适用于 OpenCV


在Fedora中安装OpenCV-Python

https://docs.opencv.org/4.x/dd/dd5/tutorial_py_setup_in_fedora.html


目标

在本教程中,我们将学习如何在 Fedora 系统上配置 OpenCV-Python。以下步骤已在 Fedora 18(64 位)和 Fedora 19(32 位)系统中通过测试。


简介

在 Fedora 系统上可以通过两种方式安装 OpenCV-Python:1) 从 Fedora 软件仓库提供的预编译二进制包安装;2) 从源代码编译安装。本节将介绍这两种方法。

另一个重要事项是所需的附加库。OpenCV-Python 仅依赖 Numpy(以及其他稍后会提到的依赖项)。但在本教程中,我们还会使用 Matplotlib 来实现简单美观的绘图功能(我认为它比 OpenCV 自带的绘图工具更好用)。Matplotlib 是可选的,但强烈推荐安装。同样,我们也会用到 IPython——一个交互式 Python 终端,这也是一款强烈推荐的工具。


从预编译二进制文件安装 OpenCV-Python

在终端中以 root 身份运行以下命令安装所有软件包。

$ yum install numpy opencv*

打开 Python IDLE(或 IPython),在 Python 终端中输入以下代码。

>>> import cv2 as cv
>>> print( cv.__version__ )

如果结果打印无误,恭喜你!!!已成功安装 OpenCV-Python。

这种方式非常简单,但存在一个问题:Yum 软件仓库可能不会始终包含最新版本的 OpenCV。例如,撰写本教程时,Yum 仓库提供的是 2.4.5 版,而最新 OpenCV 版本已是 2.4.6。就 Python API 而言,最新版本总能提供更完善的支持。此外,根据驱动程序、ffmpeg、gstreamer 等软件包的情况,还可能出现摄像头支持或视频播放等问题。

因此,我个人更推荐下一种方法——从源代码编译。而且,如果你将来想为 OpenCV 贡献代码,也必须掌握这种方式。


从源码安装 OpenCV

虽然一开始从源码编译看起来有点复杂,但一旦成功完成,就会发现其实并不困难。

首先我们需要安装一些依赖项。其中有些是必需的,有些是可选的。如果你不需要,可以跳过那些可选的依赖项。


必需依赖项

我们需要使用 CMake 来配置安装环境,GCC 进行编译,以及 Python-develNumpy 来创建 Python 扩展等。

yum install cmake
yum install python-devel numpy
yum install gcc gcc-c++

接下来我们需要GTK来支持GUI功能、相机支持(libdc1394、v4l)以及媒体支持(ffmpeg、gstreamer)等。

yum install gtk2-devel
yum install libdc1394-devel
yum install ffmpeg-devel
yum install gstreamer-plugins-base-devel

可选依赖项

上述依赖项足以在您的 Fedora 机器上安装 OpenCV。但根据需求,您可能需要一些额外的依赖项。以下是可选依赖项的列表,是否安装由您决定 😃

OpenCV 自带了支持 PNG、JPEG、JPEG2000、TIFF、WebP 等图像格式的文件,但这些文件版本可能较旧。如需获取最新的库文件,您可以安装这些格式的开发文件。

yum install libpng-devel
yum install libjpeg-turbo-devel
yum install jasper-devel
yum install openexr-devel
yum install libtiff-devel
yum install libwebp-devel

多个 OpenCV 函数通过 Intel 线程构建模块 (TBB) 实现了并行化。但若要启用该功能,需先安装 TBB。(此外,使用 CMake 配置安装时,别忘了添加 -D WITH_TBB=ON 参数。更多细节见下文。)


yum install tbb-devel

OpenCV 使用另一个库 Eigen 来优化数学运算。因此,如果您的系统已安装 Eigen,就可以利用它。(此外,在使用 CMake 配置安装时,别忘了传递参数 -D WITH_EIGEN=ON。更多细节如下。)


yum install eigen3-devel

如果你想构建文档是的,你可以在系统中创建OpenCV完整官方文档的离线HTML版本,具备全文搜索功能,这样遇到问题时无需总是联网查阅,而且速度非常快!!!),你需要安装Doxygen(一个文档生成工具)。

yum install doxygen

下载OpenCV

接下来我们需要下载OpenCV。您可以从sourceforge网站下载最新版本的OpenCV,然后解压文件夹。

或者您也可以从OpenCV的github仓库下载最新源代码(如果您想为OpenCV做贡献,请选择此方式。这将始终保持您的OpenCV为最新版本)。为此,您需要先安装Git

yum install git
git clone https://github.com/opencv/opencv.git

它会在主目录(或你指定的目录)下创建一个名为 OpenCV 的文件夹。克隆过程所需时间取决于你的网络连接速度。

现在打开终端窗口,进入下载的 OpenCV 文件夹。创建一个新的 build 文件夹并进入该目录。

mkdir build
cd build

配置与安装

现在我们已经安装了所有必需的依赖项,接下来开始安装OpenCV。安装过程需要通过CMake进行配置。配置内容包括指定要安装的模块、安装路径、使用的附加库、是否编译文档和示例等。通常使用以下命令进行配置(在构建文件夹中执行)。

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..

它指定构建类型为"Release Mode",安装路径为/usr/local。注意每个选项前的-D参数和末尾的…符号。简而言之,格式如下:

cmake [-D <flag>] [-D <flag>] ..

你可以指定任意数量的标志,但每个标志前都需要加上 -D

在本教程中,我们将安装支持 TBB 和 Eigen 的 OpenCV。我们还会构建文档,但排除性能测试和示例构建。同时禁用 GPU 相关模块(由于使用 OpenCV-Python,不需要 GPU 相关模块,这样可以节省一些时间)。

(以下所有命令可以合并为一条 cmake 语句,但为了便于理解,这里进行了拆分。)

  • 启用 TBB 和 Eigen 支持:
cmake -D WITH_TBB=ON -D WITH_EIGEN=ON ..

  • 启用文档功能并禁用测试和示例

cmake -D BUILD_DOCS=ON -D BUILD_TESTS=OFF -D BUILD_PERF_TESTS=OFF -D BUILD_EXAMPLES=OFF ..

  • 禁用所有与GPU相关的模块。
cmake -D WITH_OPENCL=OFF -D BUILD_opencv_gpu=OFF -D BUILD_opencv_gpuarithm=OFF -D BUILD_opencv_gpubgsegm=OFF -D BUILD_opencv_gpucodec=OFF -D BUILD_opencv_gpufeatures2d=OFF -D BUILD_opencv_gpufilters=OFF -D BUILD_opencv_gpuimgproc=OFF -D BUILD_opencv_gpulegacy=OFF -D BUILD_opencv_gpuoptflow=OFF -D BUILD_opencv_gpustereo=OFF -D BUILD_opencv_gpuwarping=OFF ..

  • 设置安装路径和构建类型

cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..

每次执行 cmake 语句时,它都会打印出最终的配置信息。请确保您获得的最终配置中包含以下字段(以下是我配置中的关键部分示例)。这些字段在您的系统中也应正确填写,否则可能存在问题。请检查是否已正确执行上述步骤。

...
-- GUI:
-- GTK+ 2.x: YES (ver 2.24.19)
-- GThread : YES (ver 2.36.3)
 
-- Video I/O:
-- DC1394 2.x: YES (ver 2.2.0)
-- FFMPEG: YES
-- codec: YES (ver 54.92.100)
-- format: YES (ver 54.63.104)
-- util: YES (ver 52.18.100)
-- swscale: YES (ver 2.2.100)
-- gentoo-style: YES
-- GStreamer:
-- base: YES (ver 0.10.36)
-- video: YES (ver 0.10.36)
-- app: YES (ver 0.10.36)
-- riff: YES (ver 0.10.36)
-- pbutils: YES (ver 0.10.36)
 
-- V4L/V4L2: Using libv4l (ver 1.0.0)
 
-- Other third-party libraries:
-- Use Eigen: YES (ver 3.1.4)
-- Use TBB: YES (ver 4.0 interface 6004)
 
-- Python:
-- Interpreter: /usr/bin/python2 (ver 2.7.5)
-- Libraries: /lib/libpython2.7.so (ver 2.7.5)
-- numpy: /usr/lib/python2.7/site-packages/numpy/core/include (ver 1.7.1)
-- packages path: lib/python2.7/site-packages
 
...

还有许多其他标志和设置可供探索,留待您进一步发掘。

现在您可以使用make命令构建文件,并通过make install命令进行安装。注意make install需要以root权限执行。

make
su
make install

安装已完成。所有文件已安装到 /usr/local/ 目录下。但要使用它,您的 Python 必须能够找到 OpenCV 模块。您有两种选择:

  1. 将模块移动到 Python Path 中的任意文件夹:通过在 Python 终端输入 import sys; print(sys.path) 可以查看 Python 路径。它会输出多个路径位置。将 /usr/local/lib/python2.7/site-packages/cv2.so 移动到其中任意一个文件夹即可。例如,

su mv /usr/local/lib/python2.7/site-packages/cv2.so /usr/lib/python2.7/site-packages


但每次安装 OpenCV 时都需要执行此操作。


2、将 /usr/local/lib/python2.7/site-packages 添加到 PYTHON_PATH:此操作只需执行一次。只需打开/.bashrc 文件并添加以下行,然后注销并重新登录即可。

export PYTHONPATH=$PYTHONPATH:/usr/local/lib/python2.7/site-packages

OpenCV 安装已完成。打开终端并尝试执行 import cv2 as cv

要构建文档,只需输入以下命令:

make doxygen

然后打开 opencv/build/doc/doxygen/html/index.html 并在浏览器中为其添加书签。


练习

1、在你的Fedora机器上从源码编译OpenCV。


生成于2025年4月30日 星期三 23:08:43,使用doxygen 1.12.0为OpenCV生成


在Ubuntu中设置OpenCV的Python环境教程


在Ubuntu中安装OpenCV-Python


目标

在本教程中,我们将学习在Ubuntu系统中安装OpenCV-Python。以下步骤已在Ubuntu 16.04和18.04(均为64位版本)上测试通过。

在Ubuntu系统中,可以通过两种方式安装OpenCV-Python:

  • 从Ubuntu软件仓库提供的预编译二进制包安装
  • 从源代码编译安装。本节中,我们将介绍这两种方法。

另一个重要事项是所需的附加库。OpenCV-Python仅需要Numpy(以及其他一些依赖项,我们稍后会看到)。但在本教程中,我们还会使用Matplotlib来实现一些简单美观的绘图功能(相比OpenCV的原生绘图功能,我认为Matplotlib更胜一筹)。Matplotlib是可选的,但强烈推荐安装。同样,我们也将使用IPython——一个交互式Python终端,这也是一款强烈推荐的工具。


通过预编译二进制文件安装 OpenCV-Python

此方法最适合仅用于编程和开发 OpenCV 应用程序的场景。

在终端中以 root 用户身份执行以下命令安装 python3-opencv 包:

$ sudo apt-get install python3-opencv

打开 Python IDLE(或 IPython),在 Python 终端中输入以下代码。

import cv2 as cv
print(cv.__version__)

如果结果打印无误,恭喜你!!!你已经成功安装了OpenCV-Python。

这相当简单。但存在一个问题:Apt软件源并不总是包含最新版本的OpenCV。例如,撰写本教程时,apt源提供的是2.4.8版,而最新的OpenCV版本已是3.x系列。就Python API而言,最新版本总能提供更好的支持与最新的错误修复。

因此要获取最新的源代码,推荐采用下一种方法——从源码编译。此外,如果你未来想为OpenCV贡献代码,也需要掌握此方法。


从源码构建OpenCV

初次接触时,从源码编译可能看起来有点复杂,但一旦成功完成,就会发现其实并不困难。

首先我们需要安装一些依赖项。其中有些是必需的,有些则是可选的。如果不需要,可以跳过可选依赖项的安装。


必需构建依赖项

我们需要使用 CMake 来配置安装,GCC 进行编译,以及 Python-develNumpy 来构建 Python 绑定等。

sudo apt-get install cmake
sudo apt-get install gcc g++

支持 Python2:

sudo apt-get install python-dev python-numpy to support python3:

sudo apt-get install python3-dev python3-numpy

接下来我们需要GTK支持来实现GUI功能,摄像头支持(v4l),以及媒体支持(ffmpeg, gstreamer)等。

sudo apt-get install libavcodec-dev libavformat-dev libswscale-dev
sudo apt-get install libgstreamer-plugins-base1.0-dev libgstreamer1.0-dev to support gtk2: 

sudo apt-get install libgtk2.0-dev to support gtk3: 

sudo apt-get install libgtk-3-dev

可选依赖项

上述依赖项足以在您的 Ubuntu 机器上安装 OpenCV。但根据需求,您可能需要一些额外的依赖项。以下是此类可选依赖项的列表。您可以选择忽略或安装它们,这取决于您的需求 😃

OpenCV 自带了支持 PNG、JPEG、JPEG2000、TIFF、WebP 等图像格式的文件,但这些可能版本较旧。如果您希望获取最新的库,可以安装这些格式对应的系统库开发文件。

sudo apt-get install libpng-dev
sudo apt-get install libjpeg-dev
sudo apt-get install libopenexr-dev
sudo apt-get install libtiff-dev
sudo apt-get install libwebp-dev

注意:如果您使用的是 Ubuntu 16.04,还可以安装 libjasper-dev 来为 JPEG2000 格式添加系统级支持。


下载 OpenCV

要获取最新源码,请访问 OpenCV 的 GitHub 仓库。(若想为 OpenCV 贡献代码,请选择此方式。为此,您需要先安装 Git


$ sudo apt-get install git
$ git clone https://github.com/opencv/opencv.git

它会在当前目录下创建一个名为"opencv"的文件夹。克隆过程所需时间取决于您的网络连接速度。

现在打开终端窗口,进入下载好的"opencv"文件夹。创建一个新的"build"文件夹并进入该目录。

$ mkdir build
$ cd build

配置与安装

现在我们已经准备好所有必需的依赖项,接下来开始安装 OpenCV。安装需要通过 CMake 进行配置。该配置过程会指定需要安装的模块、安装路径、需使用的附加库、是否编译文档和示例等内容。大部分工作都会通过预设的默认参数自动完成。

通常使用以下命令来配置 OpenCV 库的构建(在构建文件夹中执行):

$ cmake ../

OpenCV 默认配置假设构建类型为"Release"且安装路径是"/usr/local"。有关CMake选项的更多信息,请参考OpenCV的C++编译指南:

在CMake输出中您应该会看到以下行(这表明Python已被正确识别):

-- Python 2:
-- Interpreter: /usr/bin/python2.7 (ver 2.7.6)
-- Libraries: /usr/lib/x86_64-linux-gnu/libpython2.7.so (ver 2.7.6)
-- numpy: /usr/lib/python2.7/dist-packages/numpy/core/include (ver 1.8.2)
-- packages path: lib/python2.7/dist-packages
--
-- Python 3:
-- Interpreter: /usr/bin/python3.4 (ver 3.4.3)
-- Libraries: /usr/lib/x86_64-linux-gnu/libpython3.4m.so (ver 3.4.3)
-- numpy: /usr/lib/python3/dist-packages/numpy/core/include (ver 1.8.2)
-- packages path: lib/python3.4/dist-packages

现在你可以使用 make 命令构建文件,并通过 make install 命令进行安装。

$ make

sudo make install

安装已完成。所有文件已安装到 “/usr/local/” 目录下。打开终端并尝试导入 “cv2”。

import cv2 as cv
print(cv.__version__)

生成于 2025年4月30日 星期三 23:08:43,由 doxygen 1.12.0 为 OpenCV 生成


-------------- 功能概览


图像处理入门指南

https://docs.opencv.org/4.x/db/deb/tutorial_display_image.html


上一篇教程: 为Tegra构建支持CUDA的OpenCV

下一篇教程: 为OpenCV编写文档

原作者 Ana Huamán
兼容性 OpenCV >= 3.4.4

警告:本教程可能包含过时信息。


目标

在本教程中,你将学习如何:


源代码

可下载代码: C++

代码概览:



#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

#include <iostream>

using namespace cv;

int main()
{
    std::string image_path = samples::findFile("starry_night.jpg");
    Mat img = imread(image_path, IMREAD_COLOR);

    if(img.empty())
    {
        std::cout << "Could not read the image: " << image_path << std::endl;
        return 1;
    }

    imshow("Display window", img);
    int k = waitKey(0); // Wait for a keystroke in the window

    if(k == 's')
    {
        imwrite("starry_night.png", img);
    }

    return 0;
}

说明

在 OpenCV 3 的 C++ 版本中,我们拥有多个功能模块。每个模块分别负责图像处理的不同领域或方法。您可能已经在本教程用户指南的结构中注意到了这一点。在使用任何模块之前,首先需要包含声明各模块内容的头文件。

您几乎总会用到以下核心模块:

  • core 模块 - 这里定义了库的基础构建块
  • imgcodecs 模块 - 提供图像读写功能
  • highgui 模块 - 包含在窗口中显示图像的函数

我们还引入了 iostream 以方便控制台输入输出。

通过声明 using namespace cv;,后续代码中可以直接调用库函数而无需显式指定命名空间。

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

#include <iostream>

using namespace cv;

现在,我们来分析主代码。首先,我们从OpenCV示例中读取图像"starry_night.jpg"。为此,我们调用cv::imread函数,通过第一个参数指定的文件路径加载图像。第二个参数是可选的,用于指定我们想要的图像格式,可选值包括:

  • IMREAD_COLOR 以BGR 8位格式加载图像。这是此处使用的默认格式。

  • IMREAD_UNCHANGED 按原样加载图像(包括alpha通道,如果存在的话)

  • IMREAD_GRAYSCALE 将图像作为灰度图加载

读取图像后,数据将存储在cv::Mat对象中。

    std::string image_path = samples::findFile("starry_night.jpg");
    Mat img = imread(image_path, IMREAD_COLOR);

注意:OpenCV 支持以下图像格式:Windows 位图 (bmp)、便携式图像格式 (pbm、pgm、ppm) 以及 Sun 光栅图像 (sr、ras)。通过插件支持(若自行编译库需手动指定启用,但我们提供的预编译包默认已包含),还可以加载 JPEG (jpeg、jpg、jpe)、JPEG 2000 (jp2 - 在 CMake 中代号为 Jasper)、TIFF 文件 (tiff、tif) 和便携式网络图形 (png)。此外,还支持 OpenEXR 格式。


随后会执行检查,确认图像是否加载成功。

if(img.empty())
{
    std::cout << "Could not read the image: " << image_path << std::endl;
    return 1;
}

接着,通过调用 cv::imshow 函数显示图像。第一个参数是窗口标题,第二个参数是要显示的 cv::Mat 对象。

由于我们希望窗口持续显示直到用户按下按键(否则程序会过早结束),这里使用了 cv::waitKey 函数。该函数的唯一参数指定等待用户输入的时长(以毫秒为单位),零表示无限等待。返回值是用户按下的按键值。

imshow("Display window", img);
int k = waitKey(0); // Wait for a keystroke in the window

最终,如果按下的是"s"键,图像将被写入文件。为此会调用cv::imwrite函数,该函数以文件路径和cv::Mat对象作为参数。

if(k == 's')
{
    imwrite("starry_night.png", img);
}

生成于 2025 年 4 月 30 日 星期三 23:08:42,由 doxygen 1.12.0 为 OpenCV 生成


视频入门指南

https://docs.opencv.org/4.x/dd/d43/tutorial_py_video_display.html


目标


从摄像头捕获视频

我们经常需要从摄像头捕获实时视频流。OpenCV为此提供了一个非常简单的接口。让我们从摄像头(我使用的是笔记本电脑内置的摄像头)捕获视频,将其转换为灰度视频并显示。这是一个简单的入门任务。

要捕获视频,你需要创建一个VideoCapture对象。其参数可以是设备索引号或视频文件名。设备索引号用于指定使用哪个摄像头。通常只连接了一个摄像头(如我的情况),所以我直接传入0(或-1)。你可以传入1来选择第二个摄像头,依此类推。之后,你可以逐帧捕获视频。但最后别忘了释放捕获器。

import numpy as np
import cv2 as cv

cap = cv.VideoCapture(0)
if not cap.isOpened():
    print("Cannot open camera")
    exit()
while True:
    # Capture frame-by-frame
    ret, frame = cap.read()

    # if frame is read correctly ret is True
    if not ret:
        print("Can't receive frame (stream end?). Exiting ...")
        break
    # Our operations on the frame come here
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    # Display the resulting frame
    cv.imshow('frame', gray)
    if cv.waitKey(1) == ord('q'):
        break

# When everything done, release the capture
cap.release()
cv.destroyAllWindows()

cap.read() 返回一个布尔值(True/False)。如果帧读取成功,则为 True。因此,可以通过检查这个返回值来判断视频是否结束。

有时,cap 可能没有初始化捕获。在这种情况下,这段代码会显示错误。你可以通过 cap.isOpened() 方法检查它是否已初始化。如果返回 True,则表示正常;否则,需要使用 cap.open() 手动打开它。

你还可以通过 cap.get(propId) 方法访问视频的某些特性,其中 propId 是一个从 0 到 18 的数字,每个数字代表视频的一个属性(如果该属性适用于该视频)。完整细节可参考:cv::VideoCapture::get()。其中一些值可以通过 cap.set(propId, value) 修改,value 是你想要设置的新值。

例如,我可以通过 cap.get([cv.CAP_PROP_FRAME_WIDTH) 和 cap.get([cv.CAP_PROP_FRAME_HEIGHT])) 检查帧的宽度和高度。默认情况下,它会返回 640x480。但如果我想将其修改为 320x240,只需使用 ret = cap.set(cv.CAP_PROP_FRAME_WIDTH ,320) 和 ret = cap.set(cv.CAP_PROP_FRAME_HEIGHT ,240)。

注意:如果遇到错误,请确保你的摄像头在其他应用程序(如 Linux 下的 Cheese)中能正常工作。


从文件播放视频

从文件播放视频与从摄像头捕获视频的操作相同,只需将摄像头索引改为视频文件名即可。在显示帧时,需要为cv.waitKey()设置合适的时间参数。如果该值过小,视频会播放得很快;如果过大,视频则会变慢(这正是实现慢动作播放的原理)。通常情况下,25毫秒的延迟值是合适的。

import numpy as np
import cv2 as cv
 
cap = cv.VideoCapture('vtest.avi')
 
while cap.isOpened():
 ret, frame = cap.read()
 
 # if frame is read correctly ret is True
 if not ret:
 print("Can't receive frame (stream end?). Exiting ...")
 break
 gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
 
 cv.imshow('frame', gray)
 if cv.waitKey((1) == ord('q'):
 break

cap.release()
cv.destroyAllWindows()


注意:确保安装了正确版本的ffmpeg或gstreamer。处理视频捕获时经常会遇到问题,这通常是由于ffmpeg/gstreamer安装错误导致的。


保存视频

我们捕获视频并逐帧处理后,需要将其保存下来。对于图像来说非常简单:只需使用 cv.imwrite()。但视频保存需要更多步骤。

此时我们需要创建一个 VideoWriter 对象。需要指定输出文件名(例如:output.avi),然后设置 FourCC 编码格式(下段详述),接着传入帧率(fps)和帧尺寸参数。最后一个是 isColor 标志位。若设为 True,编码器将处理彩色帧,否则处理灰度帧。

FourCC 是用于指定视频编解码器的4字节编码。可用编码列表可在 fourcc.org 查询,不同平台支持情况不同。以下编解码器经测试可用:

  • Fedora系统:DIVX, XVID, MJPG, X264, WMV1, WMV2(推荐XVID,MJPG生成文件较大,X264生成文件较小)
  • Windows系统:DIVX(待测试补充更多)
  • OSX系统:MJPG (.mp4), DIVX (.avi), X264 (.mkv)

FourCC编码传入方式示例:MJPG格式可表示为 cv.VideoWriter_fourcc('M','J','P','G')cv.VideoWriter_fourcc(*'MJPG')

以下代码实现从摄像头捕获画面,垂直翻转每一帧并保存视频的功能。

import numpy as np
import cv2 as cv

cap = cv.VideoCapture(0)

# Define the codec and create VideoWriter object
fourcc = cv.VideoWriter_fourcc(*'XVID')
out = cv.VideoWriter('output.avi', fourcc, 20.0, (640,  480))

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        print("Can't receive frame (stream end?). Exiting ...")
        break
    frame = cv.flip(frame, 0)

    # write the flipped frame
    out.write(frame)

    cv.imshow('frame', frame)
    if cv.waitKey(1) == ord('q'):
        break

# Release everything if job is finished
cap.release()
out.release()
cv.destroyAllWindows()


OpenCV 中的绘图函数

https://docs.opencv.org/4.x/dc/da5/tutorial_py_drawing_functions.html


目标


代码

在上述所有函数中,您会看到一些共同的参数如下:

  • img:需要绘制形状的目标图像
  • color:形状的颜色。对于BGR格式,以元组形式传入,例如(255,0,0)表示蓝色;对于灰度图,只需传入标量值
  • thickness:线条或圆形的粗细度。如果是闭合图形(如圆形)传入**-1**,则会填充整个形状。默认粗细度=1
  • lineType:线条类型,可以是8连通线、抗锯齿线等。默认使用8连通线cv.LINE_AA可生成抗锯齿线,特别适合绘制曲线。

绘制直线

要绘制一条直线,需要提供直线的起点和终点坐标。我们将创建一个黑色背景的图像,并在其上从左上角到右下角绘制一条蓝色直线。

import numpy as np
import cv2 as cv

# Create a black image
img = np.zeros((512,512,3), np.uint8)

# Draw a diagonal blue line with thickness of 5 px
cv.line(img,(0,0),(511,511),(255,0,0),5)

绘制矩形

要绘制一个矩形,需要确定矩形的左上角和右下角坐标。这次我们将在图像的右上角绘制一个绿色矩形。

cv.rectangle(img,(384,0),(510,128),(0,255,0),3) 

绘制圆形

要绘制一个圆形,需要知道其圆心坐标和半径。我们将在之前绘制的矩形内部绘制这个圆形。

cv.circle(img,(447,63), 63, (0,0,255), -1)

绘制椭圆

要绘制椭圆,我们需要传递几个参数。第一个参数是中心点坐标(x,y)。第二个参数是轴长(长轴长度,短轴长度)。angle参数表示椭圆逆时针旋转的角度。startAngle和endAngle参数表示椭圆弧的起始和结束角度,按顺时针方向从长轴开始测量。例如,设置0和360度将绘制完整的椭圆。更多细节请查阅**cv.ellipse()**文档。下面的示例在图像中心绘制了一个半椭圆。

cv.ellipse(img,(256,256),(100,50),0,0,180,255,-1)

绘制多边形

要绘制多边形,首先需要顶点的坐标。将这些点转换为形状为 ROWSx1x2 的数组,其中 ROWS 是顶点数量,且数组类型应为 int32。这里我们绘制了一个包含四个顶点的小多边形,颜色为黄色。

pts = np.array([[10,5],[20,30],[70,20],[50,10]], np.int32)
pts = pts.reshape((-1,1,2))
cv.polylines(img,[pts],True,(0,255,255))

注意:如果第三个参数为False,你将得到连接所有点的折线,而非闭合形状。

cv.polylines() 可用于绘制多条线段。只需创建一个包含所有待绘制线段的列表,并将其传递给该函数。所有线段将被独立绘制。相比为每条线段调用cv.line()`,这是绘制一组线段更高效且快速的方式。


在图像上添加文字

要在图像上写入文字,需要指定以下内容:

  • 想要写入的文本内容
  • 文字放置的坐标位置(即文本起始点的左下角坐标)
  • 字体类型(支持的字体请查阅 cv.putText() 文档)
  • 字体缩放比例(控制字体大小)
  • 常规属性如颜色、粗细、线条类型等。为了更好看的效果,推荐使用 lineType = cv.LINE_AA(参见 cv.LINE_AA

我们将在图像上用白色文字写上 OpenCV

font = cv.FONT_HERSHEY_SIMPLEX
cv.putText(img,'OpenCV',(10,500), font, 4,(255,255,255),2,cv.LINE_AA)

结果

现在是我们查看绘图最终成果的时候了。正如你在之前的文章中所学到的,显示图像即可查看效果。

img


附加资源

  1. 椭圆函数中使用的角度并非我们常见的圆周角度。更多详情请参阅此讨论

练习

  1. 尝试使用 OpenCV 中的绘图函数创建 OpenCV 的徽标。

鼠标作为画笔

https://docs.opencv.org/4.x/db/d5b/tutorial_py_mouse_handling.html


目标


简单示例

这里我们创建一个简单的应用程序,在图像上双击的位置绘制一个圆形。

首先创建一个鼠标回调函数,当鼠标事件发生时执行。鼠标事件可以是任何与鼠标相关的操作,例如左键按下、左键释放、左键双击等。该函数会为每个鼠标事件提供坐标(x,y)。利用这个事件和位置信息,我们可以执行任何想要的操作。要查看所有可用的事件列表,可以在Python终端中运行以下代码:

import cv2 as cv
events = [i for i in dir(cv) if 'EVENT' in i]
print( events )

创建鼠标回调函数有一个固定的格式,这个格式在任何地方都是相同的。唯一的不同在于函数的具体功能。我们的鼠标回调函数只做一件事:在双击的位置绘制一个圆。请看下面的代码,代码中的注释已经非常清晰明了:

import numpy as np
import cv2 as cv

# mouse callback function
def draw_circle(event,x,y,flags,param):
    if event == cv.EVENT_LBUTTONDBLCLK:
        cv.circle(img,(x,y),100,(255,0,0),-1)

# Create a black image, a window and bind the function to window
img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)

while(1):
    cv.imshow('image',img)
    if cv.waitKey(20) & 0xFF == 27:
        break
cv.destroyAllWindows()

更高级的演示

现在我们来开发一个更完善的应用程序。在这个程序中,我们可以像在画图软件中那样,通过拖动鼠标来绘制矩形或圆形(具体取决于选择的模式)。因此,我们的鼠标回调函数包含两部分:一部分用于绘制矩形,另一部分用于绘制圆形。这个特定示例对于创建和理解一些交互式应用程序(如对象跟踪、图像分割等)将非常有帮助。

import numpy as np
import cv2 as cv

drawing = False # true if mouse is pressed
mode = True # if True, draw rectangle. Press 'm' to toggle to curve
ix,iy = -1,-1

# mouse callback function
def draw_circle(event,x,y,flags,param):
    global ix,iy,drawing,mode

    if event == cv.EVENT_LBUTTONDOWN:
        drawing = True
        ix,iy = x,y

    elif event == cv.EVENT_MOUSEMOVE:
        if drawing == True:
            if mode == True:
                cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
            else:
                cv.circle(img,(x,y),5,(0,0,255),-1)

    elif event == cv.EVENT_LBUTTONUP:
        drawing = False
        if mode == True:
            cv.rectangle(img,(ix,iy),(x,y),(0,255,0),-1)
        else:
            cv.circle(img,(x,y),5,(0,0,255),-1)

接下来我们需要将这个鼠标回调函数绑定到OpenCV窗口上。在主循环中,应该为按键’m’设置一个键盘绑定,用于在矩形和圆形之间切换。

img = np.zeros((512,512,3), np.uint8)
cv.namedWindow('image')
cv.setMouseCallback('image',draw_circle)

while(1):
    cv.imshow('image',img)
    k = cv.waitKey(1) & 0xFF
    if k == ord('m'):
        mode = not mode
    elif k == 27:
        break

cv.destroyAllWindows()

练习

1、在上一个示例中,我们绘制了一个实心矩形。请修改代码以绘制一个空心矩形。


生成于 2025年4月30日 星期三 23:08:42,使用 doxygen 1.12.0 为 OpenCV 生成


作为调色板的轨迹栏

https://docs.opencv.org/4.x/d9/dc8/tutorial_py_trackbar.html


目标


代码演示

我们将创建一个简单的应用程序,用于显示你指定的颜色。界面包含一个显示颜色的窗口和三个分别对应B、G、R颜色的滑动条。当滑动条移动时,窗口颜色会相应变化。默认初始颜色为黑色。

对于cv.createTrackbar()`函数:第一个参数是滑动条名称,第二个是附加的目标窗口名称,第三个是默认值,第四个是最大值,第五个是滑动条值变化时执行的回调函数。回调函数始终带有一个表示滑动条位置的默认参数。本例中该函数无需执行操作,因此直接传递即可。

滑动条的另一个重要用途是作为按钮或开关使用。OpenCV默认不提供按钮功能,因此可以通过滑动条实现类似功能。在我们的应用程序中创建了一个开关:只有当开关处于ON状态时程序才会工作,否则屏幕始终保持黑色。

import numpy as np
import cv2 as cv

def nothing(x):
    pass

# Create a black image, a window
img = np.zeros((300,512,3), np.uint8)
cv.namedWindow('image')

# create trackbars for color change
cv.createTrackbar('R','image',0,255,nothing)

cv.createTrackbar('G','image',0,255,nothing)
cv.createTrackbar('B','image',0,255,nothing)

# create switch for ON/OFF functionality
switch = '0 : OFF \n1 : ON'
cv.createTrackbar(switch, 'image',0,1,nothing)

while(1):
    cv.imshow('image',img)
    k = cv.waitKey(1) & 0xFF
    if k == 27:
        break

    # get current positions of four trackbars
    r = cv.getTrackbarPos('R','image')
    g = cv.getTrackbarPos('G','image')
    b = cv.getTrackbarPos('B','image')
    s = cv.getTrackbarPos(switch,'image')

    if s == 0:
        img[:] = 0
    else:
        img[:] = [b,g,r]

cv.destroyAllWindows()

应用程序的界面截图如下:


练习

1、创建一个带有可调颜色和画笔半径的绘图应用,使用轨迹条进行控制。关于绘图部分,请参考之前关于鼠标处理的教程。


生成于 2025年4月30日 星期三 23:08:42,针对 OpenCV,由 doxygen 1.12.0 生成


------------ 核心操作


图像基础操作

https://docs.opencv.org/4.x/d3/df2/tutorial_py_basic_ops.html


目标

学习如何:

  • 访问和修改像素值
  • 获取图像属性
  • 设置感兴趣区域(ROI)
  • 拆分与合并图像

本节几乎所有操作主要基于Numpy而非OpenCV。为了更好地优化OpenCV代码,需要熟练掌握Numpy。

(示例将在Python终端中展示,因为大多数操作只需单行代码)


访问和修改像素值

首先加载一张彩色图像:

>>> import numpy as np
>>> import cv2 as cv

>>> img = cv.imread('messi5.jpg')
>>> assert img is not None, "file could not be read, check with os.path.exists()"

可以通过行列坐标访问像素值。对于BGR图像,它会返回一个包含蓝、绿、红值的数组。对于灰度图像,则只返回对应的亮度值。

>>> px = img[100,100]
>>> print( px )
[157 166 200]

# accessing only blue pixel
>>> blue = img[100,100,0]
>>> print( blue )
157

您可以以同样的方式修改像素值。

>>> img[100,100] = [255,255,255]
>>> print( img[100,100] )
[255 255 255]

警告

Numpy 是一个针对快速数组计算优化的库。因此,简单地逐个访问和修改每个像素值会非常慢,不建议这样做。


访问图像属性

图像属性包括行数、列数和通道数;图像数据类型;像素数量等。

通过 img.shape 可以访问图像的形状。它会返回一个包含行数、列数和通道数(如果是彩色图像)的元组:

>>> print( img.shape )
(342, 548, 3)

注意:如果图像是灰度图,返回的元组仅包含行数和列数,因此这是检查加载图像是灰度图还是彩色图的有效方法。

通过 img.size 可以访问像素总数:

>>> print( img.size )
562248

通过 img.dtype 获取图像数据类型:

>>> print( img.dtype )
uint8

注意:调试时 img.dtype 非常重要,因为 OpenCV-Python 代码中的大量错误都是由无效数据类型引起的。


图像 ROI(感兴趣区域)

有时,你需要对图像的特定区域进行操作。例如,在图像中进行眼睛检测时,通常会先对整个图像进行人脸检测。当检测到人脸后,我们仅选择人脸区域并在该区域内搜索眼睛,而不是搜索整个图像。这种方法既提高了准确性(因为眼睛总是长在人脸上 😄),又提升了性能(因为我们只需在小范围内搜索)。

同样地,我们可以通过Numpy索引来获取ROI。下面这个例子展示了我如何选择图像中的球并将其复制到图像的另一个区域:

>>> ball = img[280:340, 330:390]
>>> img[273:333, 100:160] = ball

检查以下结果:


图像通道的拆分与合并

有时需要对图像的B、G、R通道分别进行处理。这种情况下,需要将BGR图像拆分为单通道图像。而在其他情况下,可能需要将这些独立通道重新合并为BGR图像。通过以下方式即可实现:

>>> b,g,r = cv.split(img)
>>> img = cv.merge((b,g,r))

Or


>>> b = img[:,:,0]

警告

cv.split()`是一个耗时操作。因此仅在必要时使用它。否则请改用Numpy索引。


为图像添加边框(填充)

如果想为图像创建边框(类似相框效果),可以使用 cv.copyMakeBorder()。该函数在卷积运算、零填充等场景中也有广泛应用,其参数说明如下:

  • src - 输入图像
  • top, bottom, left, right - 各方向上的边框宽度(像素单位)
  • borderType - 边框类型标志,可选值包括:
  • value - 当边框类型为 cv.BORDER_CONSTANT 时指定的边框颜色

以下示例代码演示了所有边框类型,便于理解:

import cv2 as cv
import numpy as np
from matplotlib import pyplot as plt

BLUE = [255,0,0]

img1 = cv.imread('opencv-logo.png')
assert img1 is not None, "file could not be read, check with os.path.exists()"

replicate = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REPLICATE)
reflect = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT)
reflect101 = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_REFLECT_101)
wrap = cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_WRAP)
constant= cv.copyMakeBorder(img1,10,10,10,10,cv.BORDER_CONSTANT,value=BLUE)

plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL')
plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE')
plt.subplot(233),plt.imshow(reflect,'gray'),plt.title('REFLECT')
plt.subplot(234),plt.imshow(reflect101,'gray'),plt.title('REFLECT_101')
plt.subplot(235),plt.imshow(wrap,'gray'),plt.title('WRAP')
plt.subplot(236),plt.imshow(constant,'gray'),plt.title('CONSTANT')

plt.show()

查看下方结果(图像通过 matplotlib 显示,因此红色和蓝色通道会互换):

生成于 2025年4月30日 星期三 23:08:42,由 doxygen 1.12.0 为 OpenCV 创建


图像算术运算

https://docs.opencv.org/4.x/d0/d86/tutorial_py_image_arithmetics.html


目标

  • 学习对图像进行多种算术运算,如加法、减法、位运算等。
  • 掌握以下函数:cv.add()、**cv.addWeighted()**等。

图像相加

你可以使用OpenCV函数cv.add()`,或者直接通过numpy操作res = img1 + img2来实现两幅图像的相加。两幅图像必须具有相同的深度和类型,或者第二幅图像可以只是一个标量值。

注意:OpenCV的加法与Numpy加法存在区别。OpenCV加法是饱和运算,而Numpy加法是模运算。


例如,参考以下示例:

>>> x = np.uint8([250])
>>> y = np.uint8([10])

>>> print( cv.add(x,y) ) # 250+10 = 260 => 255
[[255]]

>>> print( x+y )          # 250+10 = 260 % 256 = 4
[4]

当你添加两张图片时,这一点会更加明显。坚持使用 OpenCV 函数,因为它们能提供更好的效果。


图像混合

这也是图像相加的一种形式,但通过为不同图像分配不同权重来实现混合或透明效果。图像按照以下公式进行叠加:

g ( x ) = ( 1 − α ) f 0 ( x ) + α f 1 ( x )   g(x) = (1 - \alpha)f_{0}(x) + \alpha f_{1}(x)\ g(x)=(1α)f0(x)+αf1(x) 

通过将 α \alpha α 值从0调节到1,可以实现从一个图像到另一个图像的平滑过渡效果。

这里我选取了两张图像进行混合。第一张图像的权重设为0.7,第二张设为0.3。cv.addWeighted()`函数对图像应用以下公式:

[dst = \alpha \cdot img1 + \beta \cdot img2 + \gamma]

此处的 γ \gamma γ 值设为零。

img1 = cv.imread('ml.png')
img2 = cv.imread('opencv-logo.png')
assert img1 is not None, "file could not be read, check with os.path.exists()"
assert img2 is not None, "file could not be read, check with os.path.exists()"

dst = cv.addWeighted(img1,0.7,img2,0.3,0)

cv.imshow('dst',dst)
cv.waitKey(0)
cv.destroyAllWindows()

检查以下结果:


位运算

这包括按位与、或、非和异或操作。在提取图像的任意部分(我们将在后续章节看到)、定义和处理非矩形感兴趣区域(ROI)等场景中,这些操作非常有用。下面我们将通过示例演示如何修改图像的特定区域。

假设我想将OpenCV徽标叠加到图像上。如果直接相加两张图像,会导致颜色变化;如果混合它们,会产生透明效果。但我需要不透明的效果。如果是矩形区域,可以像上一章那样使用ROI处理。但OpenCV徽标并非矩形,此时可以通过如下所示的位运算来实现:

# Load two images
img1 = cv.imread('messi5.jpg')
img2 = cv.imread('opencv-logo-white.png')
assert img1 is not None, "file could not be read, check with os.path.exists()"
assert img2 is not None, "file could not be read, check with os.path.exists()"

# I want to put logo on top-left corner, So I create a ROI
rows,cols,channels = img2.shape
roi = img1[0:rows, 0:cols]

# Now create a mask of logo and create its inverse mask also
img2gray = cv.cvtColor(img2,cv.COLOR_BGR2GRAY)
ret, mask = cv.threshold(img2gray, 10, 255, cv.THRESH_BINARY)
mask_inv = cv.bitwise_not(mask)

# Now black-out the area of logo in ROI
img1_bg = cv.bitwise_and(roi,roi,mask = mask_inv)

# Take only region of logo from logo image.
img2_fg = cv.bitwise_and(img2,img2,mask = mask)

# Put logo in ROI and modify the main image
dst = cv.add(img1_bg,img2_fg)
img1[0:rows, 0:cols ] = dst

cv.imshow('res',img1)
cv.waitKey(0)
cv.destroyAllWindows()

查看下方结果。左图显示我们创建的掩膜,右图展示最终效果。为更好理解,可在上述代码中显示所有中间图像,特别是 img1_bgimg2_fg



练习

1、使用cv.addWeighted函数创建一个文件夹中图像的幻灯片展示,实现图像间的平滑过渡


生成于 2025年4月30日 星期三 23:08:42 为 OpenCV 由 doxygen 1.12.0 生成



性能测量与优化技术

https://docs.opencv.org/4.x/dc/d71/tutorial_py_optimization.html


目标

在图像处理中,由于每秒需要执行大量操作,因此代码不仅需要提供正确的解决方案,还必须以最快的方式实现。在本章中,你将学习:

除了OpenCV,Python还提供了time模块,可用于测量代码执行时间。另一个模块profile则能生成详细的代码分析报告,例如代码中每个函数的执行时间、调用次数等。但如果你使用IPython,这些功能已以更友好的方式集成。我们将介绍其中一些重要功能,更多细节请参考附加资源部分的链接。


使用OpenCV测量性能

cv.getTickCount 函数会返回从参考事件(如机器开机时刻)到调用该函数时的时钟周期数。因此,如果在函数执行前后分别调用它,就能得到执行该函数所消耗的时钟周期数。

cv.getTickFrequency 函数返回时钟周期的频率,即每秒的时钟周期数。要计算以秒为单位的执行时间,可以按以下方式操作:

e1 = cv.getTickCount()
# your code execution
e2 = cv.getTickCount()
time = (e2 - e1)/ cv.getTickFrequency()

我们将通过以下示例进行演示。该示例应用了核大小从5到49的奇数中值滤波(不必担心结果会是什么样——这不是我们的目标):

img1 = cv.imread('messi5.jpg')
assert img1 is not None, "file could not be read, check with os.path.exists()"

e1 = cv.getTickCount()
for i in range(5,49,2):
    img1 = cv.medianBlur(img1,i)
e2 = cv.getTickCount()
t = (e2 - e1)/cv.getTickFrequency()
print( t )

## Result I got is 0.521107655 seconds

注意:你可以使用 time 模块实现相同的功能。无需调用 cv.getTickCount,改用 time.time() 函数获取时间戳,然后计算两个时间戳的差值即可。


OpenCV 中的默认优化

许多 OpenCV 函数都使用 SSE2、AVX 等技术进行了优化,同时也保留了未优化的代码。因此,如果我们的系统支持这些特性(几乎所有现代处理器都支持),就应该充分利用它们。编译时默认会启用优化功能。因此,OpenCV 会优先运行优化后的代码,否则将执行未优化的版本。

您可以通过 cv.useOptimized() 检查当前是否启用了优化,并通过 cv.setUseOptimized() 来开启/关闭该功能。下面来看一个简单示例。

# check if optimization is enabled
In [5]: cv.useOptimized()
Out[5]: True

In [6]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 34.9 ms per loop

# Disable it
In [7]: cv.setUseOptimized(False)

In [8]: cv.useOptimized()
Out[8]: False

In [9]: %timeit res = cv.medianBlur(img,49)
10 loops, best of 3: 64.1 ms per loop

如你所见,优化后的中值滤波速度是未优化版本的两倍。查看其源代码可以发现,中值滤波采用了SIMD优化。因此你可以在代码开头启用该优化(默认已启用)。


在IPython中测量性能

有时你可能需要比较两个相似操作的性能。IPython提供了一个魔法命令timeit来完成这个任务。它会多次运行代码以获得更准确的结果。同样地,它适合测量单行代码的性能。

例如,你知道以下哪种加法操作更好吗:x = 5; y = x**2x = 5; y = x*xx = np.uint8([5]); y = x*x,还是y = np.square(x)?我们将在IPython shell中使用timeit来找出答案。

In [10]: x = 5

In [11]: %timeit y=x**2
10000000 loops, best of 3: 73 ns per loop

In [12]: %timeit y=x*x
10000000 loops, best of 3: 58.3 ns per loop

In [15]: z = np.uint8([5])

In [17]: %timeit y=z*z
1000000 loops, best of 3: 1.25 us per loop

In [19]: %timeit y=np.square(z)
1000000 loops, best of 3: 1.16 us per loop

可以看到,x = 5 ; y = x*x 是最快的,比 Numpy 快约 20 倍。如果考虑数组创建过程,速度差距可能达到 100 倍。很惊人吧?(Numpy 开发团队正在解决这个问题)

注意:Python 标量运算比 Numpy 标量运算更快。因此对于涉及一到两个元素的操作,使用 Python 标量比 Numpy 数组更高效。当数组规模稍大时,Numpy 才展现出优势。

我们再来看一个例子。这次我们将比较 cv.countNonZero()np.count_nonzero() 对同一张图像的处理性能。

In [35]: %timeit z = cv.countNonZero(img)
100000 loops, best of 3: 15.8 us per loop

In [36]: %timeit z = np.count_nonzero(img)
1000 loops, best of 3: 370 us per loop

可以看到,OpenCV 函数比 Numpy 函数快了近 25 倍。

注意:通常情况下,OpenCV 函数比 Numpy 函数更快。因此对于相同的操作,建议优先使用 OpenCV 函数。但也可能存在例外情况,特别是当 Numpy 使用视图(view)而非副本(copy)进行操作时。


更多 IPython 魔法命令

IPython 还提供了其他多种魔法命令,可用于性能测量、性能剖析、行级性能分析、内存测量等功能。这些命令都有完善的文档说明。因此这里仅提供相关文档链接。建议感兴趣的读者亲自尝试这些命令。


性能优化技巧

有多种技术和编码方法可以充分发挥Python和Numpy的最大性能。这里仅列出相关要点,并提供重要参考资料链接。最关键的注意事项是:首先以简单方式实现算法,待其运行正常后,再进行性能分析,找出瓶颈并优化。

  1. 尽可能避免在Python中使用循环,特别是双重/三重循环等,它们本质上运行缓慢
  2. 尽量将算法/代码向量化,因为Numpy和OpenCV针对向量运算进行了优化
  3. 充分利用缓存一致性
  4. 除非必要,否则不要复制数组。尽量使用视图(view)替代。数组复制是代价高昂的操作

如果完成上述所有优化后代码仍然缓慢,或者必须使用大型循环,可以考虑使用Cython等额外库来加速。


额外资源

1、Python 优化技巧
2、Scipy 讲义笔记 - 高级 Numpy 用法
3、IPython 中的计时与性能分析


生成于 2025年4月30日 星期三 23:08:42 为 OpenCV 由 doxygen 1.12.0 生成



网站公告

今日签到

点亮在社区的每一天
去签到