之前提到的图像变换都是将原图的某个像素点转换到目标图像的另一个位置,其结果本质上仍然是一幅图像。这里提到的图像分析则是将图像转换为另一种完全不同的形式,有可能是数组甚至是向量,比如离散傅立叶变换和 Hough 直线变换。最后将给出图像分割方法。
对于 N 个离散的点,一维离散傅立叶变换
而二维离散傅立叶变换
使用传统方法将需要 ,而如果使用快速傅立叶变换,复杂度将下降为
cv::dft(): The Discrete Fourier Transform
void cv::dft(
cv::InputArray src, // Input array (real or complex)
cv::OutputArray dst, // Output array
int flags = 0, // for inverse, or other options
int nonzeroRows = 0 // number of rows to not ignore
);
参数说明:
单通道CCS的输出如下
cv::idft(): The Inverse Discrete Fourier Transform
虽然 cv::dft() 可以实现傅立叶反变换,但是为了代码的可读性,还是建议使用独立的函数。
void cv::idft(
cv::InputArray src, // Input array (real or complex)
cv::OutputArray dst, // Output array
int flags = 0, // for variations
int nonzeroRows = 0 // number of rows to not ignore
);
其中参数与 cv::dft() 类似,其中如果 flags 设置为 cv::DFT_INVERSE,该函数将执行傅立叶变换
cv::mulSpectrums(): Spectrum Multiplication
其实现从 CCS 储存的数据中得到通常使用的数据
void cv::mulSpectrums(
cv::InputArray src1, // Input array (ccs or complex)
cv::InputArray src2, // Input array (ccs or complex)
cv::OutputArray dst, // Result array
int flags, // for row-by-row computation
bool conj = false // true to conjugate src2
);
使用离散傅立叶变换进行卷积
由于时域系统的卷积等价于频域系统的乘积,因此可以通过分别对图像和系统进行傅立叶变换,在对变换结果的乘积进行反变换就能快速的得到卷积的结果。
// Example 12-1. Using cv::dft() and cv::idft() to accelerate
// the computation of convolutions
#include
#include
using std::cout;
using std::endl;
int main(int argc, char** argv) {
if (argc != 2) {
cout << "\nExample 12-1. Using cv::dft() and cv::idft() to accelerate the"
<< "\n computation of convolutions"
<< "\nFourier Transform\nUsage: "
<< argv[0] << " \n" << endl;
return -1;
}
cv::Mat A = cv::imread(argv[1], 0);
if (A.empty()) {
cout << "Cannot load " << argv[1] << endl;
return -1;
}
cv::Size patchSize(100, 100);
cv::Point topleft(A.cols / 2, A.rows /2);
cv::Rect roi(topleft.x, topleft.y, patchSize.width, patchSize.height);
cv::Mat B = A(roi);
int dft_M = cv::getOptimalDFTSize(A.rows + B.rows - 1);
int dft_N = cv::getOptimalDFTSize(A.cols + B.cols - 1);
cv::Mat dft_A = cv::Mat::zeros(dft_M, dft_N, CV_32F);
cv::Mat dft_B = cv::Mat::zeros(dft_M, dft_N, CV_32F);
cv::Mat dft_A_part = dft_A(cv::Rect(0, 0, A.cols, A.rows));
cv::Mat dft_B_part = dft_B(cv::Rect(0, 0, B.cols, B.rows));
A.convertTo(dft_A_part, dft_A_part.type(), 1, -mean(A)[0]);
B.convertTo(dft_B_part, dft_B_part.type(), 1, -mean(B)[0]);
cv::dft(dft_A, dft_A, 0, A.rows);
cv::dft(dft_B, dft_B, 0, B.rows);
// set the last parameter to false to compute convolution instead of correlation
//
cv::mulSpectrums(dft_A, dft_B, dft_A, 0, true);
cv::idft(dft_A, dft_A, cv::DFT_SCALE, A.rows + B.rows - 1);
cv::Mat corr = dft_A(cv::Rect(0, 0, A.cols + B.cols - 1, A.rows + B.rows - 1));
cv::normalize(corr, corr, 0, 1, cv::NORM_MINMAX, corr.type());
cv::pow(corr, 3.0, corr);
B ^= cv::Scalar::all(255);
cv::imshow("Image", A);
cv::imshow("ROI", B);
cv::imshow("Correlation", corr);
cv::waitKey();
return 0;
}
通过使用此技巧,图像匹配的计算量从 下降到了
cv::dct(): The Discrete Cosine Transform
对于实数据,DFT 通过将数据分为两份分别赋值给假象数据的实部和虚部从而提高速度。离散余弦变换只处理实数据使用如下公式
其中其默认对计算结果进行归一化。
void cv::dct(
cv::InputArray src, // Input array (even size)
cv::OutputArray dst, // Output array
int flags = 0 // for row-by-row or inverse
);
其参数与 DFT 函数基本类似,其中由于 DCT 在内部调用了 DFT,只是数据大小只是输入数据的一半,因此 DCT 的最佳数据长度
其中 N 是需要变换的数据长度
cv::idct(): The Inverse Discrete Cosine Transform
出于与 DFT 相同的原因,也存在反离散余弦变换
void cv::idct(
cv::InputArray src, // Input array
cv::OutputArray dst, // Output array
int flags = 0, // for row-by-row computation
);
Opencv 提供 cv::integral() 函数实现子区域图像的快速求和,其最突出的一个应用就是哈儿小波。而图像积分又分成求和、平方求和和斜和。
基于上述三个结果就可以组合出需要的结果,比如一块区域的和可以通过这下式计算
通过这种方式可以更快地进行平滑,近似梯度,计算均值、标准差以及针对不同的窗口大小进行 block correlations。
标准求和积分运算
void cv::integral(
cv::InputArray image, // Input array, W x H
cv::OutputArray sum, // Output sum results, (W + 1) x (H + 1)
int sdepth = -1 // Results depth (e.g., cv::F32, cv::S32, cv::F64)
);
平方求和积分运算
void cv::integral(
cv::InputArray image, // Input array
cv::OutputArray sum, // Output sum results
cv::OutputArray sqsum, // Output sum of squares results
int sdepth = -1 // Results depth (e.g., cv::F32)
);
其中 sqsum 就是所需要的平方求和结果。
斜和积分运算
void cv::integral(
cv::InputArray image, // Input array
cv::OutputArray sum, // Output sum results
cv::OutputArray sqsum, // Output sum of squares results
cv::OutputArray tilted, // Output tilted sum results
int sdepth = -1 // Results depth (e.g., cv::F32)
);
其中 tilted 就是所需要的斜和结果
void cv::Canny(
cv::InputArray image, // Input single channel image
cv::OutputArray edges, // Output edge image
double threshold1, // "lower" threshold
double threshold2, // "upper" threshold
int apertureSize = 3, // Sobel aperture
bool L2gradient = false // true=L2-norm (more accurate)
);
参数说明:
霍夫变换是一种从图像中找出直线,圆或者其他简单图像的方法。
基本原理是:针对每个点,对其斜率进行离散化,通常为 180,这样直线的斜率精度为 1,从而在斜率和截距的平面中绘制通过该直线的所有可能直线的图像(通常也为一条直线)。这样该直线就包含了所有通过该点的直线,之后对每个点进行相同的操作。接着在斜率截距平面上找出多条直线相交的点,即这几个点均在该交点所表示的直线上。最后通过设置阈值就可以找出图像中的直线了。同时,考虑斜率可能存在直线垂直的情况,因此通常不使用斜率截距而使用极坐标系的距离和方向角,不过这样转换之后的所有直线的轨迹将是一条曲线。
标准霍夫变换(standard hough transform,SHT)以及多尺度霍夫变换(multiscale Hough transform,MHT)
void cv::HoughLines(
cv::InputArray image, // Input single channel image
cv::OutputArray lines, // N-by-1 two-channel array
double rho, // rho resolution (pixels)
double theta, // theta resolution (radians)
int threshold, // Unnormalized accumulator threshold
double srn = 0, // rho refinement (for MHT)
double stn = 0 // theta refinement (for MHT)
);
参数说明:
渐进概率霍夫变换
void cv::HoughLinesP(
cv::InputArray image, // Input single channel image
cv::OutputArray lines, // N-by-1 4-channel array
double rho, // rho resolution (pixels)
double theta, // theta resolution (radians)
int threshold, // Unnormalized accumulator threshold
double minLineLength = 0, // required line length
double maxLineGap = 0 // required line separation
);
参数说明
霍夫圆变换可以使用与霍夫直线变换相同的思想,但由于表示圆需要三个值(圆心的横纵坐标和半径),因此也就需要更多的存储空间和更长的计算时间。而 Opencv 在实现时使用了霍夫梯度方法的技巧来提高效率。算法的基本流程如下:
不过这样虽然提高的效率,但是也带来一些问题:
void cv::HoughCircles(
cv::InputArray image, // Input single channel image
cv::OutputArray circles, // N-by-1 3-channel or vector of Vec3f
int method, // Always cv::HOUGH_GRADIENT
double dp, // Accumulator resolution (ratio)
double minDist, // Required separation (between lines)
double param1 = 100, // Upper Canny threshold
double param2 = 100, // Unnormalized accumulator threshold
int minRadius = 0, // Smallest radius to consider
int maxRadius = 0 // Largest radius to consider
);
参数说明:
// Example 12-2. Using cv::HoughCircles() to return a sequence of circles found in a
// grayscale image
#include
#include
#include
#include
using std::cout;
using std::endl;
using std::vector;
void help(char** argv) {
cout << "\nExample 12-1. Using cv::dft() and cv::idft() to accelerate the computation of convolutions"
<< "\nHough Circle detect\nUsage: " << argv[0] <<" \n"
<< "Example:\n" << argv[0] << " ../stuff.jpg\n" << endl;
}
int main(int argc, char** argv) {
help(argv);
if (argc != 2) {
return -1;
}
cv::Mat src, image;
src = cv::imread(argv[1], 1);
if (src.empty()) {
cout << "Cannot load " << argv[1] << endl;
return -1;
}
cv::cvtColor(src, image, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(image, image, cv::Size(5, 5), 0, 0);
vector circles;
cv::HoughCircles(image, circles, cv::HOUGH_GRADIENT, 2, image.cols/4);
for (size_t i = 0; i < circles.size(); ++i) {
cv::circle(src,
cv::Point(cvRound(circles[i][0]), cvRound(circles[i][1])),
cvRound(circles[i][2]),
cv::Scalar(0, 0, 255),
2,
cv::LINE_AA);
}
cv::imshow("Hough Circles", src);
cv::waitKey(0);
return 0;
}
一副图像的距离变换被定义为一张新图,其中的每个点的像素值被设置为其原始图像对应点到最近的像素值零点的距离。其输入通常为边缘检测结果,这样所有边缘点的距离变换都为零,而非边缘的都为非零值。
非标记距离变换
void cv::distanceTransform(
cv::InputArray src, // Input image
cv::OutputArray dst, // Result image
int distanceType, // Distance metric to use
int maskSize // Mask to use (3, 5, or see below)
);
参数说明:
标记距离变换
void cv::distanceTransform(
cv::InputArray src, // Input image
cv::OutputArray dst, // Result image
cv::OutputArray labels, // Connected component ids
int distanceType, // Distance metric to use
int maskSize, // (3, 5, or see below)
int labelType = cv::DIST_LABEL_CCOMP // How to label
);
参数说明:
漫水法
int cv::floodFill(
cv::InputOutputArray image, // Input image, 1 or 3 channels
cv::Point seed, // Start point for flood
cv::Scalar newVal, // Value for painted pixels
cv::Rect* rect, // Output bounds painted domain
cv::Scalar lowDiff = cv::Scalar(),// Maximum down color distance
cv::Scalar highDiff = cv::Scalar(),// Maximum up color distance
int flags // Local/global, and mask-only
);
int cv::floodFill(
cv::InputOutputArray image, // Input w-by-h, 1 or 3 channels
cv::InputOutputArray mask, // 8-bit, w+2-by-h+2 (Nc=1)
cv::Point seed, // Start point for flood
cv::Scalar newVal, // Value for painted pixels
cv::Rect* rect, // Output bounds painted domain
cv::Scalar lowDiff = cv::Scalar(), // Maximum down color distance
cv::Scalar highDiff = cv::Scalar(), // Maximum up color distance
int flags // Local/global, and mask-only
);
参数说明:
具体 flags 的设置还比较复杂,其中低八位控制算法的连通性,而高八位设置填充的值。比如需要8连通,只填充固定距离,只修改 mask,使用 47 填充,flags 应该被设置为
flags = 8
| cv::FLOODFILL_MASK_ONLY
| cv::FLOODFILL_FIXED_RANGE
| (47 << 8);
分水岭算法
在实际应用中,如果想要分割一幅图像但又没有任何分离背景的信息,就可以考虑分水岭算法。其首先检测图像的边缘并将其作为山峰,而将较为平坦的区域作为山谷,最后从山谷开始漫水实现图像分割。
void cv::watershed(
cv::InputArray image, // Input 8-bit, three channels
cv::InputOutputArray markers // 32-bit float, single channel
);
参数说明:
Grabcuts
Graphcuts 使用用户标记的前景和背景从而建立两类分布直方图,并假定未标记的前景和背景平滑且连通从而具有相似的分布,最终基于这些假设定义能量函数,算法最后的优化方向就是最小化指定的能量函数。
Grabcuts 是 Graphcuts 的改进,主要包括
void cv::grabCut(
cv::InputArray img,
cv::InputOutputArray mask,
cv::Rect rect,
cv::InputOutputArray bgdModel,
cv::InputOutputArray fgdModel,
int iterCount,
int mode = cv::GC_EVAL
);
参数说明:
均值漂移分割
均值漂移分割——cv::pyrMeanShiftFiltering() 查找空间中颜色分布的极值。与此类似的均值漂移算法则被用于跟踪与运动,其主要用于处理序列图像的分布。
给定多维数据的集合,其中可能包含 x, y, blue, green, red,而均值漂移一个空间上的扫描窗得到数据密度最高的块。不过由于不同的维度尺度差别很大,因此通常需要针对不同的维度指定不同的窗口大小,至少也应该针对位置和颜色分别指定两个大小。而过程中,图像金字塔也将被使用,高层的结果将在低层继续优化。
void cv::pyrMeanShiftFiltering(
cv::InputArray src, // 8-bit, Nc=3 image
cv::OutputArray dst, // 8-bit, Nc=3, same size as src
cv::double sp, // Spatial window radius
cv::double sr, // Color window radius
int maxLevel = 1, // Max pyramid level
cv::TermCriteria termcrit = TermCriteria(
cv::TermCriteria::MAX_ITER | cv::TermCriteria::EPS,
5,
1
)
);
参数说明: