这个其实不是笔记...
之前这里的文章内容弄丢了,
于是找了个笔记替补上,
然后顺便用来测试公式渲染是否正常。


OpenCV (python)

Common APIs

  • add(src1, src2, dst=None, mask=None)

饱和式加法 (>255自动归为255)

  • subtract(src1, src2, dst=None, mask=None)

饱和式减法 (<0自动归为0)

  • applyColorMap(src: mat, code)

应用于灰度图像, 产生伪彩色图像

code: 可取cv2.COLORMAP_JET, cv2.COLORMAP_AUTUMN, cv2.COLORMAP_COOL, cv2.COLORMAP_HOT

  • bitwise_and(src1, src2, dst=None, mask=None)

对两个图像矩阵数组或一个数组与标量的按位与
src2: mat or int or tuple(1x4), int 等价于 tuple(int, 0, 0, 0)
参考链接

  • bitwise_xor, bitwise_or 类似
  • split(src), merge(arr)
b, g, r = cv2.split(img)
merged  = cv2.merge([b,g,r])
  • minMaxLoc(src)

return: min: float, max: float, minLoc: tuple(1x2), maxLoc: tuple(1x2)

  • meanStdDev(src: mat)

return: means: float, stddev: float

  • normalize(src: mat, dst: mat, alpha, beta, norm_type, dtype, mask)

norm_type: NORM_MINMAX, NORM_INF, NORM_L1, NORM_L2

NORM_MINMAX: 线性归一化
$dst(i, j) = \frac{(src(i, j) - min(src(x, y))) * (beta - alpha)}{max(src(x, y)) - min(src(x, y))} + alpha$

NORM_INF: 分母为L∞范数, 即矩阵各元素绝对值的最大值(切比雪夫距离)
$dst(i, j) = \frac{src(i, j)}{Max|src(x, y)|}$

NORM_L1: 分母为L1-范数, 即矩阵元素的绝对值之和(曼哈顿距离)
$dst(i, j) = \frac{src(i, j)}{|\sum{src(x, y)}|}$

NORM_L2: 分母为L2-范数, 即矩阵各元素的欧几里德距离之和
$dst(i, j) = \frac{src(i, j)}{\sqrt{ \sum{src(x, y)^2} }}$

  • flip(src: mat, code)

code: 1 (沿y翻转), 0 (沿x), -1 (对角线)

  • resize(src: mat, dsize: tuple, dst, fx, fy, interpolation)

fx, fy: float = 沿x轴, y轴的缩放系数, 使用时候dsize=(0, 0)
dsize和fx, fy用一个即可, 其中一个置零, 则默认使用另一个计算最终尺寸。

interpolationdescription
INTER_NEAREST最近邻插值
INTER_LINEAR双线性插值(默认设置)
INTER_AREA使用像素区域关系进行重采样
INTER_CUBIC4x4像素邻域的双三次插值
INTER_LANCZOS48x8像素邻域的Lanczos插值
  • line(img, pt1: tuple(x1, y1), pt2: tuple(x2, y2), color, thickness)

img:要划的线所在的图像

  • rectangle(img: mat, pt1: tuple(x1, y1), pt2: tuple(x2, y2), color: tuple(3x3), thickness: int)
  • circle(img: mat, center: tuple(x, y), radius: float, color: tuple(3x3), thickness: int)

thickness = -1为填充模式

  • putText(img, text, org: tuple(1x2), fontFace, fontScale, color, thickness, lineType)

org: 左上角坐标
fontFace: cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_PLAIN, etc...
lineType: LINE_AA (抗锯齿), LINE_4, LINE_8 (快、丑)

  • ellipse()
  • polylines(img, pts, isClosed, color, thickness, lineType, shift)

折线绘制,也可绘制闭合多边形

pts: 包含多边形上点的数组
isClosed: 决定所绘制的多边形是否闭合。若为 True, 则画若干个闭合多边形;若为 False, 则画一条连接所有点的折线
shift: 坐标精确到小数点后第几位

  • inRange(img, lower, upper)

将低于lower和高于upper部分变成0, lower~upper之间变成255

  • calcHist(images, channels, mask, histSize: tuple, ranges: tuple, accumulate)

计算图像直方图

histSize: 元素取值划分的等分
accumulate: 若为True表示多个图像时累积计算像素值个数

cv2.calcHist([img], [i], None, [256], [0, 256])
  • compareHist(H1: Any, H2: Any, method: Any)

method: cv2.HISTCMP_CORREL, cv2.HISTCMP_CHISQR, cv2.HISTCMP_INTERSECT, cv2.HISTCMP_BHATTACHARYYA

参考链接

  • GaussianBlur(src, ksize: tuple(1x2), sigmaX: double, sigmaY: double = 0)

高斯滤波与均值滤波类似,都是矩形窗口内所有像素点的像素值加权和,只不过其权重与均值滤波不一样。高斯滤波的权重服从二维正态分布,越靠近窗口中心点(也即当前滤波点),权重越大。

ksize: 高斯内核的大小。ksize.width和ksize.height可以不同,但他们都必须为正奇数
sigmaX: 表示高斯核函数在X方向的的标准偏差。
sigmaY: 表示高斯核函数在Y方向的的标准偏差。若sigmaY为零,就将它设为sigmaX。

如果sigmaX和sigmaY都是0,那么就由ksize.width和ksize.height计算出来。

对于(2n+1)*(2n+1)窗口,其权重计算公式如下,其中σ为标准差,σ越大,权重分布越均匀,滤波效果越好,图像越模糊。反之,σ越小,权重分布越偏向于窗口中心点,滤波效果越差,图像越能保留其原有清晰度。

$w(i, j)=\frac{1}{2\pi\sigma^2}\exp\{{-\frac{(i-n)^2 + (j-n)^2}{2\sigma^2}}\}$

公式参考1
公式参考2

  • bilateralFilter(src, d, sigmaColor, sigmaSpace)

双边滤波器: 滤除噪声, 且保留住图像的边缘纹理等 (因噪声、边缘、纹理均为高频信息, 所以高斯滤波会在滤除噪声同时使边缘模糊)

d: The kernel size of the filter.
sigmaColor: 色彩空间的标准方差, 一般尽可能大,较大意味着像素邻域内较远的颜色会混合在一起,从而产生更大面积的半相等颜色。
sigmaSpace: 坐标空间的标准方差(像素单位),一般尽可能小。参数值越大意味着只要它们的颜色足够接近,越远的像素都会相互影响。当d>0时,它指定邻域大小而不考虑sigmaSpace。 否则,d与sigmaSpace成正比。

若输入d!=0, 则sigmaSpace由d计算得出. 如果sigmaColor未输入,则sigmaColor由sigmaSpace计算得出。

双边滤波器的输出像素依赖于当前被卷积像素的邻域。 $i$和$j$是当前被卷积像素的坐标点,$k$和$l$是邻域像素的坐标点,公式表明取决于被卷积像素的灰度值和邻域像素的灰度值的差

$w(i, j, k, l) = \exp\left( -\frac{(i-k)^2+(j-l)^2}{2\sigma_d^2} - \frac{\Vert f(i,j) - f(k,l) \Vert ^2}{2\sigma_r^2} \right)$

公式链接参考

  • pyrMeanShiftFiltering(src, sp, sr[, dst[, maxLevel[, termcrit]]])

均值偏移滤波, 均值漂移算法, 图像在色彩层面的平滑滤波, 可中和色彩分布相近的颜色, 平滑色彩细节, 侵蚀掉面积较小的颜色区域。通常还用floodFill()将不同连通域涂上不同的颜色。

