(2)OpenCV——图像操作

二、OpenCV图像操作

2.1 像素操作

2.1.1 图像像素统计

(1)寻找最大/最小像素值
void cv::minMaxLoc(InputArray src, 
                   double *minVal, 
                   double *maxVal = 0, 
                   Point *minLoc = 0, 
                   Point *maxLoc = 0, 
                   InputArry mask = noArry());
  • src:输入图像/矩阵。
  • minVal:图像/矩阵中的最小值。
  • maxVal:图像/矩阵中的最大值。
  • minLoc:图像/矩阵最小值坐标。
  • maxLoc:图像/矩阵最大值坐标。
  • mask:掩膜,用于在指定区域寻找最值。

Tips2-1:minMaxLoc( )注意点

输入图像/矩阵要求为单通道,对于多通道矩阵或图像,需要通过reshape()方法将多通道转换为单通道后再使用:

cv::Mat cv::Mat::reshape(int cn, 
                         int rows = 0);
  • cn:转换后矩阵的通道数。
  • rows:转换后矩阵的行数(默认0,与转换前相同)。
(2)计算图像的平均值和标准差

图像像素值的平均值表示图像整体的亮暗程度,标准差表示图像明暗变化的对比程度。OpenCV4中提供了用于计算图像平均值的mean()函数:

cv::Scalar cv::mean(InputArray src,
                    InputArray mask = noArray);
  • src:输入图像。
  • mask:掩膜,用于标记求均值的图像范围。

Tips2-2:mean( )注意点

  1. mean()分别计算每个通道的均值,并返回一个4位的Scalar,若图像只有1个通道,则后三位均为0。
  2. 可以通过cv::Scalar[n]来查看第n个通道的均值。

除了mean()方法,OpenCV4还提供了meanStdDev()函数用于同时求取均值和标准差:

void cv::meanStdDev(InputArray src,
                    OutputArray mean,
                    OutputArray stddev,
                    InputArray mask = noArray());
  • src:输入图像。
  • mean:图像每个通道的均值。
  • stddev:图像每个通道的标准差。
  • mask:掩膜,用于标记求均值的图像范围。

2.1.2 图像间操作

(1)比较运算

OpenCV4中提供了用于比较两幅图像每一个对应位置像素值大小,并保留较大(较小)像素值的max()min()方法:

void cv::max(InputArray src1,
             InputArray src2,
             OutputArray dst);
void cv::min(InputArray src1,
             InputArray src2,
             OutputArray dst);
  • src1:输入图像。
  • src2:第二张输入图像,要求尺寸、通道数与数据类型和第一张输入图像相同。
  • dst:输出图像。
(2)逻辑运算

除了比较运算,OpenCV4中还提供了用于两幅图像间逻辑运算(与、或、非、异或)的函数:

void cv::bitwise_and(InputArray src1,
                     InputArray src2,
                     OutputArray dst,
                     InputArray mask = noArray());
void cv::bitwise_or(InputArray src1,
                    InputArray src2,
                    OutputArray dst,
                    InputArray mask = noArray());
void cv::bitwise_xor(InputArray src1,
                     InputArray src2,
                     OutputArray dst,
                     InputArray mask = noArray());
void cv::bitwise_not(InputArray src1,
                     InputArray src2,
                     OutputArray dst,
                     InputArray mask = noArray());
  • src1:输入图像。
  • src2:第二张输入图像,要求尺寸、通道数与数据类型和第一张输入图像相同。
  • dst:输出图像,要求尺寸、通道数与数据类型和第一张输入图像相同。
  • mask:掩膜,用于标记逻辑运算的图像范围。

Tips2-3:图像间的逻辑运算规则

对于二值图像,逻辑运算对应正常的运算规则。对于CV_8U这类数据,则需要先将像素值转换为二进制数后再进行逻辑运算,具体运算规则见表2-1:

表2-1 图像逻辑运算规则
图像数据类型像素值1像素值2异或非(图像1)
二值000001
二值100110
二值010111
二值111100
8位00000255
8位56473250

2.1.3 图像二值化

只有黑白两色(即无论什么数据类型,只有最大值和最小值)的图像称为二值图像。二值图色彩种类少,可进行高度压缩,节省存储空间。OpenCV4中提供了threshold()adaptiveThreshold()用于实现图像的二值化:

double cv::threshold(InputArray src,
                     OutputArray dst,
                     double thresh,
                     double maxval,
                     int type);
  • src:待二值化图像,只能为CV_8U或CV_32F两种数据类型。
  • dst:二值化后图像,要求与输入图像有相同尺寸、通道数和数据类型。
  • thresh:二值化阈值。
  • maxval:二值化过程的最大值(仅在THRESH_BINARY和THRESH_BINARY_INV方法中使用)。
  • type:二值化方法。THRESH_OTSU和THRESH_TRIANGLE可以与前5种方法混用,如“THRESH_BINARY | THRESH_OTSU”,详见表2-2
表2-2 二值化方法标志
type简记作用
THRESH_BINARY0 d s t ( x , y ) = { m a x v a l  if  s r c ( x , y ) > t h r e s h 0  else  dst(x,y)=\begin{cases}maxval & \text{ if } src(x,y)>thresh \\0 & \text{ else }\end{cases} dst(x,y)={maxval0 if src(x,y)>thresh else 
THRESH_BINARY_INV1 d s t ( x , y ) = { 0  if  s r c ( x , y ) > t h r e s h m a x v a l  else  dst(x,y)=\begin{cases}0 & \text{ if } src(x,y)>thresh \\maxval & \text{ else }\end{cases} dst(x,y)={0maxval if src(x,y)>thresh else 
THRESH_TRUNC2 d s t ( x , y ) = { t h r e s h o l d  if  s r c ( x , y ) > t h r e s h s r c ( x , y )  else dst(x,y)=\begin{cases}threshold & \text{ if } src(x,y)>thresh \\src(x,y) & \text{ else}\end{cases} dst(x,y)={thresholdsrc(x,y) if src(x,y)>thresh else
THRESH_TOZERO3 d s t ( x , y ) = { s r c ( x , y )  if  s r c ( x , y ) > t h r e s h 0  else dst(x,y)=\begin{cases}src(x,y) & \text{ if } src(x,y)>thresh \\0 & \text{ else}\end{cases} dst(x,y)={src(x,y)0 if src(x,y)>thresh else
THRESH_TOZERO_INV4 d s t ( x , y ) = { 0  if  s r c ( x , y ) > t h r e s h s r c ( x , y )  else dst(x,y)=\begin{cases}0 & \text{ if } src(x,y)>thresh \\src(x,y) & \text{ else}\end{cases} dst(x,y)={0src(x,y) if src(x,y)>thresh else
THRESH_OTSU8大津法自动求取全局阈值
THRESH_TRIANGLE16三角形法自动求取全局阈值
void cv::adaptiveThreshold(InputArray src,
                           OutputArray dst,
                           double maxValue,
                           int adaptiveMethod,
                           int thresholdType,
                           int blockSize,
                           double C);
  • src:待二值化图像,只能为CV_8UC1数据类型。
  • dst:二值化后图像。与输入图像有相同尺寸、通道数和数据类型。
  • maxValue:二值化最大值。
  • adaptiveMethod:自适应阈值方法,包括ADAPTIVE_THRESH_MEAN_C(均值法)和ADAPTIVE_THRESH_GAUSSIAN_C(高斯法)两种。
  • thresholdType:图像二值化方法的标志,包括THRESH_BINARY和THRESH_BINARY_INV两种,详见表2-2
  • blockSize:自适应确定阈值的相邻像素大小,一般为3,5,7的奇数。
  • C:从平均值或者加权平均值中减去的常数(可正可负)。

若要与多个阈值进行比较,则可以使用LUT()方法。LUT可以理解为一个像素灰度值的映射表,它以像素灰度值作为索引,以灰度映射后的数值作为表中的内容。

void cv::LUT(InputArray src,
             InputArray lut,
             OutputArray dst);
  • src:输入图像矩阵,只能为CV_8U数据类型。
  • lut:256个像素灰度值的查找表,单通道或与src通道数相同。
  • dst:输出图像矩阵,尺寸与src相同,数据类型与lut相同。

2.2 图像变换

2.2.1 图像连接

图像连接是指将两个具有相同高度或者宽度的图像连接在一起。OpenCV4中提供了将多个Mat类型数据连接或直接连接两个Mat数据的vconcat()函数:

void cv::vconcat(const Mat *src,
                 size_t nsrc,
                 OutputArray dst);
  • src:Mat类型数组。
  • nsrc:数组中Mat类型数据的数目。
  • dst:连接后的Mat矩阵。
void cv::vconcat(InputArray src1,
                 InputArray src2,
                 OutputArray dst);
  • src1:待连接的第一个Mat矩阵。
  • src2:待连接的第二个Mat矩阵。
  • dst:连接后的Mat矩阵。

上述vconcat()的第二个原型用于竖向拼接,要求两张图像具有相同的宽度、数据类型和通道数。hconcat()则提供了左右拼接的方法:

void cv::hconcat(const Mat *src,
                 size_t nsrc,
                 OutputArray dst);
  • src:Mat类型数组。
  • nsrc:数组中Mat类型数据的数目。
  • dst:连接后的Mat矩阵。
void cv::hconcat(InputArray src1,
                 InputArray src2,
                 OutputArray dst);
  • src1:待连接的第一个Mat矩阵。
  • src2:待连接的第二个Mat矩阵。
  • dst:连接后的Mat矩阵。

hconcat()的用法vconcat()相似,其第二个原型要求两张图像具有相同的高度、数据类型和通道数。

2.2.2 尺寸变换

图像的尺寸变换即改变图像的长宽,实现图像的缩放。OpenCV4中提供了resize()方法实现:

void cv::resize(InputArray src,
                OutputArray dst,
                Size dsize,
                double fx = 0,
                double fy = 0,
                int interpolation = INTER_LINEAR);
  • src:输入图像。
  • dst:输出图像。
  • dsize:输出图像的尺寸。
  • fx:水平轴的比例因子,放大2倍则赋值2。
  • fy:垂直轴的比例因子,dsize和fx/fy结果不一致的时候,以dszie为准。
  • interpolation:插值方法标志,详见表2-3
表2-3 插值方法标志
interpolation简记作用
INTER_NEAREST0最近邻插值法
INTER_LINEAR1双线性插值法
INTER_CUBIC2双三次插值
INTER_AREA3使用像素区域关系重新采样,首选用于缩小,放大效果与0相似
INTER_LANCZOS44Lanczos插值法
INTER_LINEAR_EXACT5位精确双线性插值法
INTER_MAX7掩码插值

2.2.3 翻转变换

OpenCV4中提供了flip()函数用于实现图像的翻转:

void cv::flip(InputArray src,
              OutputArray dst,
              int flipCode);
  • src:输入图像。
  • dst:翻转后图像。
  • flipCode:翻转方式标志,大于0为绕y轴翻转,等于0为绕x轴翻转,小于0为绕两个轴翻转。

2.2.4 仿射变换

仿射变换是旋转、平移和缩放操作的统称,可以理解为线性变换和平移变换的叠加,即
[ x ′ y ′ ] = A [ x y ] + B (2-1) \begin{bmatrix} x' \\ y' \end{bmatrix} = \mathbf{A} \begin{bmatrix} x \\ y \end{bmatrix}+\mathbf{B} \tag{2-1} [xy]=A[xy]+B(2-1)
OpenCV4中提供的仿射变换函数为warpAffine()

void cv::warpAffine(InputArray src,
                    OutputArray dst,
                    InputArray M,
                    Size dsize,
                    int flags = INTER_LINEAR,
                    int borderMode = BORDER_CONSTANT,
                    const Scalar &borderValue = Scalar());
  • src:输入图像。
  • dst:仿射变换后图像。
  • M:2×3的变换矩阵。
  • dsize:输出图像的尺寸。
  • flags:插值方法标志,详见表2-4
  • borderMode:像素边界外推方法标志,详见表2-5
  • borderValue:填充边界使用的数值,默认为0。

其中,变换矩阵M与上述的线性变换矩阵A和平移向量B存在如下关系:
M = [ A B ] = [ a 00 a 01 b 00 a 10 a 11 b 10 ] (2-2) \mathbf{M} = \begin{bmatrix} \mathbf{A} & \mathbf{B} \\ \end{bmatrix} = \begin{bmatrix} a_{00} & a_{01} & b_{00}\\ a_{10} & a_{11} & b_{10}\end{bmatrix} \tag{2-2} M=[AB]=[a00a10a01a11b00b10](2-2)

表2-4 仿射变换插值方法标志
flags简记作用
WRAP_FILL_OUTLIERS8填充输出图像的所有像素,对落在边界的像素设定为fillval
WRAP_INVERSE_MAP16设置为M输出图像到输入图像的反变换
表2-5 边界外推方法标志
borderMode简记作用
BORDER_CONSTANT0特定值填充
BORDER_PEPLICATE1两端复制填充
BORDER_PEFLECT2倒序填充
BORDER_WRAP3正序填充
BORDER_REFLECT_1014不包含边界值的倒序填充
BORDER_TRANSPARENT5随机填充
BORDER_REFLECT1014同4
BORDER_DEFAULT4同4
BORDER_ISOLATED16不关心ROI区域以外的部分

2.2.5 透视变换

透视变换是按照物体成像投影规律进行变换,即将物体重新投影到新的成像平面。图像的透视变换可以用一个3×3的变换矩阵表示,该矩阵可以通过两幅图像中的4个对应点坐标求取,其函数原型为:

cv::Mat cv::getPerspectiveTransform(const Point2f src[],
                                    const Point2f dst[],
                                    int solveMethod = DECOMP_LU);
  • src[ ]:原图像4个点的坐标。
  • dst[ ]:透视变换后4个点的坐标。
  • solveMethod:计算透视变换矩阵方法的标志,详见表2-6
表2-6 透视变换矩阵计算方法标志
solveMethod简记作用
DECOMP_LU0最佳主轴元素的高斯消元法
DECOMP_SVD1奇异值分解法(SVD)
DECOMP_EIG2特征值分解法
DECOMP_CHOLESKY3Cholesky分解法
DECOMP_QR4QR分解法
DECOMP_NORMAL16使用正规方程公式,可以与其他标志一起使用

OpenCV4也提供了根据透视变换矩阵进行透视变换的方法:

void cv::warpPerspective(InputArray src,
                         OutputArray dst,
                         InputArray M,
                         Size dsize,
                         int flags = INTER_LINEAR,
                         int borderMode = BORDER_CONSTANT,
                         const Scalar &borderValue = Scalar());
  • src:输入图像。
  • dst:透视变换后图像。
  • M:3×3透视变换矩阵。
  • dsize:输出图像尺寸。
  • flags:插值方法标志,详见表2-3
  • borderMode:像素边界外推方法标志,详见表2-5
  • borderValue:填充边界使用的数值,默认0。

2.2.6 极坐标变换

极坐标变换即将图像在直角坐标系和极坐标坐标系中互相转换,常用于处理钟表、圆盘等图像(将圆形图像变换为矩形图像)。OpenCV4中提供的函数为:

void cv::warpPolar(InputArray src,
                   OutputArray dst,
                   Size dsize,
                   Point2f center,
                   double maxRadius,
                   int flags);
  • src:输入图像。
  • dst:极坐标变换后图像。
  • dsize:输出图像尺寸。
  • center:极坐标原点坐标。
  • maxRadius:变换时边界圆的半径,决定了逆变换时的比例参数。
  • flags:插值方法与极坐标映射方法标志,方法间用”|“连接,详见表2-7
表2-7 极坐标映射方法标志
flags作用
WARP_POLAR_LINEAR极坐标变换
WARP_POLAR_LOG半对数极坐标变换
WARP_INVERSE_MAP逆变换

2.2.7 ROI区域截取

在OpenCV4中,提供了两种截取ROI的方式,即分别使用Rect或Range数据结构进行标记。

cv::Mat img, ROI1, ROI2;

// 1.使用Rect截取ROI
ROI1 = img(Rect(x, y, width,height));   

// 2.使用Range截取ROI
ROI2 = img(Range(rows_start, rows_end), Range(cols_start, cols_end));  

然而,上述方法为浅拷贝。可以使用copyTo()方法实现深拷贝。

cv::Mat ROI1_copy;
img(Rect(x, y, width,height)).copyTo(ROI1_copy); 

2.3 图像绘制

2.3.1 圆形绘制

void cv::circle(InputOutputArray img,
                Point center,
                int radius,
                const Scalar &color,
                int thickness = 1,
                int lineType = LINE_8,
                int shift = 0);
  • img:待绘制的图像。
  • center:圆中心坐标。
  • radius:圆半径,单位像素。
  • color:圆形颜色。
  • thickness:轮廓宽度,若为负则绘制实心圆。
  • lineType:边界的线型,FILLED、LINE_4、LINE_8或者LINE_AA。
  • shift:中心坐标和半径数值中的小数位数。

2.3.2 直线绘制

void cv::line(InputOutputArray img,
              Point pt1,
              Point pt2,
              const Scalar &color,
              int thickness = 1,
              int lineType = LINE_8,
              int shift = 0);
  • img:待绘制的图像。
  • pt1:直线起点坐标。
  • pt2:直线终点坐标。
  • color:直线颜色。
  • thickness:直线宽度。
  • lineType:边界的线型,FILLED、LINE_4、LINE_8或者LINE_AA。
  • shift:坐标数值中的小数位数。

2.3.3 椭圆绘制

void cv::ellipse(InputOutputArray img,
                 Point center,
                 Size axes,
                 double angle,
                 double startAngle,
                 double endAngle,
                 const Scalar &color,
                 int thickness = 1,
                 int lineType = LINE_8,
                 int shift = 0);
  • img:待绘制的图像。
  • center:椭圆中心坐标。
  • axes:椭圆主轴半径。
  • angle:椭圆旋转角度,单位度。
  • startAngle:椭圆弧起始角度,单位度。
  • endAngle:椭圆弧终止转角度,单位度。
  • color:圆形颜色。
  • thickness:轮廓宽度,若为负则绘制实心椭圆。
  • lineType:边界的线型,FILLED、LINE_4、LINE_8或者LINE_AA。
  • shift:坐标等数值中的小数位数。

此外,OpenCV4中还提供了输出椭圆边界坐标的函数ellipse2Poly()(该函数无法绘制椭圆):

void cv::ellipse2Poly(Point center,
                      Size axes,
                      int angle,
                      int arcStart,
                      int arcEnd,
                      int delta,
                      std::vector<Point> &pta);
  • center:椭圆中心坐标。
  • axes:椭圆主轴半径。
  • angle:椭圆旋转角度,单位度。
  • arcStart:椭圆弧起始角度,单位度。
  • arcEnd:椭圆弧终止转角度,单位度。
  • delta:后续折线顶点之间的角度,定义了近似精度。
  • pts:椭圆边缘像素坐标向量集合。

2.3.4 多边形绘制

矩形绘制:

void cv::rectangle(InputOutputArray img,
                   Point pt1,
                   Point pt2,
                   const Scalar &color,
                   int thickness = 1,
                   int lineType = LINE_8,
                   int shift = 0);
void cv::rectangle(InputOutputArray img,
                   Rect rec,
                   const Scalar &color,
                   int thickness = 1,
                   int lineType = LINE_8,
                   int shift = 0);
  • img:待绘制的图像。
  • pt1:矩形一个顶点。
  • pt2:与第一个顶点相对的另一个顶点。
  • rec:矩形的左上角坐标和长宽。
  • color:矩形颜色。
  • thickness:轮廓宽度,若为负则绘制实心矩形。
  • lineType:边界的线型,FILLED、LINE_4、LINE_8或者LINE_AA。
  • shift:坐标数值中的小数位数。

任意多边形绘制:

void cv::fillPoly(InputOutputArray img,
                  const Point **pts,
                  const int *npts,
                  int ncontours,
                  const Scalar &color,
                  int thickness = 1,
                  int lineType = LINE_8,
                  int shift = 0
                  Point offset = Point());
  • img:待绘制的图像。
  • pts:多边形顶点数组,可以存放多个多边形的顶点坐标数组。
  • npts:每个多边形顶点数组中顶点的个数。
  • ncontours:绘制的多边形个数。
  • color:多边形颜色。
  • thickness:轮廓宽度,若为负则绘制实心多边形。
  • lineType:边界的线型,FILLED、LINE_4、LINE_8或者LINE_AA。
  • shift:坐标数值中的小数位数。
  • offset:所有顶点的可选偏移。

2.3.5 文字生成

void cv::putText(InputOutputArray img,
                 const String &text,
                 Point org,
                 int fontFace,
                 double fontScale,
                 Scalar color,
                 int thickness = 1,
                 int lineType = LINE_8,
                 int shift = 0,
                 bool bottomLeftOrigin = False);
  • img:待绘制的图像。
  • text:文字内容,仅支持英文。
  • org:文字字符左下角像素点坐标。
  • ffontFace:字体类型的选择标志。
  • fontScale:字体的大小。
  • color:字体颜色。
  • thickness:字体粗细。
  • lineType:边界的线型,FILLED、LINE_4、LINE_8或者LINE_AA。
  • shift:坐标数值中的小数位数。
  • bottomLeftOrigin:图像数据远点的位置,默认左上角,true则为左下角。

2.4 图像金字塔

2.4.1 图像下采样与高斯金字塔

构建高斯金字塔是解决尺度不确定性的常用方法。高斯金字塔是指通过下采样不断将图像尺寸缩小,进而构建包含多个尺寸图像的集合。OpenCV4中提供了pyrDown()方法用于图像的下采样。pyrDown()首先将src图像与内核矩阵卷积,之后通过不使用偶数行和列的方式进行下采样,即模糊+下采样。

void cv::pyrDown(InputArray src,
                 OutputArray dst,
                 const Size &dstsize = Size(),
                 int borderType = BORDER_DEFAULY);
  • src:输入图像。
  • dst:下采样后图像,数据类型和通道数与src相同。
  • dstSize:输出图像尺寸(默认一半)。
  • borderType:像素边界外推方法,详见表2-5

2.4.2 图像上采样与拉普拉斯金字塔

与高斯金字塔想反,拉普拉斯金字塔基于小尺寸的图像,构建下层大尺寸图像。通过联合高斯金字塔,可以实现预测残差的作用。OpenCV4中提供了pyrUp()方法实现上采样:

void cv::pyrUp(InputArray src,
               OutputArray dst,
               const Size &dstsize = Size(),
               int borderType = BORDER_DEFAULY);
  • src:输入图像。
  • dst:上采样后图像,数据类型和通道数与src相同。
  • dstSize:输出图像尺寸(默认一半)。
  • borderType:像素边界外推方法,详见表2-5

2.5 图像直方图

2.5.1 直方图统计

图像的直方图具有平移不变性和放缩不变性,可以用于分析图像亮暗对比度、灰度值集中范围等。OpenCV4中提供了能够统计灰度值数目的方法calcHist()

void cv::calcHist(const Mat *images,
                  int nimages,
                  const int *channels,
                  IntputArray mask,
                  OutputArray hist,
                  int dims,
                  const int *histSize,
                  const float **ranges,
                  bool uniform = true,
                  bool accumulate = false);
  • images:待统计直方图图像数组,所有图像应具有相同尺寸和数据格式(CV_8U、CV_16U、CV_32F),通道数可以不同。
  • nimages:输入图像的数量。
  • channels:需要统计的通道索引数组。
  • mask:可选的掩码,若为空矩阵则统计所有位置像素。
  • hist:输出的统计直方图,是一个dims维度的数组。
  • dims:需要计算直方图的维度。
  • histSize:存放每个维度直方图的数组数组的尺寸。
  • ranges:每个图像通道中灰度值的取值范围。
  • uniform:直方图是否均匀的标志。、
  • accumulate:是否累计统计直方图的标志,用于统计多张图像的整体直方图。

2.5.2 直方图归一化

若直接将统计的直方图数据绘制出来,往往出现某些灰度值的数目超过了图像的高度。因此,在绘制时需要采取一定的策略对其进行缩放以完整地将直方图绘制在图像中。OpenCV4中提供了多种形式实现归一化的函数normalize()

void cv::normalize(InputArray src,
                   InputOutputArray dst,
                   double alpha = 1,
                   double beta = 0,
                   int norm_type = NORM_L2,
                   int dtype = -1,
                   InputArray mask = noArray());
  • src:输入的数组矩阵。
  • dst:与src尺寸相同的数组矩阵。
  • alpha:在范围归一化的情况下,归一化到下限边界的标准值。
  • beta:范围归一化时的上限边界,不用于标准规范化。
  • norm_type:归一化方法标志,详见表2-8
  • dtype:输出数据类型的选择标志,负数时(默认)与输入相同,否则与输入通道数相同、数据类型不同。
  • mask:掩膜。
表2-8 归一化方法标志
norm_type简记作用原理公式
NORM_INF1无穷范数,向量最大值 ∣ s r c ∣ L ∞ = m a x l ∣ s r c ( I ) ∣ {\left |src\right |}_{L^\infty}={max_l} \vert {src(I)} \vert srcL=maxlsrc(I)
NORM_L12L1范数,绝对值之和 ∣ s r c ∣ L 1 = ∑ l ∣ s r c ( I ) ∣ {\left |src\right |}_{L1}=\sum_{l}{\vert {src(I)} \vert} srcL1=lsrc(I)
NORM_L24L2范数及模长归一化,平方和之根 ∣ s r c ∣ L 2 = ∑ l ∣ s r c ( I ) ∣ 2 {\left |src\right |}_{L2}=\sqrt{ \sum_{l}{\vert {src(I)} \vert}^2} srcL2=lsrc(I)2
NORM_L2SQR5L2范数平方 ∣ s r c ∣ L 1 2 = ∑ l s r c ( I ) 2 {\left |src\right |}_{L1}^{2}=\sum_{l}{{src(I)}^2} srcL12=lsrc(I)2
NORM_MINMAX32偏移归一化

Tips2-4:normalize( )注意点

  1. norm_type参数决定了输入矩阵中每个数据要除以的数值,直接决定了直方图缩放的结果。
  2. NORM_L1输出结果为每个灰度值所占的比例。
  3. NORM_INF输出结果为除以数据中的最大值,将所有数据归一化到0~1。
  4. 无论是否归一化,或采取何种归一化方法,直方图的分布特性都不会改变。

2.5.3 直方图比较

由于直方图能表示图像灰度值的分布特性,因此可以通过对比两幅图像的直方图分布比较其相似程度。尽管图象在缩放等操作后直方图分布会有变化,但仍可以利用其较高的相似性进行初筛和识别。OpenCV4中提供的相关函数为compareHist()

void cv::compareHist(InputArray H1,
                     InputArray H2
                     int method);
  • H1:第一幅图像的直方图。
  • H2:第二幅图像的直方图。
  • method:比较方法标志,详见表2-9
表2-9 直方图比较方法标志
method简记作用
HISTCMP_CORREL0相关法
HISTCMP_CHISQR1卡方法
HISTCMP_INTERSECT2直方图相交法
HISTCMP_BHATTACHARYYA3巴氏距离法
HISTCMP_HELLINGER3同上
HISTCMP_CHISQR_ALT4替代卡方法
HISTCMP_KL_DIV5相对熵法(K-L散度法)

相关方法的原理公式如下:
HISTCMP_CORREL(0):
d ( H 1 , H 2 ) = ∑ I ( H 1 ( I ) − H ‾ 1 ) ( H 2 ( I ) − H ‾ 2 ) ∑ I ( H 1 ( I ) − H ‾ 1 ) 2 ∑ I ( H 2 ( I ) − H ‾ 2 ) 2 (2-3) d(H_1,H_2)=\frac{\sum_{I}{(H_1(I)-\overline H_1)(H_2(I)-\overline H_2)}}{\sqrt{\sum_{I}{{(H_1(I)-\overline H_1)}^2}\sum_{I}{{(H_2(I)-\overline H_2)}^2}}} \tag{2-3} d(H1,H2)=I(H1(I)H1)2I(H2(I)H2)2 I(H1(I)H1)(H2(I)H2)(2-3)
H ‾ k = 1 N ∑ J H k ( J ) (2-4) \overline H_k=\frac{1}{N}{\sum_{J}{H_k(J)}} \tag{2-4} Hk=N1JHk(J)(2-4)
HISTCMP_CHISQR (1):
d ( H 1 , H 2 ) = ∑ I ( H 1 ( I ) − H 2 ( I ) ) 2 H 1 ( I ) (2-5) d(H_1,H_2)=\sum_{I}{\frac{{(H_1(I)-H_2(I))}^2}{H_1(I)}} \tag{2-5} d(H1,H2)=IH1(I)(H1(I)H2(I))2(2-5)
HISTCMP_INTERSECT(2):
d ( H 1 , H 2 ) = ∑ I m i n ( H 1 ( I ) , H 2 ( I ) ) (2-6) d(H_1,H_2)=\sum_{I}{min(H_1(I),H_2(I))} \tag{2-6} d(H1,H2)=Imin(H1(I),H2(I))(2-6)
HISTCMP_BHATTACHARYYA & HISTCMP_HELLINGER(3) :
d ( H 1 , H 2 ) = 1 − 1 H 1 H 2 N 2 ∑ I H 1 ( I ) × H 2 ( I ) (2-7) d(H_1,H_2)=\sqrt{1-\frac{1}{\sqrt{H_1H_2N^2}}\sum_{I}{\sqrt{H_1(I)×H_2(I)}}} \tag{2-7} d(H1,H2)=1H1H2N2 1IH1(I)×H2(I) (2-7)
HISTCMP_CHISQR_ALT(4):
d ( H 1 , H 2 ) = 2 ∑ I ( H 1 ( I ) − H 2 ( I ) ) 2 H 1 ( I ) + H 2 ( I ) (2-8) d(H_1,H_2)=2\sum_{I}{\frac{{(H_1(I)-H_2(I))}^2}{H_1(I)+H_2(I)}} \tag{2-8} d(H1,H2)=2IH1(I)+H2(I)(H1(I)H2(I))2(2-8)
HISTCMP_KL_DIV(5):
d ( H 1 , H 2 ) = ∑ I H 1 ( I ) l n ( H 1 ( I ) H 2 ( I ) ) (2-9) d(H_1,H_2)=\sum_{I}{H_1(I)ln \left ( \frac {H_1(I)}{H_2(I)} \right ) } \tag{2-9} d(H1,H2)=IH1(I)ln(H2(I)H1(I))(2-9)

2.5.4 直方图均衡化

直方图均衡化的目的在于将原本灰度值分布较为集中的图像通过映射关系将灰度值分布范围扩大,进而增加对比度,突出图像纹理。OpenCV4中提供的函数为equalizeHist()

void cv::equalizeHist(InputArray src,
                      OutputArray dst);
  • src:输入图像,仅支持单通道的灰度图像。
  • dst:直方图均衡化后图像。

2.5.5 直方图匹配

直方图均衡化可以极为方便的改变图像的直方图分布,但不能指定输出图像的直方图分布形式。将输入图像直方图映射为指定形式算法称为直方图匹配或直方图规定化。在理想状态下,经过直方图匹配后图像的直方图分布形式与目标分布一致,即两者灰度值的累计概率分布一致,用数学公式表达这种从原灰度n映射为r的关系为:
n , r = a r g   m i n n , r ∣ V s ( n ) − V z ( r ) ∣ (2-10) n,r={arg}\ {min}_{n,r}|V_s(n)-V_z(r)| \tag{2-10} n,r=arg minn,rVs(n)Vz(r)(2-10)
式中, V s ( n ) V_s(n) Vs(n)为原图像各灰度级累计概率, V z ( r ) V_z(r) Vz(r)为匹配直方图后各灰度级累计概率。

OpenCV4中未给出相关方法,需要自行实现,示例如下:

using namespace cv;
using namespace std;

Mat src1 = imread("src1.png");
Mat src2 = imread("src2.png");
Mat hist1, hist2;  ///< 输出的直方图
const int channels[1] = {0};  ///< 通道数索引
const int bins[1] = {256};  ///< 每个维度直方图的数组数组的尺寸
float inRange[2] = {0,255};
const float *ranges[1] = {inRanges};  ///< 每个图像通道中灰度值的取值范围

calcHist(&src1, 1, channels, Mat(), hist1, 1, bins, ranges);  ///< 获取src1直方图
calcHist(&src2, 1, channels, Mat(), hist2, 1, bins, ranges);  ///< 获取src2直方图

normalize(hist1, hist1, 1, 0, NORM_L1, -1, Mat());  ///< src1直方图归一化
normalize(hist2, hist2, 1, 0, NORM_L1, -1, Mat());  ///< src2直方图归一化

float hist1_cdf[256] = {hist1.at<float>(0)};
float hist2_cdf[256] = {hist2.at<float>(0)};
for(int i = 0; i < 256; i++)  ///< 计算src1和src2的灰度值累计概率
{
    hist1_cdf[i] = hist1_cdf[i-1] + hist1_cdf.at<float>(i);
    hist2_cdf[i] = hist2_cdf[i-1] + hist2_cdf.at<float>(i);
}

float diff_cdf[256][256]
for(int i = 0; i < 256; i++)  ///< 构建累积误差矩阵
{
    for(int j = 0; j < 256; j++)
    {
        diff_cdf[i][j] = fabs(hist1_cdf[i] - hist2_cdf[j]);
    }
}

Mat lut(1, 256, CV_8U);
for(int i = 0; i < 256; i++)  ///< 生成LUT(映射表)
{
    float min = diff_cdf[i][0];
    int index = 0;
    for(int j = 1; j < 256; j++)
    {
        min  = diff_cdf[i][j];
        index = j;
    }
    lut.at<uchar>(i) = (uchar)index;
}

Mat result;
LUT(src1, lut, result);  ///< 根据映射表进行直方图匹配
imshow("待匹配图像", src1);
imshow("模板图像", src2);
imshow("直方图匹配结果", result);

2.5.6 直方图反向投影

所谓反向投影,即计算某一特征(结构纹理、形状等)的直方图模型,然后使用该模型去图像中寻找是否存在该特征的方法。OpenCV4中提供了calcBackProject()方法用于对图形进行直方图反向投影:

void cv::calcBackProject(const Mat *images,
                         int nimages,
                         const int *channels,
                         InputArray hist,
                         OutputArray backProject,
                         const float **ranges,
                         double scale = 1,
                         bool uniform = true);
  • images:待统计直方图的图像数组。
  • nimages:输入图像数量。
  • channels:需要统计的通道索引数组。
  • hist:输入直方图。
  • backProject:目标为反向投影图像,与输入图像具有相投尺寸和数据类型的单通道图像。
  • ranges:每个图像通道中灰度值的取值范围。
  • scale:输出反向投影矩阵的比例因子。
  • uniform:直方图是否均衡的标志。

直方图并不能直接反映图像的纹理特征,若不同的模板具有相同的直方图分布特性,则通过反向投影寻找模板将不再有用。实际上,更为常用和有效的是模板匹配(即通过直接比较图像中像素的形式寻找是否存在相同的内容),这一部分在将下一篇中介绍。

2.6 图像滤波

图像滤波是指去除图像中不重要的内容,从而使关心的内容表现得更加清晰的方法,例如去除噪声、提取某些信息等。由于噪声信号在图像中主要集中在高频段,因此图像去噪可以看作去除图像中高频信号的同时保留图像低频段和中频段信号的滤波操作。因此,通常使用低通滤波器或高阻滤波器去除噪声,使用高通滤波器实现图像边缘信息提取、增强和图像锐化。

2.6.1 图像卷积

图像卷积的过程可以看作是一个卷积模板在图像上移动,并对卷积模板覆盖的区域进行点乘,得到的值作为中心像素点的输出值。卷积首先要将模板逆时针旋转180°,之后从图像的左上角按照从左到右、从上到下的顺序进行卷积运算。OpenCV4中提供了filter2D()函数用于实现卷积运算。

void cv::filter2D(InputArray src,
                  OutputArray dst,
                  int ddepth,
                  InputArray kernel,
                  Point anchor = Point(-1,-1),
                  double delta = 0,
                  int borderType = BORDER_DEFAULT);
  • src:输入图像。
  • dst:卷积后图像,与输入图像有相同尺寸和通道数。
  • ddepth:输出图像的数据类型(深度),值为-1时自动选择。
  • kernel:卷积核,CV_32FC1矩阵。
  • anchor:卷积核的基准点(锚点),默认值(-1,-1)代表其位于核的中心。
  • delta:在计算结果基础上加上的偏值。
  • border:像素外推方法,默认为不包含边界值倒序填充。

Tips2-5:filter2D( )注意点

  1. 卷积核的尺寸一般为奇数。
  2. filter2D()方法并不会将卷积模板进行旋转,对于非对称模板,需要先旋转卷积核再进行卷积。
  3. 在使用时,为了防止卷积后的值超出范围,一般需要先对卷积核进行归一化操作。

2.6.2 线性滤波

(1)均值滤波

K = 1 k s i z e . w i d t h × k s i z e . h e i g h t [ 1 1 … 1 1 1 … 1 ⋮ ⋮ ⋱ ⋮ 1 1 . . . 1 ] (2-11) K=\frac{1}{ksize.width×ksize.height}\begin{bmatrix} 1 & 1 & \dots & 1\\ 1 & 1 & \dots & 1\\ \vdots &\vdots & \ddots &\vdots \\ 1 & 1& ...&1\end{bmatrix} \tag{2-11} K=ksize.width×ksize.height1 111111...111 (2-11)

void cv::blur(InputArray src,
              OutputArray dst,
              Size ksize,
              Point anchor = Point(-1,-1),
              int borderType = BORDER_DEFAULT);
  • src:输入图像。
  • dst:均值滤波后图像。
  • ksize:卷积核尺寸。
  • anchor:卷积核基准点。
  • borderType:像素外推方法。
(2)方框滤波

方框滤波为均值滤波的一般形式。方框滤波可以选择不进行归一化,将所有像素值的和作为滤波结果。
K = [ 1 1 … 1 1 1 … 1 ⋮ ⋮ ⋱ ⋮ 1 1 . . . 1 ] (2-12) K=\begin{bmatrix} 1 & 1 & \dots & 1\\ 1 & 1 & \dots & 1\\ \vdots &\vdots & \ddots &\vdots \\ 1 & 1& ...&1\end{bmatrix} \tag{2-12} K= 111111...111 (2-12)

void cv::boxFilter(InputArray src,
                   OutputArray dst,
                   int ddepth,
                   Size ksize,
                   Point anchor = Point(-1,-1),
                   bool normalize = true,
                   int borderType = BORDER_DEFAULT);
  • src:输入图像。
  • dst:方框滤波后图像。
  • ddepth:输出图像的数据类型(深度)。
  • ksize:卷积核尺寸。
  • anchor:卷积核基准点。
  • normalize:是否将卷积核进行归一化的标志。
  • borderType:像素外推方法。

Tips2-6:boxFilter( )注意点

在不考虑数据类型的情况下,boxFilter()和blur()在默认参数下具有相同的滤波结果。

(3) 高斯滤波

高斯滤波器考虑了像素距离滤波器中心距离的影响,以滤波器中心位置为高斯分布的均值,根据高斯分布和每个像素距离中心位置的距离计算出滤波器内每个位置的数值。OpenCV4提供了对图像进行高斯滤波的GaussianBlur()函数,该函数能根据输入参数自动生成高斯滤波器,实现对图像的高斯滤波。

void cv::GaussianBlur(InputArray src,
                      OutputArray dst,
                      Size ksize,
                      double sigmaX,
                      double sigmaY = 0,                      
                      int borderType = BORDER_DEFAULT);
  • src:输入图像。
  • dst:高斯滤波后图像。
  • ksize:卷积核尺寸。当输入量为0时,将根据输入的标准偏差计算尺寸。
  • sigmaX:x轴方向的高斯滤波器标准偏差。
  • sigmaY:y轴方向的高斯滤波器标准偏差。若输入量为0,则将其设置为等于SigmaX。
  • borderType:像素外推方法。

高斯滤波器的尺寸和标准偏差存在一定的互相转换关系,OpenCV4提供了根据滤波器单一方向尺寸和标准偏差生成单一方向高斯滤波器的getGaussianKernel()函数。

Mat cv::getGaussianKernel(int ksize,
                          double sigma,
                          int ktype = CV_64F);
  • ksize:高斯滤波器尺寸。
  • sigma:高斯滤波器标准差。
  • ktype:滤波器系数的数据类型(CV_32F或CV_64F)。
(4) 可分离滤波

图像滤波具有并行性,即在原始图像上移动滤波器的过程中每一次的计算结果都不会影响到后面过程的计算结果。此外,图像滤波还具有可分离性,即顺序对X/Y和Y/X方向滤波的结果与将两个方向的滤波器联合后整体滤波的结果相同。OpenCV4提供了可以输入两个方向滤波器实现滤波的函数sepFilter2D()

void cv::seqFilter2D(InputArray src,
                     OutputArray dst,
                     int ddepth,
                     InputArray kernelX,
                     InputArray kernelY,
                     Point anchor = Point(-1,-1),
                     double delta = 0,
                     int borderType = BORDER_DEFAULT);
  • src:输入图像。
  • dst:滤波后图像。
  • ddepth:输出图像的数据类型(深度),输入为-1时,自动选择。
  • kernelX:x方向的滤波器。
  • kernelY:y方向的滤波器。
  • anchor:卷积核基准点。
  • delta:在计算结果基础上加上的偏值。
  • borderType:像素外推方法。

Tips2-7:sepFilter2D( )与filter2D( )的区别

  1. filter2D()需要通过滤波器的尺寸区分滤波操作是作用在X方向还是Y方向,例如Kx1时为Y方向,1xK时为X方向。
  2. sepFilter2D()通过不同参数区分方向,与滤波器输入尺寸无关。

2.6.3 非线性滤波

所谓非线性滤波即其计算过程包含了排序、逻辑计算等非线性组合计算过程。由于线性滤波是对所有像素值的线性组合计算得到滤波结果,因此噪声不会被消除,而是以更柔和的形式存在。此时,使用非线性滤波效果可能更好。

(1)中值滤波

中值滤波即用滤波器覆盖范围内所有像素值的中值替代中心像素点的滤波方法,是一种基于排序统计理论的非线性信号处理方法,对斑点噪声和椒盐噪声的处理有较好的效果。此外,中值滤波对图像的边缘信息保护效果更佳,可以避免图像细节的模糊。当然,随着滤波尺寸的增大,中值滤波也会产生图像模糊的效果。处理时间上,中值滤波耗时也更多。OpenCV4提供了对图像进行中值滤波的medianBlur()函数。

void cv::medianBlur(InputArray src,
                    OutputArray dst,
                    int ksize);
  • src:输入图像。
  • dst:滤波后图像。
  • ksize:滤波器尺寸,必须为大于1的奇数。
(2)双边滤波

双边滤波一种经典的能够保留图像边缘信息的滤波算法之一。双边滤波作为一种综合考虑图像空域和灰度值相似性信息滤波器,能平滑高频率的波动信号,同时保留大幅度的信号波动,进而实现保留图像边缘信息的同时去除噪声。双边滤波器的数学原理如下式所示:
g ( i , j ) = ∑ k , l f ( k , l ) ω ( i , j , k , l ) ∑ k , l ω ( i , j , k , l ) (2-13) g(i,j)=\frac{\sum_{k,l}^{}f(k,l)\omega(i,j,k,l)}{\sum_{k,l}^{}\omega(i,j,k,l)} \tag{2-13} g(i,j)=k,lω(i,j,k,l)k,lf(k,l)ω(i,j,k,l)(2-13)
式中, ω ( i , j , k , l ) \omega(i,j,k,l) ω(i,j,k,l)为加权系数,取决于空域滤波器 d ( i , j , k , l ) d(i,j,k,l) d(i,j,k,l)和值域滤波器 r ( i , j , k , l ) r(i,j,k,l) r(i,j,k,l)的乘积。
d ( i , j , k , l ) = e x p ( − ( i − k ) 2 + ( j − l ) 2 2 σ d 2 ) (2-14) d(i,j,k,l)=exp\left(-\frac{(i-k)^2+(j-l)^2}{2\sigma_{d}^{2}}\right) \tag{2-14} d(i,j,k,l)=exp(2σd2(ik)2+(jl)2)(2-14)
r ( i , j , k , l ) = e x p ( − ∥ f ( i , j ) − f ( k , l ) ∥ 2 2 σ r 2 ) (2-15) r(i,j,k,l)=exp\left(-\frac{\left\|f(i,j)-f(k,l)\right\|^2}{2\sigma_{r}^{2}}\right) \tag{2-15} r(i,j,k,l)=exp(2σr2f(i,j)f(k,l)2)(2-15)
即:
w ( i , j , k , l ) = e x p ( − ( i − k ) 2 + ( j − l ) 2 2 σ d 2 − ∥ f ( i , j ) − f ( k , l ) ∥ 2 2 σ r 2 ) (2-16) w(i,j,k,l)=exp\left(-\frac{(i-k)^2+(j-l)^2}{2\sigma_{d}^{2}}-\frac{\left\|f(i,j)-f(k,l)\right\|^2}{2\sigma_{r}^{2}}\right) \tag{2-16} w(i,j,k,l)=exp(2σd2(ik)2+(jl)22σr2f(i,j)f(k,l)2)(2-16)
OpenCV4提供了进行双边滤波操作的bilateralFilter()函数:

void cv::bilateralFilter(InputArray src,
                         OutputArray dst,
                         int d,
                         double sigmaColor,
                         double sigmaSpace,
                         int borderType = BORDER_DEFAULT);
  • src:输入图像。
  • dst:滤波后图像。
  • d:滤波时每个像素邻域的直径,如果为非正数则由第五个参数sigmaSpace计算得到,建议设为5。
  • sigmaColor:颜色空间滤波器的标准差值,值越大,该像素领域内更多颜色被混合到一起,一般与下一个参数设相同的值。
  • sigmaSpace:空间坐标中滤波器的标准差值,值越大,越远的像素间会相互影响。d大于0时邻域范围由d决定,d小于等于0时邻域范围正比于这个参数。
  • borderType:像素外推方法,详见表2-5

2.7 形态学操作

图像形态学用具有一定形态的结构元素度量和提取图像中的对应形状,以达到对图像分析和识别的目的,主要包括腐蚀、膨胀、开运算、闭运算等。

2.7.1 像素距离

(1)欧氏距离

两个像素简单直线距离,计算公式为:
d = ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 (2-17) d=\sqrt{(x_1-x_2)^2+(y_1-y_2)^2} \tag{2-17} d=(x1x2)2+(y1y2)2 (2-17)

(2)街区距离

两个像素在X方向和Y方向的距离之和,计算公式为:
d = ∣ x 1 − x 2 ∣ + ∣ y 1 − y 2 ∣ (2-18) d=\left |x_1-x_2\right |+\left |y_1-y_2\right | \tag{2-18} d=x1x2+y1y2(2-18)

(3)棋盘距离

两个像素在X方向和Y方向距离的最大值,计算公式为
d = m a x ( ∣ x 1 − x 2 ∣ , ∣ y 1 − y 2 ∣ ) (2-19) d=max\left ( \left |x_1-x_2\right |,\left |y_1-y_2\right |\right ) \tag{2-19} d=max(x1x2,y1y2)(2-19)
OpenCV4提供了计算不同距离的distanceTransform()函数:

void cv::distanceTransform(InputArry src,
                           OutputArry dst,
                           OutputArry labels,
                           int distanceType,
                           int maskSize,
                           int labelType = DIST_LABEL_CCOMP);
  • src:输入图像。
  • dst:输出图像。
  • labels:二位标签数组(原始图像的离散Voronoi图),CV_32S单通道类型。
  • distanceType:距离计算方法标志,详见表2-10
  • maskSize:距离变换掩码矩阵标志,DIST_MASK_3(3×3)或者DIST_MASK_5(5×5)。
  • labelType:要构建的标签数组的类型,详见表2-11
表2-10 像素距离计算方法标志
distanceType简记作用
DIST_USER-1自定义距离
DIST_L11街区距离
DIST_L22欧式距离
DIST_C3棋盘距离
表2-11 标签数组类型标志
labelType简记作用
DIST_LABEL_CCOMP0输入图像中每个0像素(及其相邻的非零元素)都将被分配为同一个标签
DIST_LABEL_PIXEL1输入图像中每个0像素(及其相邻的非零元素)都有自己的标签

上述函数原型会生成离散的Voronoi图,占用内存资源,若不需要可以使用以下这种原型:

void cv::distanceTransform(InputArry src,
                           OutputArry dst,
                           int distanceType,
                           int maskSize,
                           int dstType = CV_32F);
  • src:输入图像。
  • dst:输出图像。
  • distanceType:距离计算方法标志,同上。
  • maskSize:距离变换掩码矩阵尺寸,DIST_MASK_3(3×3)或者DIST_MASK_5(5×5)。
  • dstType:输出图像数据类型,CV_8U或者CV_32F。

Tips2-8:distanceType( )注意点

  1. distanceType()的输出图像中每个像素值表示该像素在原始图像中距离0像素的最小距离。
  2. 由于距离可能超过255,因此输出类型可以选择为CV_8U或者CV_32F。
  3. 选择欧氏距离时,maskSize建议5×5,选用3×3时智能粗略计算结果。
  4. 选择街区距离时,maskSize尺寸对结果没有影响,默认选择3×3以加快速度。
  5. 选择棋盘距离时,maskSize尺寸对结果没有影响,随意选择。

2.7.2 图像连通域

图像连通域是指图像中具有相同像素值并且位置相邻的像素组成的区域。为了防止像素值波动对连通域检测结果的影响,一般处理的二值化后的图像。

2.7.3 腐蚀与膨胀

(1)结构元素

与图像的卷积计算相似,腐蚀和膨胀也需要一种模板矩阵控制计算结果,称其为结构元素。

结构元素可以根据需求自己生成,OpenCV4也提供了常用的几种结构元素:

cv::Mat cv::getStructuringElement(int shape,
                                  Size ksize,
                                  Point anchor = Point(-1,-1));
  • shape:结构元素种类,详见表2-12
  • ksize:结构元素尺寸。
  • anchor:中心点位置,默认几何中心点。
表2-12 结构元素类型标志
shape简记作用
MORPH_RECT0矩形结构元素,全为1
MORPH_CROSS1十字结构元素
MORPH_ELLIPSE2椭圆结构元素
(2)腐蚀

将结构元素绕中心点旋转180°后,依次将中心点放到每一个非零元素处,若此时结构元素覆盖的每个像素均不为0则保留当前中心点的元素,否则删除,数学公式表达为:
A Θ B = { z ∣ ( B ) z ⊂ A } (2-20) A\Theta B=\left \{z|(B)_z \subset A\right \} \tag{2-20} AΘB={z(B)zA}(2-20)

void cv::erode(InputArry src,
               OutputArry dst,
               InputArry kernel,
               Point anchor = Point(-1,-1),
               int iterations = 1,
               int borderType = BORDER_CONSTANT,
               const Scalar &borderValue = morphologyDefaultBorderValue());
  • src:输入图像。
  • dst:腐蚀后图像。
  • kernel:结构元素。
  • anchor:结构元素中心点位置。
  • iterations:腐蚀的次数。
  • borderType:像素外推方法,默认为不包含边界值的倒序填充。
  • borderValue:使用边界不变外推法时的边界值。
(3)膨胀

相比于腐蚀,膨胀是相反的操作。将结构元素绕中心点旋转180°后,依次将中心点放到每一个非零元素处,若此时结构元素覆盖的某个元素像素值与中心元素像素值不相等,则将该元素的像素值修改为中心点像素值,数学公式表达为:
A ⊕ B = { z ∣ ( B ) z ∩ A ≠ ϕ } (2-21) A\oplus B=\left \{z|(B)_z \cap A \ne \phi \right \} \tag{2-21} AB={z(B)zA=ϕ}(2-21)

void cv::dilate(InputArry src,
                OutputArry dst,
                InputArry kernel,
                Point anchor = Point(-1,-1),
                int iterations = 1,
                int borderType = BORDER_CONSTANT,
                const Scalar &borderValue = morphologyDefaultBorderValue());
  • src:输入图像。
  • dst:膨胀后图像。
  • kernel:结构元素。
  • anchor:结构元素中心点位置。
  • iterations:膨胀的次数。
  • borderType:像素外推方法,默认为不包含边界值的倒序填充。
  • borderValue:使用边界不变外推法时的边界值。

Tips2-9:腐蚀与膨胀

  1. 腐蚀与膨胀的过程只针对非零元素,图像以0为背景和255为背景的效果相反。
  2. 腐蚀和膨胀对每个通道独立进行计算。
  3. 腐蚀可以将细小的噪声区域去除,但会导致主要区域面积缩小。
  4. 膨胀可以填充的较小的空洞,但会增加噪声的面积。

2.7.4 形态学应用

(1)开运算

首先对图像进行腐蚀,消除图像中的噪声和较小的连通域,之后在进行膨胀弥补较大连通域因腐蚀造成的面积减小。

(2)闭运算

首先对图像进行膨胀,填充连通域内的小型空洞,扩大连通域边界,之后再进行腐蚀减少由膨胀造成的面积增大。

(3)形态学梯度

形态学梯度可以分为基本梯度、内部梯度和外部梯度。基本梯度是原图像膨胀后的图像与腐蚀后的图像之差,内部梯度是原图像和腐蚀后图像之差,外部梯度是膨胀后图像和原图像之差。下面的morphologyEx()提供的是基本梯度,另两种需要自行实现。

(4)顶帽运算

顶帽运算是原图像与开运算之差,用来分离比邻近点亮一些的斑块。

(5)黑帽运算

黑帽运算是原图像与闭运算之差,用来分离比邻近点暗一些的斑块。

(6)击中击不中变换

击中击不中变换时比腐蚀操作更严格的一种形态学操作。腐蚀进要求图像能够将结构元素中的所有非零元素包含,击中击不中变换需要原图像中存在与结构元素一模一样的结构才保留,即结构元素中的零元素也需要同时考虑。

OpenCV4提供了图像腐蚀和膨胀运算不同组合形式的morphologyEx()函数实现上述算法:

void cv::morphologyEx(InputArray src,
                      OutputArray dst,
                      int op,
                      InputArray kernel,
                      Point anchor = Point(-1,-1),
                      int iterations = 1,
                      int borderType = BORDER_CONSTANT,
                      const Scalar &borderValue = morphologyDefaultBorderValue());
  • src:输入图像。
  • dst:形态学操作后图像。
  • op:形态学操作类型标志,详见表2-13
  • kernel:结构元素。
  • anchor:结构元素中心点位置。
  • iterations:膨胀的次数。
  • borderType:像素外推方法,默认为不包含边界值的倒序填充。
  • borderValue:使用边界不变外推法时的边界值。
表2-13 形态学操作类型标志
op简记作用
MORPH_ERODE0腐蚀
MORPH_DILATE1膨胀
MORPH_OPEN2开运算
MORPH_CLOSE3闭运算
MORPH_GRANDIENT4形态学梯度
MORPH_TOPHAT5顶帽运算
MORPH_BLACKHAT6黑帽运算
MORPH_HITMISS7击中击不中运算
(7)图像细化

图像细化(又称“骨架化”、“中轴变换”),是模式识别领域重要处理步骤之一,能将图像的线条的多像素减少到像素宽度,主要应用在线条状物体上(圆环、文字等)。根据处理步骤的不同,细化算法可以分为迭代细化算法和非迭代细化算法。其中,迭代细化算法根据检测像素的方法还可以分为串行细化和并行细化(Zhang细化方法)。

OpenCV4提供了将二值图像细化的thinning()函数:

void cv::ximgproc::thinning(InputArray src,
                            OutputArray dst,
                            int thinningType = THINNING_ZHANGSUEN);
  • src:输入图像。
  • dst:细化后图像。
  • op:细化算法类型标志,THINNING_ZHANGSUEN(简记0,Zhang细化方法),THINNING_GUOHALL(简记1,Guo细化方法)。

2.8 函数列表

序号函数说明
1minMaxLoc( )寻找矩阵/图像中最大/最小值及其位置
2reshape( )改变矩阵/图像尺寸与通道数
3mean( )计算矩阵/图像每个通道的均值
4meanStdDev( )计算矩阵/图像每个通道的均值和方差
5max( )比较图像每个像素灰度值的较大值
6min( )比较图像每个像素灰度值的较小值
7bitwise_and( )像素“与”运算
8bitwaise_or( )像素“或”运算
9bitwise_xor( )像素“异或”运算
10bitwise_not( )像素“非”运算
11threshold( )像素阈值操作
12adaptiveThreshold( )图像自适应二值化
13LUT( )根据映射表改变灰度
14vconcat( )图像竖向拼接
15hconcat( )图像横向拼接
16resize( )改变图像尺寸
17flip( )图像翻转变换
18warpAffine( )图像仿射变换
19getPerspectiveTransform( )获取透视矩阵
20warpPerspective( )图像透视变换
21warpPolar( )图像极坐标变换
22circle( )圆形绘制
23line( )直线绘制
24ellipse( )椭圆绘制
25ellipse2Poly( )获取椭圆边界坐标
26rectangle( )矩形绘制
27fillPoly( )多边形绘制
28putText( )文字生成
29pyrDown( )图像下采样
30pyrUp( )图像上采样
31calcHist( )计算图像直方图
32nolmalize( )直方图归一化
33compareHist( )直方图比较
34equalizeHist( )直方图均衡化
35calcBackProject( )直方图反向投影
36filter2D( )图像卷积
37blur( )均值滤波
38boxFilter( )方框滤波
39GaussianBlur( )高斯滤波
40getGaussianKernel( )获取高斯核
41sepFilter2D( )双方向卷积运算
42medianBlur( )中值滤波
43bilateralFilter( )双边滤波
44distanceTransform( )像素距离变换
45connectedComponents( )连通域计算
46connectedComponentsWithStats( )含有更多信息的连通域计算
47getStructuringElement( )获取形态学操作结构元素
48erode( )腐蚀运算
49dilate( )膨胀运算
50morphologyEx( )形态学操作
51thinning( )图像细化
  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
opencv——基于python语言实现》pdf 是一本介绍了使用Python语言实现OpenCV库的编程技术的电子书。 OpenCV 是一个广泛应用于计算机视觉领域的开源库,它提供了许多用于图像处理和计算机视觉的函数和算法。 该电子书首先介绍了OpenCV和Python的基本概念和背景知识,如图像处理的基本操作图像编程的基本原理。它还详细介绍了OpenCV库中的各种图像处理算法和函数,并提供了许多示例代码和案例研究,以帮助读者理解和应用这些算法和函数。 该电子书还涵盖了如何使用Python与OpenCV进行人脸检测、目标跟踪、图像识别和图像分割等常见任务。它还介绍了如何利用OpenCV和Python进行视频处理和实时图像处理,并展示了如何与摄像头和外部设备进行交互。 这本书对于希望学习和应用OpenCV和Python进行图像处理和计算机视觉的人来说是非常有价值的。它详细讲解了OpenCV库中的各种函数和算法的使用方法,并提供了丰富的示例和案例研究。读者可以通过学习该书,深入了解OpenCV和Python的使用,并掌握图像处理和计算机视觉的基本原理和技术,从而能够应用于实际项目和应用中。 总之,《opencv——基于python语言实现》pdf 是一本适合初学者和有一定基础的人学习和应用OpenCV和Python进行图像处理和计算机视觉的电子书。通过阅读该书,读者可以全面了解OpenCV库的使用方法和基本原理,并掌握使用Python语言进行图像处理和计算机视觉任务的技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值