OpenCV 4.1.2 文档整理,把整个文档都阅读整理了一遍。
wechatdl OpenCV-Python Tutorials 官方文档
启用 TBB 和 Eigen 支持:
cmake -D WITH_TBB=ON -D WITH_EIGEN=ON ..
OpenCV 是由英特尔公司发起并参与开发,以 BSD 许可证授权发行,可以在商业和研究领域中免费使用。
加入这个头文件即可(还有相应的 lib 文件)
#include "opencv2/nonfree/nonfree.hpp"
https://github.com/oneapi-src/oneTBB oneAPI Threading Building Blocks (oneTBB) lets you easily write parallel C++ programs that take full advantage of multicore performance, that are portable, composable and have future-proof scalability.
用 CMake 生成的时候实际上是在文件 cvconfig.h 里写上了:
#define HAVE_TBB
cmake .. -G "Visual Studio 16 2019" -A Win32 -D WITH_TBB=ON -D WITH_OPENMP=ON -D OPENCV_FORCE_3RDPARTY_BUILD=ON
ONETBB(1000); TBB(990); OPENMP(980)
opencv_core_parallel_onetbb453d.dll => FAILED
opencv_core_parallel_tbb453d.dll => FAILED
opencv_core_parallel_openmp453d.dll => FAILED
D:\2021\opencv_liebao\4.5.3\
parallel\registry_parallel.impl.hpp (96)
cv::parallel::ParallelBackendRegistry::ParallelBackendRegistry core(parallel):
Enabled backends(3, sorted by priority): ONETBB(1000); TBB(990); OPENMP(980)
utils\plugin_loader.impl.hpp (67) load opencv_core_parallel_onetbb453d.dll => FAILED
utils\plugin_loader.impl.hpp (67) load opencv_core_parallel_tbb453d.dll => FAILED
utils\plugin_loader.impl.hpp (67) load opencv_core_parallel_openmp453d.dll => FAILED
这几行错误提示有毒,一直以为这个世界上存在 opencv_core_parallel_tbb453d.dll
这种东西,直到看了源码,是静态编译进去的。
#if defined HAVE_TBB
# define CV_PARALLEL_FRAMEWORK "tbb"
#elif defined HAVE_HPX
# define CV_PARALLEL_FRAMEWORK "hpx"
#elif defined HAVE_OPENMP
# define CV_PARALLEL_FRAMEWORK "openmp"
#elif defined HAVE_GCD
# define CV_PARALLEL_FRAMEWORK "gcd"
#elif defined WINRT
# define CV_PARALLEL_FRAMEWORK "winrt-concurrency"
#elif defined HAVE_CONCURRENCY
# define CV_PARALLEL_FRAMEWORK "ms-concurrency"
#elif defined HAVE_PTHREADS_PF
# define CV_PARALLEL_FRAMEWORK "pthreads"
#endif
static
std::vector<ParallelBackendInfo>& getBuiltinParallelBackendsInfo()
{
static std::vector<ParallelBackendInfo> g_backends
{
#ifdef HAVE_TBB
DECLARE_STATIC_BACKEND("TBB", createParallelBackendTBB)
#elif defined(PARALLEL_ENABLE_PLUGINS)
DECLARE_DYNAMIC_BACKEND("ONETBB") // dedicated oneTBB plugin (interface >= 12000, binary incompatibe with TBB 2017-2020)
DECLARE_DYNAMIC_BACKEND("TBB") // generic TBB plugins
#endif
#ifdef HAVE_OPENMP
DECLARE_STATIC_BACKEND("OPENMP", createParallelBackendOpenMP)
#elif defined(PARALLEL_ENABLE_PLUGINS)
DECLARE_DYNAMIC_BACKEND("OPENMP") // TODO Intel OpenMP?
#endif
};
return g_backends;
};
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 函数都是使用 SSE2、 AVX 等进行优化的。 你可以使用 cvUseoptimized 检查是否启用 / 禁用和 cvSetuseoptimized 以启用 / 禁用它。
首先尝试以一种简单的方式实现算法。一旦它运行起来,分析它,找到瓶颈并优化它们。
HSV 的色相范围为 [0, 179],饱和度范围为 [0, 255],值范围为 [0, 255]。
对象追踪:
import cv2 as cv
import numpy as np
cap = cv.VideoCapture(0)
while (1):
# 读取帧
_, frame = cap.read()
# 转换颜色空间 BGR 到 HSV
hsv = cv.cvtColor(frame, cv.COLOR_BGR2HSV)
# 定义 HSV 中蓝色的范围
lower_blue = np.array([110,50,50])
upper_blue = np.array([130,255,255])
# 设置 HSV 的阈值使得只取蓝色
mask = cv.inRange(hsv, lower_blue, upper_blue)
# 将掩膜和图像逐像素相加
res = cv.bitwise_and(frame, frame, mask=mask)
cv.imshow('frame',frame)
cv.imshow('mask',mask)
cv.imshow('res',res)
k = cv.waitKey(5) & 0xFF
if k == 27:
break
cv.destroyAllWindows()
平移、旋转、仿射变换等。
旋转或移动图像
def rotate(img, angle, center=None):
w, h = img.shape[:2]
if center == None:
center = (w//2, h//2)
# center is the center of image from which we have to rotate
# if it is None then it is cconsider as the center of the original image.
rotMat = cv.getRotationMatrix2D(center, angle, 1.0)
dim = (w, h)
return cv.warpAffine(img, rotMat, dim)
OpenCV 主要提供四种类型的模糊技术。
开运算(MORPH_OPEN):先腐蚀再膨胀 开放只是侵蚀然后扩张的另一个名称。如上文所述,它对于消除噪音很有用。 opening = cv.morphologyEx(img, cv.MORPH_OPEN, kernel)
闭运算(MORPH_CLOSE):先膨胀再腐蚀 闭运算与开运算相反,先扩张然后再侵蚀。在关闭前景对象内部的小孔或对象上的小黑点时很有用。 closing = cv.morphologyEx(img, cv.MORPH_CLOSE, kernel)
形态梯度(MORPH_GRADIENT):膨胀图与腐蚀图之差(保留物体边缘轮廓) 形态学梯度 这是图像扩张和侵蚀之间的区别。结果将看起来像对象的轮廓。
顶帽(MORPH_TOPHAT):原图像与开运算之差
顶帽运算(image)= 原图(image)- 开运算(image)
所以顶帽运算得到的实际上是噪声图像。
黑帽(MORPH_BLACKHAT):闭图像与原图像之差
黑帽运算(image)= 闭运算(image)-原图(image)
实际得到的是前景图中的黑点和小洞。
查找图像梯度、边缘等。 我们将看到以下函数:cv.Sobel(),cv.Scharr(),cv.Laplacian() 等。
梯度(Gradient)定位边缘
# Apply gradient filtering
sobel_x = cv2.Sobel(img, cv2.CV_64F, dx = 1, dy = 0, ksize = 5)
sobel_y = cv2.Sobel(img, cv2.CV_64F, dx = 0, dy = 1, ksize = 5)
blended = cv2.addWeighted(src1=sobel_x, alpha=0.5, src2=sobel_y,
beta=0.5, gamma=0)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
拉普拉斯运算使用的是 x 和 y 的二阶导数,数学表达式如下。
\[L(x, y)=\frac{\partial^{2} I}{\partial x^{2}}+\frac{\partial^{2} I}{\partial y^{2}}\]OpenCV 中常用的四种模糊效果
cv.bilateralFilter()
在保持边缘锐利的同时去除噪音非常有效。但与其他过滤器相比,操作速度较慢。输入图像为原始图像,thereshold-1 即像素值低于 150 被视为非边缘,threshold-2 即像素值高于 175 被视为有效边缘。 如果该值在 150 和 175 之间,那么如果边缘像素与有效边缘相连,则仅将其视为有效边缘。
cv.Canny(originalImg, 150, 175)
我们将使用图像金字塔创建一个新的水果“Orapple”。 我们将看到以下功能:cv.pyrUp(),cv.pyrDown()。
有两种图像金字塔。1)高斯金字塔和 2)拉普拉斯金字塔。 拉普拉斯金字塔的层由高斯金字塔的层与高斯金字塔的高层的扩展版本之间的差形成。
全景图像拼接 Image Blending
查找轮廓 cv.findContours(),绘制轮廓 cv.drawContours()。
一般均衡图像的对比度或者亮度。
OpenCV 函数比 np.histogram() 快大约 40 倍。因此,尽可能使用 OpenCV 函数。
OpenCV 中的直方图均衡
img = cv.imread('wiki.jpg', 0)
equ = cv.equalizeHist(img)
res = np.hstack((img, equ)) # stacking images side-by-side
cv.imwrite('res.png', res)
自适应直方图均衡,CLAHE(对比度受限的自适应直方图均衡)
import numpy as np
import cv2 as cv
img = cv.imread('tsukuba_l.png', 0)
# create a CLAHE object (Arguments are optional).
clahe = cv.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
cl1 = clahe.apply(img)
cv.imwrite('clahe_2.jpg', cl1)
在这种情况下,图像被分成称为“tiles”的小块(在 OpenCV 中,tileSize 默认为 8x8)。然后,像往常一样对这些块中的每一个进行直方图均衡。 因此,在较小的区域中,直方图将限制在一个较小的区域中(除非存在噪声)。如果有噪音,它将被放大。 为了避免这种情况,应用了对比度限制。如果任何直方图 bin 超出指定的对比度限制(在 OpenCV 中默认为 40),则在应用直方图均衡之前, 将这些像素裁剪并均匀地分布到其他 bin。均衡后,要消除图块边界中的伪影,请应用双线性插值。
对于颜色直方图,我们需要将图像从 BGR 转换为 HSV。
看了 color_histogram.py 才明白什么含义。
small = cv.pyrDown(frame)
hsv = cv.cvtColor(small, cv.COLOR_BGR2HSV)
dark = hsv[...,2] < 32
hsv[dark] = 0
# channel = [0,1],因为我们需要同时处理 H 和 S 平面。
# bins = [180,256] 对于 H 平面为 180,对于 S 平面为 256。
# range = [0,180,0,256] 色相值介于 0 和 180 之间,饱和度介于 0 和 256 之间。
h = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
h = np.clip(h*0.005*self.hist_scale, 0, 1)
vis = hsv_map*h[:,:,np.newaxis] / 255.0
cv.imshow('hist', vis)
GIMP(GNU Image Manipulation Program,GNU 图像处理程序),它是一个图像处理与合成工具。GIMP 的扩展性很强,用户可以通过自己编写的插件来扩充 GIMP 功能。
这是由 Michael J. Swain 和 Dana H. Ballard 在他们的论文《通过颜色直方图索引》中提出的。 直方图反投影与 camshift 算法等配合使用。
它用于图像分割或在图像中查找感兴趣的对象。OpenCV 提供了一个内建的函数 cv.calcBackProject()。
// 3. 计算公路的直方图
MatND roiHist; // 直方图对象
int dims = 2; // 特征数目(直方图维度)
float hranges[] = { 0, 180 }; // 特征空间的取值范围
float Sranges[] = { 0, 256 };
const float* ranges[] = { hranges, Sranges };
int size[] = { 20, 32 }; // 存放每个维度的直方图的尺寸的数组
int channels[] = {0, 1}; // 通道数
calcHist(&RoiImage_HSV, 1, channels, Mat(), roiHist, dims, size, ranges);
// 4. 直方图归一化
normalize(roiHist, roiHist, 0, 255, NORM_MINMAX);
// 5. 反向投影
Mat proImage; // 投影输出图像
calcBackProject(&HsvImage, 1, channels, roiHist, proImage, ranges);
import numpy as np
import cv2 as cvfrom matplotlib
import pyplot as plt
# roi 是我们需要找到的对象或对象区域
roi = cv.imread('rose_red.png')
hsv = cv.cvtColor(roi, cv.COLOR_BGR2HSV)
# 目标是我们搜索的图像
target = cv.imread('rose.png')
hsvt = cv.cvtColor(target, cv.COLOR_BGR2HSV)
# 使用 calcHist 查找直方图。也可以使用 np.histogram2d 完成
M = cv.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256] )
I = cv.calcHist([hsvt], [0, 1], None, [180, 256], [0, 180, 0, 256] )
h,s,v = cv.split(hsvt)
B = R[h.ravel(), s.ravel()]
B = np.minimum(B, 1)
B = B.reshape(hsvt.shape[:2])
disc = cv.getStructuringElement(cv.MORPH_ELLIPSE, (5,5))
cv.filter2D(B, -1, disc, B)
B = np.uint8(B)
cv.normalize(B, B, 0, 255, cv.NORM_MINMAX)
ret, thresh = cv.threshold(B, 50, 255, 0)
from 对一张图像使用傅立叶变换就是将它分解成正弦和余弦两部分。也就是将图像从空间域 (spatial domain) 转换到频域 (frequency domain)。 这一转换的理论基础来自于以下事实:任一函数都可以表示成无数个正弦和余弦函数的和的形式。傅立叶变换就是一个用来将函数分解的工具。 2 维图像的傅立叶变换可以用以下数学公式表达 :
$F(k,l) = \displaystyle\sum\limits_{i=0}^{N-1}\sum\limits_{j=0}^{N-1} f(i,j)e^{-i2\pi(\frac{ki}{N}+\frac{lj}{N})}$
$e^{ix} = \cos{x} + i\sin {x}$
式中 f 是空间域 (spatial domain) 值, F 则是频域 (frequency domain) 值。 转换之后的频域值是复数, 因此,显示傅立叶变换之后的结果需要使用实数图像 (real image) 加虚数图像 (complex image), 或者幅度图像 (magitude image) 加相位图像 (phase image)。 在实际的图像处理过程中,仅仅使用了幅度图像,因为幅度图像包含了原图像的几乎所有我们需要的几何信息。 然而,如果你想通过修改幅度图像或者相位图像的方法来间接修改原空间图像,你需要使用逆傅立叶变换得到修改后的空间图像,这样你就必须同时保留幅度图像和相位图像了。
如果您仔细观察结果,尤其是最后一张 JET 颜色的图像,您会看到一些伪像(我用红色箭头标记的一个实例)。 它在那里显示出一些波纹状结构,称为 振铃效应 。 这是由我们用于遮罩的矩形窗口引起的。此掩码转换为正弦形状,从而导致此问题。因此,矩形窗口不用于过滤。更好的选择是高斯窗口。
# 没有缩放参数的简单均值滤波器
mean_filter = np.ones((3,3))
# 创建高斯滤波器
x = cv.getGaussianKernel(5,10)
gaussian = x*x.T
# 不同的边缘检测滤波器
# x 方向上的 scharr
scharr = np.array([[-3, 0, 3],
[-10,0,10],
[-3, 0, 3]])
# x 方向上的 sobel
sobel_x= np.array([[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]])
# y 方向上的 sobel
sobel_y= np.array([[-1,-2,-1],
[0, 0, 0],
[1, 2, 1]])
# 拉普拉斯变换
laplacian=np.array([[0, 1, 0],
[1,-4, 1],
[0, 1, 0]])
为什么拉普拉斯算子是高通滤波器? 从图像中,您可以看到每种内核阻止的频率区域以及它允许经过的区域。从这些信息中,我们可以说出为什么每个内核都是 HPF 或 LPF。 Fourier Transform
cv.matchTemplate(),cv.minMaxLoc()
检测图像中的线条。 cv.HoughLines(),cv.HoughLinesP()
霍夫圈变换,查找图像中的圆。 cv.HoughCircles()
图像分割与 Watershed 算法 分水岭算法实现基于标记的图像分割 cv.watershed()
图像修补技术及相关函数 cv2.inpaint(),人像祛斑应用。
GrabCut 算法来提取图像中的前景。 OpenCV 示例包含一个示例 catchcut.py,这是一个使用 grabcut 的交互式工具。
哈里斯角检测 cv.cornerHarris(), cv.cornerSubPix()
Shi-tomas 拐角检测器和益于跟踪的特征 cv.goodFeaturesToTrack()
SIFT 尺度不变特征变换
SURF 简介(加速的强大功能)
BRIEF(二进制的鲁棒独立基本特征)
FLANN 是近似最近邻的快速库。它包含一组算法,这些算法针对大型数据集中的快速最近邻搜索和高维特征进行了优化。
特征匹配 如何将一个图像中的特征与其他图像进行匹配。
Brute-Force 匹配器和 FLANN 匹配器
把 calib3d 模块中的特征匹配和 findHomography 混合在一起,以在复杂图像中找到已知对象。
这个是基于视频的。
跟踪视频中对象的 Meanshift 和 Camshift 算法。
Lucas-Kanade 方法计算稀疏特征集的光流(在我们的示例中为使用 Shi-Tomasi 算法检测到的角)。OpenCV 提供了另一种算法来查找密集的光流。它计算帧中所有点的光通量。它基于 Gunner Farneback 的算法,在 2003 年 Gunner Farneback 的“基于多项式展开的两帧运动估计”中对此进行了解释。
光流 Lucas-Kanade 方法 使用 cv.calcOpticalFlowPyrLK() 之类的函数来跟踪视频中的特征点。 使用 cv.calcOpticalFlowFarneback() 方法创建一个密集的光流场。
k 近邻(kNN)算法的原理。
SVM 的本质就是画线:所以 SVM 是用来做分类的,而且一条线只能把数据分成两类。
K-Means 的本质就是画圈:所以 K-Means 是一种聚类算法,而且可以画 K 个圈圈分成 K 种类别奥。 https://zhuanlan.zhihu.com/p/104557021
常见的一种方法是 elbow method,x 轴为聚类的数量,y 轴为 WSS(within cluster sum of squares)也就是各个点到 cluster 中心的距离的平方的和。 https://www.zhihu.com/question/29208148
对于 K-means 中 K 的选择,通常有 四种方法:
scipy cluster 库简介 scipy.cluster 是 scipy 下的一个做聚类的 package,共包含了两类聚类方法:
聚类方法实现:k-means 和 hierarchical clustering。
from Programming Computer Vision with Python · 第六章 图像聚类
去除图像中噪声的非局部均值去噪算法。 cv.fastNlMeansDenoising(),cv.fastNlMeansDenoisingColored() 等。 OpenCV 提供了此方法的四个变体。
NL-Means 的全称是:Non-Local Means,直译过来是非局部平均,该算法使用自然图像中普遍存在的冗余信息来去噪声。 与常用的双线性滤波、中值滤波等利用图像局部信息来滤波不同的是,它利用了整幅图像来进行去噪, 以图像块为单位在图像中寻找相似区域,再对这些区域求平均,能够比较好地去掉图像中存在的高斯噪声。 这种算法比较耗时,但是结果很好。对于彩色图像,要先转换到 CIELAB 颜色空间,然后对 L 和 AB 成分分别去噪。
Content-Aware Fill 是 Adobe Photoshop 中使用的一种先进的修复技术。 在进一步的搜索中,我发现 GIMP 中已经存在相同的技术,但名称不同,为“ Resynthesizer”(你需要安装单独的插件)。 https://blog.csdn.net/lcbwlx/article/details/18272295 Resythesizer(正确的应该是 Resynthesizer)
了解如何根据曝光顺序生成和显示 HDR 图像。 使用曝光融合来合并曝光序列。
深度学习与计算机视觉
微信号 uncle_pn
opencv 中的 cv::ximgproc::SuperpixelSLIC, cv::ximgproc::SuperpixelSEEDS, SuperpixelLSC 是超像素算法的
检测直线(利用 cv::ximgproc::FastLineDetector) ximgproc 图像处理模块。包含结构森林,变化域滤波器,导向滤波,自适应流行滤波器,联合双边滤波器和超像素。 from
xphoto 是一个 white balance namespace,opencv 自带了三种白平衡的算法