sp: 漂移物理空间半径大小
sr: 漂移色彩空间半径大小
maxLevel: 金字塔的最大层数
termcrit: 漂移迭代终止条件 迭代10次或者至少移动1次可表示为: ( cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, 10, 1 )
sp和sr越小,细节保留得越多,sp和sr越大,平滑力度越大。
参考

  • calcBackProject(...)
  • meanShift, CamShift(src, track_window, term_crit)

用于寻找和追踪视频中的目标物体

参考链接

  • polylines(img, pts: array, isClosed: bool, color: tuple, thickness: int=None, lineType=None, shift=None)

pts:为所画多边形的顶点坐标,当一张图片需要有多个四边形时,该数组shape为Nx4x2 (四个顶点的xy坐标)
isClosed: 所画四边形是否闭合,通常为True
color: RGB三个通道的值
shift:顶点坐标中小数的位数

  • edgePreservingFilter(src, flags=1, sigma_s=60, sigma_r=0.4)

是滤波过程中能够有效的保留图像中的边缘信息的滤波器

flags:保变滤波器类型。取RECURS_FILTER(递归滤波)= 1, NORMCONV_FILTER(归一化卷积)= 2。使用RECURS_FILTER选项比NORMCONV_FILTER快约3.5倍。但NORMCONV_FILTER产生边缘锐化。当不希望锐化时,要求速度应该使用RECURS_FILTER
sigma_s:范围在0到200之间
sigma_r:范围在0到1之间
当sigma_s不变,sigma_r越大图像滤波效果越明显;
当sigma_r不变,sigma_s越大图像模糊效果越明显;
当sgma_r取值很小时效果不好

  • filter2D(src, ddepth, kernel, dst, anchor: Point = Point(-1,-1), delta: double = 0, borderType: int = BORDER_DEFAULT)

使用自定义内核对图像进行卷积。该函数实际计算的是相关性,而不是卷积

ddepth: 目标图像的所需深度,可取src.depth()或者-1
kernel: 卷积核是一种单通道浮点矩阵;若要将不同的核应用于不同的通道,使用split再分别处理
anchor: 表示锚点(即被平滑的那个点) 默认值Point(-1,-1),即取核的中心为锚点。锚点指内核中过滤点的相对位置
delta: 在将筛选的像素存储到dst中之前添加到这些像素的可选值。说的有点专业了其实就是给所选的像素值添加一个值delta。类似于偏置
borderType: 用于推断图像外部像素的某种边界模式, 默认为BORDER_DEFAULT

$\texttt{dst} (x,y) = \sum _{ \stackrel{0\leq x' \lt \texttt{kernel.cols},}{0\leq y' \lt \texttt{kernel.rows}} } \texttt{kernel} (x',y')* \texttt{src} (x+x'- \texttt{anchor.x} ,y+y'- \texttt{anchor.y} )$

参考链接

  • fillPoly(src, [polypoints, polypoints, ...], color)

绘制多个多边形

polypoints: 多边形外接顶点

  • *fillConvexPoly(src, polypoint, color)

绘制单个多边形

  • approxPolyDP(curve, epsilon, closed, approxCurve)

把一个连续光滑曲线折线化

Binary & Contours Related APIs

  • threshold(src, thresh, maxval, type[, dst]) → retval, dst

二值化图像 (基于全局阈值)

src:灰度化的图片
thresh: 阈值
maxval: 最大值
type:划分时使用的算法
retval: 返回阈值
dst: 返回二值化图像

optionsI > threshelse
cv2.THRESH_BINARYmaxval0
cv2.THRESH_BINARY_INV0maxval
cv2.THRESH_TRUNCthresh当前灰度值
cv2.THRESH_TOZERO当前灰度值0
cv2.THRESH_TOZERO_INV0当前灰度值
optionsnamesummary
cv2.THRESH_OTSU大津法自动寻全局阈值
cv2.THRESH_TRIANGLE三角形法自动寻全局阈值
cv2.THRESH_MASK未知未知
  • adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None)

自适应阈值函数。原理是将图像分割不同的区域,每个区域都计算阈值。

