以下只列举常用的方法和常用的参数,例如枚举,只会将最常用的几个列举出来,详细的请参考官方文档。
对于
C++
默认使用了using namespace cv;
对于
Python
默认使用了import cv2 as cv
和import numpy as np
OpenCV版本:3.4.7
读取图片
1 |
imread(filename, flags) |
filename
:图片路径flags
:IMREAD_COLOR
:默认值,返回3通道的BGR色彩图像IMREAD_GRAYSCALE
:返回单通道灰色图片IMREAD_UNCHANGED
:按原样返回。
C++
1 |
Mat src = imread("1.jpg", IMREAD_UNCHANGED); |
对于C++
来说,imread
的返回值为Mat
类,该类是OpenCV的自建类
Python
1 |
src = cv.imread("1.jpg", cv.IMREAD_UNCHANGED) |
对于Python
来说,imread
的返回值为NumPy.ndarray
对于图像矩阵,
C++
都是自建的Mat
类,而Python
则是numpy.ndarray
。后面不在赘述这个问题。
保存图片
1 |
imwrite(filename, img) |
filename
:图片要保存的路径img
:图像矩阵
比较简单,不做详细介绍。
该方法还有一个参数,可以参数编码,详细点击imwrite
显示图片
1 |
show(winname, src) |
winname
:窗口的名称src
:显示的图片矩阵
图片缩放
图片缩放常见算法有:
- 最近领域插值法
- 双线性插值法
- 双三次插值法
OpenCV官方提供的resize()
的函数(默认为双线性插值法):
1 |
resize(src, dst, dsize, fx, fy, interpolation) |
src
要缩放的图片dst
输出的图像dsize
输出图像的大小C++
:Size
类Python
:元组,前面为width
,后面为height
fx
:沿水平轴的比例因子- 当它等于 0 时,计算为
dsize.width / src.cols
- 当它等于 0 时,计算为
fy
:沿垂直轴的比例因子- 当它等于 0 时,计算为
dsize.height / src.rows
- 当它等于 0 时,计算为
interpolation
:缩放用的方法INTER_NEAREST
:最近邻域插值法INTER_LINEAR
:双线性插值法INTER_CUBIC
:双三次插值法
C++
1 |
Mat dst; |
Python
1 |
dst = cv.resize(src, (100, 100), interpolation=cv.INTER_NEAREST) |
图像操作
仿射变换
矩阵的缩放、旋转、位移都可以使用矩阵变换来实现
缩放矩阵:
$$
\begin{bmatrix}
k_x & 0 & 0 \
0 & k_y &
0
\end{bmatrix}
$$
旋转矩阵:
$$
\begin{bmatrix}
cos\theta & -sin\theta & 0
\
sin\theta & cos\theta & 0
\end{bmatrix}
$$
位移:
$$
\begin{bmatrix}
1 &
0 & t_x \
0 & 1 & t_y
\end{bmatrix}
$$
仿射变换的函数:
1 |
warpAffine(src, dst, M, dsize) |
src
:输入的图像dst
:输出的图像M
:(2 x 3)的转换矩阵dsize
:输出图像的尺寸
1 |
getRotationMatrix2D(center, angle, scale) |
center
:旋转中心c++
:类型为Point2f
Python
:类型为元组,两个元素,分别是x,y
。
angle
:旋转角度,单位为度,正值表示逆时针旋转。scale
:缩放比例
C++
1 |
// 平移矩阵 |
Python
1 |
# 获取图像的高和宽 |
透视变换
透视变换(Perspective Transformation)是指利用透视中心、像点、目标点三点共线的条件,按透视旋转定律使承影面(透视面)绕迹线(透视轴)旋转某一角度,破坏原有的投影光线束,仍能保持承影面上投影几何图形不变的变换。
透视变换的函数:
1 |
warp(src, dst, M, dsize) |
src
:输入的图像dst
:输出的图像M
:(3 x 3)转换矩阵dsize
:输出图像的尺寸
1 |
getPerspectiveTransform(src, dst) |
src
:原图中四边形顶点的坐标C++
:类型为vector<Point2f>
Python
:类型为numpy.ndarray
,dtype
为numpy.float32
dst
:目标图像中的对应四个点的坐标,类型同上。
C++
1 |
// 这里仅仅说明用法,参数根据实际情况,src_point和dst_point应该不一样才对 |
Python
1 |
# 同上 |
图像金字塔(上、下采样)
上采样
1 |
pyrUp(src, dst) |
下采样
1 |
pyrDown(src, dst) |
src
:原图dst
:采样结果图
C++
1 |
Mat up_dst, down_dst; |
Python
1 |
# 上采样 |
图像融合
1 |
addWeighted(src1, alpha, src2, beta, gamma, dst) |
src1
:图1alpha
:图1系数,doublesrc2
:图2beta
:图2系数,doublegamma
:添加的标量dst
:输出图像
C++
1 |
Mat dst; |
Python
1 |
dst = cv.addWeighted(src, 0.5, src, 0.5, 100) |
绘制图形
绘制线段
1 |
line(src, pt1, pt2, color, thickness, lineType) |
src
:图片pt1
:起点C++
:Point
类型Python
:元组类型
pt2
:终止点,类型同上color
:颜色C++
:Scalar
类型,GBRPython
:颜色(GBR)元组
thickness
:线宽,int
,默认值为1lineType
:线类型LINE_AA
:抗锯齿线LINE_8
:8连线,默认值
C++
1 |
line(src, Point(100, 100), Point(200, 200), Scalar(0, 0, 255)); |
Python
1 |
cv.line(src, (100, 100), (200, 200), (0, 0, 255)) |
绘制矩形
1 |
rectangle(src, pt1, pt2, color, thickness, lineType) |
参数同上,但是pt1
和pt2
一定是矩形的对角点。
注意:如果
thickness
为负数,则会使用color
填充整个矩阵
C++
1 |
rectangle(src, Point(100, 100), Point(200, 200), Scalar(0, 0, 255)); |
Python
1 |
cv.rectangle(src, (100, 100), (200, 200), (0, 0, 255)) |
绘制圆
1 |
circle(src, center, radius, color, thickness, lineType) |
src
:图片center
:圆的中点C++
:Point
类型Python
:元组类型,(x, y)
radius
:半径,int
color
:颜色,同line
thickness
:线宽,同line
lineType
:线类型,同line
C++
1 |
circle(src, Point(100, 100), 100, Scalar(0, 0, 255)) |
Python
1 |
cv.circle(src, (100, 100), 100, (0, 0, 255)) |
绘制多边形
该方法用于绘制多条多边形曲线
1 |
polylines(src, pts, isClosed, color, thickness, lineType) |
与上相同的参数不做解释(src
、color
、thickness
、lineType
)
pts
:顶点集合,二维数组,分别表示多边形数量,多边形的顶点位置isClosed
:是否闭合。如果闭合会从最后一个顶点到第一个顶点绘制一条直线。
C++
1 |
vector<vector<Point>> pts{ |
Python
1 |
pts = np.array([[(100, 100), (200, 200), (300, 400), (200, 100)]]) |
绘制文字
1 |
putText(src, text, org, fontFace, fontScale, Color, lineType) |
与上相同的参数不做介绍。
text
:文字文本,string
类型org
:文字左下角的坐标位置C++
:Point
类型Python
:元组类型,(x, y)
fontFace
:字体名称。具体见HersheyFonts,注意:OpenCV默认不支持中文fontScale
:字体的缩放大小。
C++
1 |
putText(src, "I'm a text", Point(100, 100), FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255)); |
Python
1 |
cv.putText(src, "I'm a text", (100, 100), cv.FONT_HERSHEY_PLAIN, 1, (0, 0, 255) |
转换颜色空间
1 |
cvtColor(src, dst, code) |
code
:颜色空间转换代码
该方法我们常常用来将原图转换为灰色图和将GBR转换为HSV
C++
1 |
Mat dst; |
Python
1 |
dst = cv.cvtColor(src, cv.COLOR_BGR2GRAY) |
直方图
统计直方图
1 |
calcHist(images, channels, mask,hist,histSize, ranges) |
images
:需要处理的图片数组channels
:计算对应图片的哪个通道mask
:蒙版hist
:输出的直方图数组histSize
:输出的直方图的大小ranges
:范围
C++
1 |
Mat hist; |
注意:这里的vector后面使用的是大括号,注意vector大括号初始化和小括号初始化的差别。
Python
1 |
hist = cv.calcHist([src], [0], None, [256], [0, 256]) |
或者使用numpy
的方式统计
1 |
# 统计第1个通道,其他类似 |
绘制直方图
C++中绘制直方图比较麻烦,我们使用polylines
取画出多边形,从而形成直方图。抽成自定义函数如下
1 |
void drawHist(Mat &hist, const Scalar &color = Scalar(0, 0, 255)) { |
在Python中使用Matplotlib即可,方便快捷高效。
1 |
plt.plot(hist) |
HSV模型
HSV(Hue, Saturation, Value)是根据颜色的直观特性由A. R. Smith在1978年创建的一种颜色空间, 也称六角锥体模型(Hexcone Model)。
这个模型中颜色的参数分别是:色调(H),饱和度(S),明度(V)
- 当 S = 1,V=1时,H所代表的任何颜色被称为纯色
- 当 S = 0,即饱和度为0,颜色最浅,最浅被描述为灰色,灰色的亮度由V决定,此时H无意义
- 当 V = 0 时,颜色最暗,最暗被描述为黑色,因此此时H和S均无意义。
注意:在OpenCV中,H、S、V的取值范围是[0, 180]、[0, 255]、[0, 255],而不是[0, 360]、[0, 1]、[0, 1]
下面列出部分的HSV空间颜色值:
图像二值化
手动设置阈值
1 |
threshold(src, dst, thresh, maxval, type) |
src
:单通道灰度图thresh
:阈值maxval
:最大值(一般取255)type
:类型
当前公式解析有问题,公式来源
参数名 | 参数解释 |
---|---|
THRESH_BINARY | $dst\left( x,y\right) =\begin{cases}maxval & if\ src(x, y)\ >\ thresh \ 0\end{cases}$ |
THRESH_BINARY_INV | $dst\left( x,y\right) =\begin{cases}0 & if\ src(x, y)\ >\ thresh \ maxval\end{cases}$ |
THRESH_TRUNC | $dst\left( x,y\right) =\begin{cases}threshold & if\ src(x, y)\ >\ thresh \ src(x,y)\end{cases}$ |
THRESH_TOZERO | $dst\left( x,y\right) =\begin{cases}src(x,y) & if\ src(x, y)\ >\ thresh \ 0\end{cases}$ |
THRESH_TOZERO_INV | $dst\left( x,y\right) =\begin{cases}0 & if\ src(x, y)\ >\ thresh \ src(x,y) \end{cases}$ |
OpenCV官网使用了一张图片来描述这5个参数不同的含义,如下:
除了上面的5个参数,还有两个自动算法标志:
参数名 | 参数解释 |
---|---|
THRESH_OTSU | 大津算法 |
THRESH_TRIANGLE | 三角算法(常用在图中出现大量的近视颜色的情况下) |
C++
1 |
Mat dst; |
Python
1 |
# retval 实际阈值 |
自适应阈值
1 |
adaptiveThreshold(src, dst, maxValue, adaptiveMethod, thresholdType, C) |
adaptiveMethod
:自适应阈值算法ADAPTIVE_THRESH_MEAN_C
:附近区域减去恒定的平均CADAPTIVE_THRESH_GAUSSIAN_C
:领域值减去参数C的高斯加权和
thresholdType
:阈值类型,只允许是以下两个THRESH_BINARY
:超过阈值是maxval,低于阈值是0THRESH_BINARY_INV
:超过阈值是0,低于阈值是maxval
blockSize
:邻域大小,必须是奇数。C
:参数C
C++
1 |
adaptiveThreshold(src, dst, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY, 3, 5); |
Python
1 |
dst = cv.adaptiveThreshold(src, 255, |
图片卷积
自定义卷积核
1 |
filter2D(src, dst, ddepth, kernel) |
ddepth
:目标图像的深度kernel
:卷积核
官网给出的计算的公式:
$$
\texttt{dst} (x,y) = \sum _{ \stackrel{0\leq x’ < \texttt{kernel.cols},}{0\leq
y’ < \texttt{kernel.rows}} } \texttt{kernel} (x’,y’)* \texttt{src} (x+x’- \texttt{anchor.x} ,y+y’-
\texttt{anchor.y} )
$$
C++
1 |
Mat dst; |
Python
1 |
# 1. 均值滤波 |
自定义卷积核比较灵活,只要定好卷积核,就可以使用该方法进行卷积。
均值滤波
1 |
blur(src, dst, ksize) |
ksize
:卷积核尺寸C++
:Size
类型Python
:元组类型
C++
高斯模糊
1 |
GaussianBlur(src, dst, ksize, sigmaX, sigmaY) |
sigmaX
:x轴上的高斯标准差sigmaY
:y轴上的高斯标准差,如果为0,设置为等于sigmaY
**C++**:
1 |
Mat dst; |
Python
1 |
dst = cv.GaussianBlur(src, (3, 3), 1, 1) |
中值滤波
1 |
medianBlur(src, dst, ksize) |
注意此处的 ksize 为int类型
C++
1 |
medianBlur(src, dst, 3); |
Python
Sobel算子
1 |
Sobel(src, dst, ddepth, dx, dy, ksize=3) |
dx
:沿x轴的阶数dy
:沿y轴的阶数
C++
Python
1 |
# 沿 x 轴计算一阶sobel |
Scharr滤波器
1 |
Scharr(src, dst, ddepth, dx, dy) |
C++
1 |
// 沿 x 轴计算一阶 Scharr |
Python
1 |
# 沿 x 轴计算一阶 Scharr |
拉普拉斯算子
1 |
Laplacian(src, dst, ddepth, ksize=1) |
C++
1 |
Laplacian(src, dst, -1); |
Python
1 |
dst = cv.Laplacian(src, -1) |
canny边缘检测算法
1 |
Canny(src, edges, threshold1, threshold2) |
edges
:边缘图,单通道8位。threshold1
:第一个阈值threshold2
:第二个阈值
C++
1 |
Canny(src, dst, 50, 100); |
Python
1 |
dst = cv.Canny(src, 50, 100) |
双边滤波
1 |
bilateralFilter(src, dst, d, sigmaColor, sigmaSpace) |
d
:滤波期间使用的每个像素邻域的直径。sigmaColor
:在色彩空间中过滤的标准差sigmaSpace
:在坐标空间中过滤的标准差
C++
1 |
bilateralFilter(src, dst, 3, 1, 1); |
Python
1 |
dst = cv.bilateralFilter(src, 3, 1, 1) |
霍夫变换
霍夫圆
使用霍夫变换在灰度图中查找圆
1 |
HoughCircles(src, circles, method, dp, minDist, |
circles
:找到的圆的输出向量method
:检测方法- 当前唯一实现的方法是
HOUGH_GRADIENT
- 当前唯一实现的方法是
dp
:分辨率,累加器分辨率和图像分辨率的反比。minDist
:检测到圆心的最小距离param1
:传递给Canny边缘检测器的两个阈值中的更高的那个,更低的是它的一半param2
:它是检测圆心的累加器阈值,越小,假圆可能越多。minRadius
:最小圆半径maxRadius
:最大圆半径
C++
1 |
Mat circles; |
Python
1 |
circles = cv.HoughCircles(src, HOUGH_GRADIENT, 1, 100, 160, 50, 0, 1000) |
霍夫直线变换–找直线
使用标准霍夫变换或者标准多尺度霍夫变换查找直线
1 |
HoughLines(src, lines, rho, theta, threshold, |
lines
:检测出来的直线(里面的参数是rho和theta)rho
:距离分辨率(以像素为单位)theta
:角度分辨率(以弧度为单位)threshold
:累加器阈值srn
:它是距离分辨率rho的除数stn
:它是角度分辨率theta的除数,如果两个都等于0,则使用标准霍夫变换。否则使用多尺度霍夫变换min_theta
:最小角度:介于0
和max_theta
之间max_theta
:最大角度:介于min_theta
和CV_PI
之间
注意:输入图必须是二值图
C++
1 |
// 将 src 转化为二值图 |
Python
1 |
# 将 src 转化为二值图 |
注意 lines 的参数分别对应的是一条直线的 rho 和 theta
霍夫直线变换–找线段
使用概率霍夫变换在二进制图像中查找线段
1 |
HoughLinesP(src, lines, rho, theta, theshold, minLineLength=0, maxLineGap=0) |
minLineLength
:线段的最小长度。maxLineGap
:连接该线上的点之间的最大允许间隙。
注意:输入图必须是二值图
C++
1 |
Mat binary, gray; |
Python
1 |
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) |
注意:实际使用中,C++的
HoughLineP
和Python的HoughLineP
最终的结果有差异,原因未知。
查找轮廓和绘制轮廓
1 |
findContours(image, contours, hierachy, mode, method) |
参数描述:
image
:二值图contours
:查找到的所有轮廓hierachy
:层级关系(目前很少用到)mode
:轮廓的检索模式RETR_EXTERNAL
:仅检索外部轮廓。RETR_LIST
:不建立索引关系的情况下,检索所有轮廓。RETR_CCOMP
:检索所有轮廓,分为两级层次结构。RETR_TREE
:检索所有轮廓,重建嵌套的完整结构。
method
:轮廓近似方法CHAIN_APPROX_NONE
:绝对存储所有轮廓点CHAIN_APPROX_SIMPLE
:压缩水平,垂直和对角线段,仅保留其端点。
1 |
drawContours(image, contours, contourIdx, color, thickness, lineType) |
contours
:上面方法找到的所有轮廓点countourIdx
:要绘制的索引,-1代表所有
C++
1 |
Mat gray, binary; |
Python
1 |
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) |
注意:
findContours
会返回三个值,分别是,图像,轮廓,层级。
膨胀和腐蚀
形态学变化是基于图像形状的一些简单操作。操作对象一般是二值图像,需要两个输入,一个是我们的原图,另一个是3x3的结构元素(内核),决定了膨胀操作的本质。常见的操作是图像的膨胀和腐蚀。以及他们的进阶操作注入Opening、Closing、Gradient等等。
获取结构元素
1 |
getStructuringElement(shape, ksize) |
shape
: 结构元素的形状MORPH_RECT
:矩形结构元素MORPH_CROSS
:十字形结构元素MORPH_ELLIPSE
:椭圆形结构元素
用法在下面
膨胀
用大值填充小值
1 |
dilate(src, dst, kernel) |
kernel
:结构元素
C++
1 |
Mat dilate_later; |
Python
1 |
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) |
腐蚀
用小值填充大值
1 |
erode(src, dst, kernel) |
C++
1 |
Mat erode_later; |
Python
1 |
kernel = cv.getStructuringElement(cv.MORPH_RECT, (5, 5)) |
高级形态转化
1 |
morphologyEx(src, dst, op, kernel) |
op
:形态转化方式MORPH_ERODE
:腐蚀MORPH_DILATE
:膨胀MORPH_OPEN
:开操作,先腐蚀后膨胀MORPH_CLOSE
:闭操作,先膨胀后腐蚀- …
C++
1 |
Mat src_later; |
Python
1 |
kernel = getStructuringElement(cv.MORPH_RECT, Size(5, 5)) |
泛洪填充(漫水填充)
1 |
floodFill(image, mask,seedPoint, newVal, rect, loDiff=Scalar(), upDiff=Scalar(), flags) |
seedPoint
:起始点newVal
:新值rect
:最小边界矩形,一般使用默认loDiff
:最大较低色差upDiff
:最大较高色差flags
:操作标志FLOODFILL_FIXED_RANGE
$$
\texttt{src} (x’,y’)- \texttt{loDiff} \leq \texttt{src} (x,y) \leq \texttt{src} (x’,y’)+
\texttt{upDiff}
$$
C++
1 |
// {5, 5} 是一种简写方式,会自动生成 Point,{0, 0, 255}同理,会自动生成 Scalar |
Python
1 |
cv.floodFill(src, (5, 5), (0, 0, 255)); |
图像分水岭
1 |
watershed(image, markers) |
markders
:标记,类型必须是CV_32S
C++
1 |
Mat markers(src.size(), CV_32S); |
Python
1 |
height, width = src.shape[:2] |
注意:标定点一般需要我们实际取获取。这里仅仅是使用固定点举例子罢了。
距离变换
1 |
distanceTransform(src, dst, distanceType, maskSize, dstType=CV_32F) |
src
:8位单通道二进制图dst
:输出具有计算出的距离图像。它是大小与src
相同的8位或32位浮点单通道图像。distanceType
:距离类型,一般采用欧式距离就好了DIST_L1
:distance = |x1-x2| + |y1-y2|DIST_L2
:简单的欧式距离DIST_C
:distance = max(|x1-x2|,|y1-y2|)DIST_L12
:L1-L2 metric: distance = 2(sqrt(1+x*x/2) - 1))DIST_FAIR
:distance = c^2(|x|/c-log(1+|x|/c)), c = 1.3998DIST_WELSCH
:distance = c^2/2(1-exp(-(x/c)^2)), c = 2.9846DIST_HUBER
:distance = |x|<c ? x^2/2 : c(|x|-c/2), c=1.345
maskSize
:距离变换蒙版大小,一般用3或5dstType
:输出类型,一般采用默认值
C++
1 |
Mat gray, binary, dst; |
Python
1 |
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) |