您现在的位置是:首页 >其他 >Opencv C++图像处理:点多边形测试 + 矩 + 凸包 + 映射 + 反向投影网站首页其他
Opencv C++图像处理:点多边形测试 + 矩 + 凸包 + 映射 + 反向投影
简介Opencv C++图像处理:点多边形测试 + 矩 + 凸包 + 映射 + 反向投影
1、点多边形测试
1.1、计算像素点是在轮廓内部、外部或边界上:cv::pointPolygonTest()
#include <opencv2/imgproc.hpp>
函数说明:double cv::pointPolygonTest( InputArray contour, Point2f pt, bool measureDist );
输入参数:
contour 输入轮廓。
pt 测试点坐标。
measureDist 是否返回距离值。
返回值: 若measureDist=True,返回点到最近轮廓边的有符号距离。
若measureDist=False时,返回值分别为+1(内部)、-1(外部)和0(边界上)。
1.2、计算最小值和最大值及其位置:cv::minMaxLoc()
该函数不适用于多通道阵列。如果需要在所有通道中找到最小或最大元素,请首先使用Mat::整形将数组重新解释为单个通道。或者,您可以使用extractImageCOI、mixChannels或split提取特定频道。
#include <opencv2/core.hpp>
函数说明:void cv::minMaxLoc( InputArray src, double * minVal, double * maxVal = 0, Point * minLoc = 0, Point * maxLoc = 0, InputArray mask = noArray() );
输入参数:
src 输入单通道阵列。
minVal 最小值;如果不需要,则使用NULL。
maxVal = 0 最大值;如果不需要,则使用NULL。
minLoc = 0 最小位置(在2D情况下);如果不需要,则使用NULL。
maxLoc = 0 最大位置(在2D情况下);如果不需要,则使用NULL。
mask = noArray() 用于选择子阵列的可选掩码。如果掩码不是空数组,则在指定的数组区域中搜索极值。
1.3、实战案例
#include <opencv2/opencv.hpp>
//using namespace cv;
//using namespace std;
int main(int argc, const char* argv[])
{
//(1)构建一张单通道8位400 x 400的图像,
const int r = 100;
cv::Mat src = cv::Mat::zeros(r * 4, r * 4, CV_8UC1);
//(2)绘制自定义六边形(通过line绘制六次)
std::vector<cv::Point2f> vert(6);
vert[0] = cv::Point(3 * r / 2, static_cast<int>(1.34 * r));
vert[1] = cv::Point(1 * r, 2 * r);
vert[2] = cv::Point(3 * r / 2, static_cast<int>(2.866 * r));
vert[3] = cv::Point(5 * r / 2, static_cast<int>(2.866 * r));
vert[4] = cv::Point(3 * r, 2 * r);
vert[5] = cv::Point(5 * r / 2, static_cast<int>(1.34 * r));
for (int ii = 0; ii < 6; ii++)
{
cv::line(src, vert[ii], vert[(ii+1) % 6], cv::Scalar(255), 3, 8, 0);
}
//(3)轮廓检测
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierachy;
cv::Mat src_contours = src.clone();
cv::findContours(src_contours, contours, hierachy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
//(4)对图像中所有像素点进行【点多边形测试】:测试像素点是在多边形内部、边界或外部上
cv::Mat raw_dist = cv::Mat::zeros(src_contours.size(), CV_32FC1);
for (int row = 0; row < raw_dist.rows; row++)
{
for (int col = 0; col < raw_dist.cols; col++)
{
//输入参数:输入轮廓,测试点,是否返回距离值。(False:1表示在内部,0表示在边界上,-1表示在外部)(True表示返回实际距离值)
double dist = cv::pointPolygonTest(contours[0], cv::Point2f(static_cast<float>(col), static_cast<float>(row)), true);
raw_dist.at<float>(row, col) = static_cast<float>(dist);
}
}
//(5)按内部、边界、外部三个区域分开,并且内/外部依据距离远近动态赋值,形成渐变色(也可以自定义为固定值)
double minValue, maxValue;
cv::minMaxLoc(raw_dist, &minValue, &maxValue, 0, 0, cv::Mat()); //计算最大值和最小值
cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC3);
for (int row = 0; row < dst.rows; row++)
{
for (int col = 0; col < dst.cols; col++)
{
float dist = raw_dist.at<float>(row, col);
if (dist > 0)
dst.at<cv::Vec3b>(row, col)[0] = (uchar)(abs(1.0 - (dist / maxValue)) * 255); //内部
else if (dist < 0)
dst.at<cv::Vec3b>(row, col)[2] = (uchar)(abs(1.0 - (dist / minValue)) * 255); //外部
else
{
dst.at<cv::Vec3b>(row, col)[0] = (uchar)(abs(255 - dist)); //边界
dst.at<cv::Vec3b>(row, col)[1] = (uchar)(abs(255 - dist)); //边界
dst.at<cv::Vec3b>(row, col)[2] = (uchar)(abs(255 - dist)); //边界
}
}
}
//(6)显示图像
cv::imshow("src", src);
cv::imshow("dst", dst);
cv::waitKey(0);
return 0;
}
2、矩
cv::moments是OpenCV中一个常用的函数,用于计算图像区域的各阶矩。如:计算输入图像中一个区域的几何矩、中心矩和归一化中心矩。
- 几何矩:是用来描述图像区域的形状和位置的;
- 中心矩:是关于区域质心的矩,它可以用于计算区域的方向;
- 归一化中心矩:是中心矩的归一化版本,可以用于不同尺度的比较。
常用于形状分析、图像识别、目标跟踪等领域。
- 在形状分析方面,可以用来计算图像中各个对象的重心、面积、方向和轮廓等信息,从而实现图像分割和形状匹配。
- 在图像识别方面,可以用来计算图像中特定对象的各种特征,如Hu矩,用于快速分类和识别目标。
- 在目标跟踪方面,可以用来计算目标位置和方向,从而实现实时跟踪。
2.1、计算多边形或光栅化形状的三阶以下的所有力矩:cv::moments()
spatial moments(空间距) | central moments(中心矩) | central normalized moments(中心归一化矩) |
---|---|---|
double m00 | double mu20 | double nu20 |
double m10 | double mu11 | double nu11 |
double m01 | double mu02 | double nu02 |
double m20 | double mu30 | double nu30 |
double m11 | double mu21 | double nu21 |
double m02 | double mu12 | double nu12 |
double m30 | double mu03 | double nu03 |
double m21 | ||
double m12 | ||
double m03 |
#include <opencv2/imgproc.hpp>
函数说明:Moments cv::moments( InputArray array, bool binaryImage = false );
输入参数:
array 光栅图像(单通道、8位或浮点2D阵列)或2D点(点或点2f)的阵列(1×N或N×1)。
binaryImage = false 如果为True,则将所有非零图像像素视为1。该参数仅用于图像。
返回值: moments.
备注:仅适用于Python绑定中的轮廓矩计算:请注意,输入数组的numpy类型应为np.int32或np.float32。
备注:由于轮廓矩是使用格林公式计算的,对于具有自相交的轮廓,您可能会得到看似奇怪的结果,例如蝴蝶形轮廓的零面积(m00)。
2.2、计算轮廓面积:cv::contourArea()
#include <opencv2/imgproc.hpp>
函数说明:double cv::contourArea( InputArray contour, bool oriented = false );
输入参数: contour 2D点(轮廓顶点)的输入向量,存储在std::vector或Mat中。
oriented = false 定向区域标志符。
返回值: 若oriented=true,返回一个带符号的面积值,符号值取决于轮廓方向(顺时针或逆时针)。
若oriented=false(默认),返回面积的绝对值。
备注:与力矩类似,使用格林公式计算面积。因此,如果使用drawContours或fillPoly绘制轮廓,则返回的面积和非零像素数可能不同。此外,对于具有自相交的轮廓,该函数肯定会给出错误的结果。
2.3、计算曲线长度或闭合轮廓周长:cv::arcLength()
#include <opencv2/imgproc.hpp>
函数说明:double cv::arcLength( InputArray curve, bool closed );
输入参数: curve 2D点的输入矢量,存储在std::vector或Mat中。
closed 曲线是否闭合的标志符。
返回值: 若closed=true,返回闭合轮廓周长。
若closed=false,返回曲线长度。
2.4、实战案例
#include <opencv2/opencv.hpp>
//using namespace std;
//using namespace cv;
int main(int argc, char** argv)
{
//(1)输入图像
cv::Mat src = cv::imread("test.jpg");
if (!src.data)
{
std::cout << "can't read image!" << std::endl;
return -1;
}
//(2)图像处理
cv::Mat src_gray_, src_blur, src_canny;
cv::cvtColor(src, src_gray_, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(src_gray_, src_blur, cv::Size(3, 3), 0, 0);
cv::Canny(src_blur, src_canny, 0, 160); //该参数极大影响最终效果
//cv::Canny(blur_src, canny_src, 80, 160); //该参数极大影响最终效果
//(3)轮廓检测
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierachy;
cv::findContours(src_canny, contours, hierachy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
//(4)计算每个轮廓的矩。矩中心点center=(x0, y0)。【其中:x0=m10/m00,y0=m01/m00】
std::vector<cv::Moments> contours_moments(contours.size());
std::vector<cv::Point2f> ccs(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
//输入参数:输入图像,是否返回二值化图像
contours_moments[i] = cv::moments(contours[i]); //计算矩
ccs[i] = cv::Point(static_cast<float>(contours_moments[i].m10 / contours_moments[i].m00), static_cast<float>(contours_moments[i].m01 / contours_moments[i].m00));
}
//(5)绘制轮廓和圆(打印轮廓面积 + 弧长)
cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC3); //空矩阵
//cv::Mat dst = src.clone();
cv::RNG rng(12345);
for (size_t i = 0; i < contours.size(); i++)
{
if (contours[i].size() < 10) //轮廓筛选(过滤较小的轮廓)
continue;
cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255)); //生成随机数
cv::drawContours(dst, contours, i, color, 2, 8, hierachy, 0, cv::Point(0, 0));
cv::circle(dst, ccs[i], 2, color, 2, 8);
std::cout << "当前为第[i]个轮廓:" << i << "【轮廓中心点】x=" << ccs[i].x << ", y=" << ccs[i].y << "【轮廓面积contourArea】" << cv::contourArea(contours[i]) << "【轮廓弧长arcLength】" << cv::arcLength(contours[i], true) << std::endl;
}
//(6)显示图像
cv::imshow("src", src);
cv::imshow("gray", src_gray_);
cv::imshow("blur", src_blur);
cv::imshow("canny", src_canny);
cv::imshow("dst", dst);
cv::waitKey(0);
return 0;
}
3、凸包
定义:基于给定二维平面上的点集,将最外层的点连接起来构成的凸多边型即凸包。该多边形包含点集中所有的点。
凸包算法详解(convex hull)
Graham扫描算法
- 11、首先选择 y 方向最低的点作为起始点p0,然后对p0进行极坐标扫描,依次添加p1…pn(排列顺序根据极坐标的角度大小,逆时针方向决定)
- 22、添加任意 pi 点到凸包中,若导致左转向(逆时针),则添加该点到凸包;反之,若导致右转向(顺时针),则删除该点。
3.1、计算凸包:cv::convexHull()
#include <opencv2/imgproc.hpp>
函数说明:void cv::convexHull( InputArray points, OutputArray hull, bool clockwise = false, bool returnPoints = true );
输入参数:
points 输入2D点集,存储在std::vector或Mat中。
hull 输出凸包。
(1)指数的整数向量。在第一种情况下,hull元素是原始数组中凸包点的基于0的索引(因为凸包的点集是原始点集的子集)。
(2)点的向量。在第二种情况下,hull元素是凸船体点本身。
clockwise = false 定位标志。true表示输出凸包为顺时针方向。否则为逆时针方向。假设的坐标系是X轴向右,Y轴向上。
returnPoints = true 操作标记。true则返回凸包点。否则返回凸包点的索引。
当输出数组为std::vector时,该标志被忽略。
输出取决于vector的类型:(1)std::vector<int>则returnPoints=false;(2)std::vector<Point>则returnPoints=true。
3.2、实战案例
#include <opencv2/opencv.hpp>
//using namespace std;
//using namespace cv;
int main(int argc, char** argv)
{
//(1)输入图像
cv::Mat src = cv::imread("test.jpg");
if (!src.data)
{
std::cout << "can't read image!" << std::endl;
return -1;
}
//(2)图像处理
cv::Mat src_gray, src_blur, src_bin;
cv::cvtColor(src, src_gray, cv::COLOR_BGR2GRAY);
cv::blur(src_gray, src_blur, cv::Size(3, 3));
cv::threshold(src_blur, src_bin, 100, 255, cv::THRESH_BINARY); //该参数极大影响最终效果
//(3)轮廓检测
std::vector<std::vector<cv::Point>> contours;
std::vector<cv::Vec4i> hierachy;
cv::findContours(src_bin, contours, hierachy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE, cv::Point(0, 0));
//(4)计算凸包
std::vector<std::vector<cv::Point>> convexs(contours.size());
for (size_t i = 0; i < contours.size(); i++)
{
//输入参数:轮廓点,凸包,方向(默认False=逆时针),是否返回点个数(默认True)
cv::convexHull(contours[i], convexs[i], false, true);
}
//(5)绘制凸包
cv::Mat dst = cv::Mat::zeros(src.size(), CV_8UC3); //空矩阵
//cv::Mat dst = src.clone(); //复制原图
std::vector<cv::Vec4i> empty(0);
cv::RNG rng(12345);
for (size_t k = 0; k < contours.size(); k++)
{
cv::Scalar color = cv::Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));
cv::drawContours(dst, contours, k, color, 2, cv::LINE_8, hierachy, 0, cv::Point(0, 0));
cv::drawContours(dst, convexs, k, color, 2, cv::LINE_8, empty, 0, cv::Point(0, 0));
}
//(6)显示图像
cv::imshow("src", src);
cv::imshow("src_gray", src_gray);
cv::imshow("src_blur", src_blur);
cv::imshow("src_bin", src_bin);
cv::imshow("dst", dst);
cv::waitKey(0);
return 0;
}
4、映射
4.1、像素重映射:cv::remap()
#include <opencv2/imgproc.hpp>
函数说明: void cv::remap( InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode = BORDER_CONSTANT, const Scalar &borderValue = Scalar() );
输入参数:
(1)src 源图像。
(2)dst 目的图像。大小与map1相同,类型与src相同。
(3)map1 (x,y)点的第一个映射或只有x值的第一个映射,其类型为CV_16SC2, CV_32FC1或CV_32FC2。
(4)map2 第二个y值的映射,其类型分别为CV_16UC1、CV_32FC1或none(如果map1为(x,y)点,则为空映射)。
(5)interpolation 插值方法。不支持INTER_AREA和INTER_LINEAR_EXACT方法。
cv::INTER_NEAREST 最近邻插值
cv::INTER_LINEAR 双线性插值(默认)
cv::INTER_CUBIC 双三次插值
cv::INTER_AREA 使用像素面积关系进行重新采样。
cv::INTER_LANCZOS4 8x8邻域上的Lanczos插值
cv::INTER_LINEAR_EXACT 位精确双线性插值
cv::INTER_NEAREST_EXACT 位精确最近邻插值。这将产生与PIL、scikit图像或Matlab中的最近邻方法相同的结果。
cv::INTER_MAX 插值代码掩码
cv::WARP_FILL_OUTLIERS 标志,填充所有目的地图像像素。如果其中一些对应于源图像中的异常值,则将其设置为零。
cv::WARP_INVERSE_MAP 标志,逆变换
(6)borderMode = BORDER_CONSTANT 边界类型(即边界填充方式)。
cv::BORDER_CONSTANT = 0 iiiiii|abcdefgh|iiiiiii 常量法。填充常数值
cv::BORDER_REPLICATE = 1 aaaaaa|abcdefgh|hhhhhhh 复制法。复制最边缘像素
cv::BORDER_REFLECT = 2 fedcba|abcdefgh|hgfedcb 反射法。以两边为轴
cv::BORDER_WRAP = 3 cdefgh|abcdefgh|abcdefg 外包装法。
cv::BORDER_REFLECT_101 = 4 gfedcb|abcdefgh|gfedcba 反射法。以最边缘像素为轴
cv::BORDER_TRANSPARENT = 5 uvwxyz|abcdefgh|ijklmno
cv::BORDER_REFLECT101 = 6 same as BORDER_REFLECT_101
cv::BORDER_DEFAULT = 7 same as BORDER_REFLECT_101
cv::BORDER_ISOLATED = 8 do not look outside of ROI
(7)Scalar &borderValue = Scalar() 边界值(在边界不变的情况下)。缺省值是0。
备注:此函数不能原地操作。
4.2、实战案例
#include <opencv2/opencv.hpp>
//using namespace std;
//using namespace cv;
int main(int argc, char** argv)
{
while (true)
{
//(1)输入图像
cv::Mat src = cv::imread("test.jpg");
if (!src.data)
{
std::cout << "can't read image!" << std::endl;
return -1;
}
//(2)像素重映射的四种类型(自定义)
int c = cv::waitKey(500); //2.1、等待键盘事件
if ((char)c == 27) //2.2、退出键:Esc
break;
int index = c % 4; //2.3、根据输入值进行四种类型判断:[0, 1, 2, 3]
cv::Mat map_x, map_y;
map_x.create(src.size(), CV_32FC1); //x映射表
map_y.create(src.size(), CV_32FC1); //y映射表
for (int row = 0; row < src.rows; row++)
{
for (int col = 0; col < src.cols; col++)
{
switch (index)
{
case 0: //2.2.1、缩小一半
if (col > (src.cols * 0.25) && col <= (src.cols*0.75) && row > (src.rows*0.25) && row <= (src.rows*0.75))
{
map_x.at<float>(row, col) = 2 * (col - (src.cols*0.25));
map_y.at<float>(row, col) = 2 * (row - (src.rows*0.25));
}
else
{
map_x.at<float>(row, col) = 0;
map_y.at<float>(row, col) = 0;
}
break;
case 1: //2.2.2、沿着Y方向翻转
map_x.at<float>(row, col) = (src.cols - col - 1);
map_y.at<float>(row, col) = row;
break;
case 2: //2.2.3、沿着X方向翻转
map_x.at<float>(row, col) = col;
map_y.at<float>(row, col) = (src.rows - row - 1);
break;
case 3: //2.2.4、沿着XY方向同时翻转
map_x.at<float>(row, col) = (src.cols - col - 1);
map_y.at<float>(row, col) = (src.rows - row - 1);
break;
}
}
}
//(3)像素重映射:将输入图像的所有像素根据指定规则进行映射,并形成新图像。
cv::Mat dst;
cv::remap(src, dst, map_x, map_y, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 255, 255));
//(4)显示图像
cv::imshow("src", src);
cv::imshow("dst", dst);
}
return 0;
}
5、反向投影
反向投影是一种被广泛应用于医学影像处理,计算机视觉和计算机图形学中的图像重建技术。
- 在医学领域,反向投影技术经常用于CT(计算机断层扫描仪)和PET(正电子发射断层扫描)成像中,用于3D图像重建和图像分割。
- 在计算机视觉领域,反向投影在3D物体重建中被广泛使用。例如,通过将多个2D图像拼接起来重建3D物体,如在机器人领域中处理3D传感器数据或为虚拟现实应用程序创建3D模型。
- 在计算机图形学中,反向投影可以用于对3D对象进行渲染和投射,如对电影制作和电子游戏开发。
5.1、将指定通道从输入阵列复制到输出阵列的指定通道:cv::mixChannels()
#include <opencv2/core.hpp>
函数说明:void cv::mixChannels( const Mat * src, size_t nsrcs, Mat * dst, size_t ndsts, const int * fromTo, size_t npairs );
输入参数: (1)src 矩阵的输入数组或向量;所有矩阵必须具有相同的大小和相同的深度。
(2)nsrcs src中的矩阵数。
(3)dst 矩阵的输出阵列或向量;必须分配所有矩阵;它们的大小和深度必须与src[0]中的相同。
(4)ndsts dst中的矩阵数。
(5)fromTo 索引对数组:指定要复制的通道以及复制的位置。
fromTo[k*2]是src中输入通道的基于0的索引,fromTo[m*2+1]是dst中输出通道的索引;
使用连续的通道编号: 第一个输入图像通道的索引从0到src[0].channels()-1;
第二个输入图像频道的索引从src[0]到src[0].channels()+src[1].Channelss()-1;
依此类推,输出图像通道使用相同的方案。
特殊情况:当fromTo[k*2]为负时,相应的输出通道填充为零。
(6)npairs fromTo中的索引对数。
5.2、计算直方图的反向投影:cv::calcBackProject()
- 反向投影:在输入图像中,查找与模板图像最匹配的点或区域,即定位模板图像出现在输入图像的位置。
- 定位方法:不断的在输入图像中切割跟模板图像大小一致的图像块,并用直方图对比的方式与模板图像进行比较。
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。
#include <opencv2/imgproc.hpp>
函数说明:void cv::calcBackProject( const Mat * images, int nimages, const int * channels, InputArray hist, OutputArray backProject, const float ** ranges, double scale = 1, bool uniform = true );
输入参数: (1)images 输入图像(CV_8U、CV_16U或CV_32F)。具有相同的深度和尺寸,且每一个都可以具有任意数量的通道。
(2)nimages 输入图像的数量。
(3)channels 计算反向投影的通道列表。通道数量必须与直方图维度相匹配。
第一个阵列通道的计数从0到images[0].channels()-1;
第二个阵列通道的计数从images[0].channels()到images[0].channels() + images[1].channels()-1, and so on.
(4)hist 直方图。可以是密集的或稀疏的
(5)backProject 目标反向投影阵列。与images[0]具有相同大小和深度的单通道阵列。
(6)ranges 每个维度中的直方图bin边界的数组。请参见calcHist。
(7)scale = 1 可选比例因子,一般都设置成1。
(8)uniform = true 直方图是否一致的标志符。
备注:该函数的执行效率非常的低。在使用之前需要注意图像大小,直方图维数,对比方式。
举例:对于1010 x 1010的RGB输入图像,10x10的模板图像,需要生成1百万次3维直方图,然后对比1百万次3维直方图。
5.3、实战案例
#include <opencv2/opencv.hpp>
//using namespace cv;
//using namespace std;
int main(int argc, const char* argv[])
{
//(1)输入图像
cv::Mat src = cv::imread("test.jpg");
if (src.empty())
{
std::cout << "can't read image!" << std::endl;
return -1;
}
//(2)图像处理
cv::Mat src_hsv, src_hue;
cv::cvtColor(src, src_hsv, cv::COLOR_BGR2HSV); //格式转换
src_hue.create(src_hsv.size(), src_hsv.depth()); //新建矩阵
int nchannels[] = {0, 0};
cv::mixChannels(&src_hsv, 1, &src_hue, 1, nchannels, 1); //将制定通道从输入阵列复制到输出阵列的指定通道
//(3)计算直方图和归一化
cv::Mat h_hist;
int bins = 12;
float range[] = {0, 180};
const float* histRanges = {range};
cv::calcHist(&src_hue, 1, 0, cv::Mat(), h_hist, 1, &bins, &histRanges);
cv::normalize(h_hist, h_hist, 0, 255, cv::NORM_MINMAX, -1, cv::Mat());
//(4)计算直方图的反向投影
cv::Mat Back_Project_Image;
cv::calcBackProject(&src_hue, 1, 0, h_hist, Back_Project_Image, &histRanges, 1);
//(5)计算反向投影的直方图
int hist_h = 400;
int hist_w = 400;
cv::Mat Hist_Image(hist_w, hist_h, CV_8UC3, cv::Scalar(0, 0, 0));
int bin_w = hist_w / bins;
for(int ii = 1; ii < bins; ii++)
{
cv::rectangle(Hist_Image, cv::Point((ii - 1) * bin_w, (hist_h - cvRound(h_hist.at<float>(ii - 1) * (400 / 255)))), cv::Point(ii * bin_w, hist_h), cv::Scalar(0, 0, 255), -1);
}
//(6)显示图像
cv::imshow("src", src); //原图
cv::imshow("BackProj", Back_Project_Image); //反向投影
cv::imshow("Histogram", Hist_Image); //反向投影的直方图
cv::waitKey(0);
return 0;
}
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。