src:灰度化的图片
maxValue:满足条件的像素点需要设置的灰度值
adaptiveMethod:自适应方法。可为ADAPTIVE_THRESH_MEAN_C, ADAPTIVE_THRESH_GAUSSIAN_C
thresholdType:二值化方法,可为THRESH_BINARYTHRESH_BINARY_INV
blockSize:分割计算的区域大小,取奇数
C:常数,每个区域计算出的阈值的基础上在减去这个常数作为这个区域的最终阈值,可以为负数
dst:输出图像,可选

ADAPTIVE_THRESH_MEAN_C(均值)方法就是以计算区域像素点灰度值的平均值作为该区域所有像素的灰度值。这其实就是一种平滑或滤波作用。
ADAPTIVE_THRESH_GAUSSIAN_C(高斯加权和算法)是将区域中点周围的像素根据高斯函数加权计算他们离中心点的距离。一般情况下建议使用高斯加权和。

一般情况下,滤波器宽度应该大于被识别物体的宽度。

参考文章
原理解析

  • findContours(image, mode, method[, contours[, hierarchy[, offset ]]]) -> contours, hierarchy

image: 二值图
mode:轮廓的检索模式,可为:

optionsdescriptions
cv2.RETR_EXTERNAL表示只检测外轮廓,包含在外围轮廓内的内围轮廓被忽略
cv2.RETR_LIST检测所有的轮廓,包括内围、外围轮廓,但是检测到的轮廓不建立等级关系,彼此之间独立,没有等级关系
cv2.RETR_CCOMP检测所有的轮廓,但所有轮廓只建立两个等级关系,外围为顶层,若外围内的内围轮廓还包含了其他的轮廓信息,则内围内的所有轮廓均归属于顶层
cv2.RETR_TREE建立一个等级树结构的轮廓外层轮廓包含内层轮廓,内层轮廓还可以继续包含内嵌轮廓

method:轮廓的近似办法,可为

optionsdescriptions
cv2.CHAIN_APPROX_NONE存储所有边界点
cv2.CHAIN_APPROX_SIMPLE压缩垂直、水平、对角方向,只保留端点

offset:Point偏移量,所有的轮廓信息相对于原始图像对应点的偏移量,相当于在所有检测出的轮廓点上加上该偏移量,并且Point可为负值

contours:一个list,list中每个元素都是图像中的一个轮廓,用numpy中的ndarray表示。

hierarchy:轮廓关系的内在索引编号。如果选择了cv2.RETR_TREE,则以树形结构组织输出,hierarchy[0][i]的四列分别对应下一个轮廓索引、上一个轮廓索引、子轮廓索引、父轮廓索引。该值为负数表示没有对应项。

  • drawContours(img, contours, contourIdx, color, thickness)

contours:轮廓。
contourIdx:轮廓的索引,-1表示绘制所有轮廓

  • arcLength(contours[i], isClosed: bool)

求轮廓长度

isClosed: 是否封闭

  • contourArea(contours[i])

求轮廓面积

  • minAreaRect(points) -> rect

求出点集下的最小面积矩形

points: 点集,如array((x1,y1),(x2,y2),....,(xn,yn))

rect: ((x, y), (width, height), θ)
rect[0]: 矩形中点坐标 (x, y)
rect[1][0]: 宽度width
rect[1][1]: 长度heith
rect[2]: 旋转角度 θ

配合使用 (获取选择矩阵):

if rect[2] < -45.:
    rect[2] += 90
rot_mat = cv2.getRotationMatrix2D(rect[0], rect[2], 1)
  • boxPoints(rect) -> array

获取矩形的四个顶点坐标

rect: 使用minAreaRect()返回的rect

返回: array([[x1, y1], [x2, y2], [x3, y3], [x4, y4]], 'float')

并不清楚这四个坐标点各对应着矩形的哪一个顶点,可以通过下面方法获取
new: 据说boxPoints返回四个点顺序:左上→右上→右下→左下 但是因为旋转出现颠倒问题

# 获取四个顶点坐标
left_point_x = np.min(box[:, 0])
right_point_x = np.max(box[:, 0])
top_point_y = np.min(box[:, 1])
bottom_point_y = np.max(box[:, 1])
 
left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0]
right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0]
top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0]
bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0]
# 上下左右四个点坐标
vertices = np.array([[top_point_x, top_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y], [right_point_x, right_point_y]])
  • boundingRect(contours[i]) -> (x, y, w, h)

获取轮廓最小正矩形

x, y 为矩形左上角顶点的坐标

  • moments(src, binaryImage: bool) -> dict

计算图像中的中心矩

src: 光栅图像(单通道,8-bit或浮点型二维数组), 或二维数组(1 X N或N X 1), 二维数组类型为Point或Point2f
binaryImage: 默认值是false,如果为true,则所有非零的像素都会按值1对待

例子:求取质心

mu = cv2.moments(cnt)  # cnt是一个轮廓
cx = mu['m10'] / mu['m00']  # 轮廓的质心的横坐标
cy = mu['m01'] / mu['m00']  # 轮廓的质心的纵坐标
  • HuMoment(mu) -> array

计算hu矩

mu: 由moments函数算得

  • matchShapes(cnt1, cnt2, method, param)

对两个Hu矩或灰度图进行匹配

cnt1:轮廓向量或者灰度图。
cnt2:轮廓向量或者灰度图。
cnt1与cnt2无顺序之分
method:使用的比较方式,可为:
CONTOURS_MATCH_I1 对应:$\sum|{\frac{1}{H_i^B} - \frac{1}{H_i^A}}|$
CONTOURS_MATCH_I2 对应:$\sum|{H_i^B - H_i^A}|$
CONTOURS_MATCH_I3 对应:$\sum\frac{|{H_i^B - H_i^A}|}{| H_i^A |}$
param: 取0即可

  • warpAffine(src, M, dsize=(width, height)) -> dst

仿射变换

M:变换矩阵
dsize:输出图像尺寸大小, 必填

  • warpPerspective(src, M, dsize) -> dst

透视变换

同warpAffine,仿射变换与透视变换关系

  • getRotationMatrix2D(center, angle, scale) -> M

获取仿射变换的转换矩阵

angle: 逆时针旋转角度
scale: 缩放大小

def rotate_img(img, angle):
    h, w = img.shape[:2]
    rotate_center = (w/2, h/2)
    M = cv2.getRotationMatrix2D(rotate_center, angle, 1.0)
    new_w = int(h * np.abs(M[0, 1]) + w * np.abs(M[0, 0]))
    new_h = int(h * np.abs(M[0, 0]) + w * np.abs(M[0, 1]))
    M[0, 2] += (new_w - w) / 2
    M[1, 2] += (new_h - h) / 2
    rotated_img = cv2.warpAffine(img, M, (new_w, new_h))
    return rotated_img
  • getAffineTransform(src, dst) -> M

指定原始图像的三个顶点坐标,并指定目标图像对应的三个点的坐标,来生成仿射变换矩阵M

src: 输入图像的三个点坐标, 形为:array([[x, y], [x, y], [x, y]], float32)
dst: 输出图像的三个点坐标, 形式同上

  • getPerspectiveTransform(src, dst) -> M

获取将图像投影到一个新的平面的透视变换矩阵M

src: 透视变换前的4点对应位置,形如: array([[x, y], [x, y], [x, y], [x, y]], float32)
dst: 透视变换后的4点对应位置

  • approxPolyDP(curve, epsilon, closed: bool) -> points (类似于cnt)

多边拟合函数,将轮廓拟合成多边形

epsilon:指定的精度,即是原始曲线与近似曲线之间的最大距离。
closed:true则近似曲线是闭合的。

ep = 0.01*cv2.arcLength(cnt, True)
ap = cv2.approxPolyDP(cnt, ep, True)