// Starfeatures_detector2.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include
#include
#include
#include
#include
#include
using namespace cv;
using namespace std;
int main()
{
//读取视频
VideoCapture cap;
cap.open("C:\\Users\\lbn\\Desktop\\夜间车辆\\6.mp4");
if (!cap.isOpened()) {
cout << "Failed to load the video!" << endl;
return -1;
}
Mat image;
while (1) {
cap >> image;//等同于cap.read(frame);
if (image.empty())break;//如果某帧为空则退出循坏
//重置图片大小
//Mat Image(480, 640, CV_8UC3);
//const int nTimes = 100;
//for (int i = 0; i < nTimes; i++)
//{
// //resize(srcImage, Image, dstImage.size(), 0, 0);
// resize(image, Image, Image.size(), 0, 0);
//}
Mat Image;
image.copyTo(Image);
//灰度化
Mat image_gray;
cvtColor(Image, image_gray, CV_RGB2GRAY);
//将图像最上面的1/3区域置零
Size img_size = Image.size();
Mat image_t = Image.clone();
Mat Mask(cvFloor(image_t.rows / 3), image_t.cols, CV_8UC3, Scalar(0));
Mat roi = image_t(Rect(0, 0, image_t.cols, cvFloor(image_t.rows / 3)));
Mask.copyTo(roi);
//double t1 = (double)getTickCount();
//for (int i = 0; i < cvFloor(image_t.rows / 3); i++) {
// for (int j = 0; j < image_t.cols; j++) {
// image_t.at
// image_t.at
// image_t.at
// }
//}
//double t2 = (double)getTickCount();
//cout << "time:" << (t2 - t1) * 1000 / getTickFrequency() << endl;
//imshow("【原图】", image_t);
//=============================================================在RGB空间分割红色和白色区域================================================================//
//将RGB图像分离通道
vector
Mat dst;
split(image_t, imageRGB);
merge(imageRGB, dst);
//显示图像
//imshow("dst",dst);
//分离RGB通道并分别显示
vector
Mat tmp(image_t.size(), CV_8U, Scalar(0));
for (int i = 0; i < 3; i++) {
if (i == 0)
b.push_back(imageRGB[0]);
else
b.push_back(tmp);
if (i == 1)
g.push_back(imageRGB[1]);
else
g.push_back(tmp);
if (i == 2)
r.push_back(imageRGB[2]);
else
r.push_back(tmp);
}
//merge(b,dst);
//imshow("b",dst);
//merge(g,dst);
//imshow("g",dst);
merge(r, dst);
//imshow("r",dst);
//检测红色强度较高的区域
Mat imgdst;
dst.copyTo(imgdst);
for (int i = 0; i < image_t.rows; i++) {
for (int j = 0; j < image_t.cols; j++) {
//检测红色区域
if (dst.at
{
imgdst.at
imgdst.at
imgdst.at
}
else
{
imgdst.at
imgdst.at
imgdst.at
}
/* if (dst.at
imgdst.at
imgdst.at
imgdst.at
}
else
{
imgdst.at
imgdst.at
imgdst.at
}*/
}
}
// imshow("红色提取RGB", imgdst);
//进行形态学腐蚀膨胀,以去除小区域
int g_nStructElementSize = 2;
Mat element = getStructuringElement(MORPH_ELLIPSE, Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1), Point(g_nStructElementSize, g_nStructElementSize));
//erode(imgdst, imgdst, element); erode(imgdst, imgdst, element);
dilate(imgdst, imgdst, element); dilate(imgdst, imgdst, element);
imshow("红色区域提取RGB", imgdst);
//imwrite("E:/夜间车辆/test.jpg",imgdst);
////将特征点画在颜色分割图上
//Mat Star_RGBrect;
////drawkeypoints(image, keypoints, image, DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS);
//drawKeypoints(imgdst, keypoints, Star_RGBrect, Scalar::all(-1), 0);
////drawKeypoints(image, keypoints, image_out, Scalar(0,0,0), 0);
//// imshow("Star_RGBrect", Star_RGBrect);
//=========================================================对原图进行Blob检测================================================================//
//灰度转换
//Mat imgdst_gray;
//cvtColor(image, imgdst_gray, CV_RGB2GRAY);
//Blob特征检测
//特征检测器的参数设置
SimpleBlobDetector::Params params;
// size_t minRepeatability; //2 重复的最小次数,只有属于灰度图像斑点的那些二值图像斑点数量大于该值时,该灰度图像斑点才被认为是特征点
// float minDistBetweenBlobs; //10 最小的斑点距离,不同二值图像的斑点间距离小于该值时,被认为是同一个位置的斑点,否则是不同位置上的斑点
params.filterByColor = true; //斑点颜色的限制变量
params.blobColor = 255; //表示只提取黑色斑点;如果该变量为255,表示只提取白色斑点
//阈值控制
params.minThreshold = 50; //二值化的起始阈值 40
params.maxThreshold = 255; //二值化的终止阈值 160
params.thresholdStep = 2; //二值化的阈值步长
//像素面积大小控制
params.filterByArea = true;
params.minArea = 50; //斑点的最小面积
params.maxArea = 800000; //斑点的最大面积
//形状(凸)
params.filterByCircularity = true;
params.minCircularity = 0.5; //斑点的最小圆度
params.maxCircularity = std::numeric_limits
//形状凹
params.filterByConvexity = false;
params.minConvexity = 0.5; //0.05f//斑点的最小凹度
params.maxConvexity = std::numeric_limits
//形状圆
params.filterByInertia = false;
params.minInertiaRatio = 0.5f; //斑点的最小惯性率 圆的值为1,直线的值为0 //.05f
params.maxInertiaRatio = std::numeric_limits
Ptr
vector
detector1->detect(image_t, keyPoints);
Mat image_Blob;
drawKeypoints(image_t, keyPoints, image_Blob, Scalar(0, 0, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//drawKeypoints(image_Star, keyPoints, image_Star_Blob, Scalar(255, 0, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//// imshow("image_Star_Blob", image_Star_Blob);
//cout << "Blob_size 点个数:" << keyPoints.size() << endl;
//cout << "Blob_size 直径:" << keyPoints[0].size << endl;
//cout << "Blob_response 点强度:" << keyPoints[0].response << endl;
//cout << "Blob_pt 点位置:" << keyPoints[0].pt << endl;
//cout << "keypoints _angle" << keyPoints[0].angle << endl;
//cout << "keypoints_octave" << keyPoints[0].octave << endl;
Mat RGBrect_Blob;
drawKeypoints(imgdst, keyPoints, RGBrect_Blob, Scalar(255, 0, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
imshow("RGBrect_Blob", RGBrect_Blob);
//Mat Star_HSVrect_Blob;
//drawKeypoints(Star_HSVrect, keyPoints, Star_HSVrect_Blob, Scalar(255, 0, 0), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//// imshow("Star_HSVrect_Blob", Star_HSVrect_Blob);
//圆圈的大小表示特征的重要性大小
//
//=========================================================根据Blob检测结果划分ROI区域================================================================//
Mat Blob_contour;
Mat back(Image.size(), CV_8UC3, Scalar(0, 0, 0));
drawKeypoints(back, keyPoints, Blob_contour, Scalar(255, 255, 255), DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
//imshow("Blob_contour", Blob_contour);
cvtColor(Blob_contour, Blob_contour, CV_BGR2GRAY);
////二值化
int th = 50;
threshold(Blob_contour, Blob_contour, th, 255, CV_THRESH_BINARY);
// imshow("Blob_contour_bina", Blob_contour);
//漫水填充
Mat BlobRect;
Size m_Size = Blob_contour.size();
Mat temimage = Mat::zeros(m_Size.height + 2, m_Size.width + 2, Blob_contour.type());//延展图像
Blob_contour.copyTo(temimage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)));
floodFill(temimage, Point(0, 0), Scalar(255, 255, 255));
//裁剪延展的图像
Mat cutImg;
temimage(Range(1, m_Size.height + 1), Range(1, m_Size.width + 1)).copyTo(cutImg);
BlobRect = Blob_contour | (cutImg);
BlobRect = ~BlobRect;
//imshow("BlobRect", BlobRect);
//
//=========================================================融合颜色特征、Blob特征================================================================//
//将RGB空间红色特征作为掩码
cvtColor(imgdst, imgdst, CV_BGR2GRAY);
Mat mask = imgdst;
Mat dstRect_RGB;
BlobRect.copyTo(dstRect_RGB, mask);
imshow("dstRect_RGB", dstRect_RGB);
////将HSV空间白色和红色特征作为掩码
//cvtColor(imageout, imageout, CV_BGR2GRAY);
//Mat mask1 = imageout;
//Mat dstRect_HSV;
//ShareRect.copyTo(dstRect_HSV, mask1);
//// imshow("dstRect_HSV", dstRect_HSV);
//
//====================================================================车灯配对================================================================//
//用canny检测特征轮廓,以得到坐标点
Mat Canny_output(dstRect_RGB.size(), CV_8UC1, Scalar(0));
int cannylowthreshold = 27;
Canny(dstRect_RGB, Canny_output, cannylowthreshold, cannylowthreshold * 3, 3);
// imshow("Canny_output", Canny_output);
/// 提取轮廓
vector
vector
//contours为函数findContours计算得到的轮廓点分布值
findContours(Canny_output, contours_light, hierarchy1, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
Mat out(dstRect_RGB.size(), CV_8UC1, Scalar(0));
//drawContours(out, contours_light, -1, Scalar(255), cv::FILLED, 8, hierarchy1);
drawContours(out, contours_light, -1, Scalar(255), 1, 8, hierarchy1);
// imshow("out",out);
/*cout << "contours_light[0]=" << endl << contours_light[0] << endl;
cout << "contours_light[1]=" << endl << contours_light[1] << endl;
cout << "contours_light[2]=" << endl << contours_light[2] << endl;
cout << "contours_light[3]=" << endl << contours_light[3] << endl;
cout << "contours_light[4]=" << endl << contours_light[4] << endl;
cout << "contours_light[5]=" << endl << contours_light[5] << endl;*/
//计算轮廓的中心矩
int num = contours_light.size();//轮廓的条数
vector
for (int i = 0; i < num; i++) {
Moments M = moments(contours_light[i], true);
mc[i] = Point(M.m10 / M.m00, M.m01 / M.m00);
}
cout << "mc=" << endl << mc << endl;
// //画出轮廓及中心矩
//Mat lightContours(dstRect_RGB.size(),CV_8UC1,Scalar(0));
//for (int i = 0; i < num; i++) {
// drawContours(lightContours, contours_light, i, Scalar(255), 1, 8, hierarchy1);
////circle(lightContours,mc[i],4,Scalar(255),-1,8,0);
//}
// imshow("lightContours", lightContours);
Mat dst_rect(dstRect_RGB.size(), CV_8UC1, Scalar(0)); //车灯识别画布
//contours_light
//int q[num];
// int p = 0;//状态变量
//进行配对
for (int ii = 0; ii < num - 1; ii++) {
////判断输入是否已经绘制
//for (int a = 0; a < num;a++) {
// if (ii == q[a]) {
// p = 1;
// }
//}
//if (p == 1) { p = 0; break; }
for (int jj = ii + 1; jj < num; jj++)
{
////判断输入是否已经绘制
//for (int a = 0; a < num; a++) {
// if (jj == q[a]) {
// p = 1;
// }
//}
//if (p == 1) { p = 0; break; }
// if ((abs(mc[ii].y - mc[jj].y) < 8) && (abs(mc[ii].x - mc[jj].x)) >25 && (abs(mc[ii].x - mc[jj].x)) < 125 ) {
if ((abs(mc[ii].y - mc[jj].y) < 6) && (abs(mc[ii].x - mc[jj].x)) >5 && (abs(mc[ii].x - mc[jj].x)) < 125) {
//绘制配对轮廓
drawContours(dst_rect, contours_light, ii, Scalar(255), 1, 8, hierarchy1);
drawContours(dst_rect, contours_light, jj, Scalar(255), 1, 8, hierarchy1);
//查找轮廓
//vector
//contours_rect;
//findContours(dst_rect, contours_rect, hierarchy2, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
//计算每个车灯的外接矩形
RotatedRect rect1 = minAreaRect(contours_light[ii]);
Point2f P1[4];
rect1.points(P1);
cout << "points[0]:" << P1[0] << endl;
cout << "points[1]:" << P1[1] << endl;
cout << "points[2]:" << P1[2] << endl;
cout << "points[3]:" << P1[3] << endl;
RotatedRect rect2 = minAreaRect(contours_light[jj]);
Point2f P2[4];
rect2.points(P2);
cout << "points[0]:" << P2[0] << endl;
cout << "points[1]:" << P2[1] << endl;
cout << "points[2]:" << P2[2] << endl;
cout << "points[3]:" << P2[3] << endl;
//计算车灯区域的四个顶点
float rowData[2][4] = { { P1[0].x, P1[1].x, P1[2].x , P1[3].x },{ P2[0].x, P2[1].x, P2[2].x, P2[3].x } };
float colData[2][4] = { { P1[0].y, P1[1].y, P1[2].y , P1[3].y },{ P2[0].y, P2[1].y, P2[2].y, P2[3].y } };
Mat rowDataMat(2, 4, CV_32FC1, rowData);
Mat colDataMat(2, 4, CV_32FC1, colData);
double xmin, xmax, ymin, ymax;
minMaxIdx(rowDataMat, &xmin, &xmax);
minMaxIdx(colDataMat, &ymin, &ymax);
cout << "xmin=" << xmin << endl;
cout << "xmax=" << xmax << endl;
cout << "ymin=" << ymin << endl;
cout << "ymax=" << ymax << endl;
vector
Point p1(xmin, ymax);
P.push_back(p1);
Point p2(xmin, ymin);
P.push_back(p2);
Point p3(xmax, ymin);
P.push_back(p3);
Point p4(xmax, ymax);
P.push_back(p4);
for (int j = 0; j < 4; j++) {
line(Image, P[j], P[(j + 1) % 4], Scalar(255), 2);
}
}
}
}
imshow("MinAreaRect", Image);
waitKey(1);
}
system("pause");
destroyAllWindows();
//waitKey();
// return 0;
}
//************************************StarDetector函数详解*********************************************//
/*
CV_WRAP static Ptr
int lineThresholdProjected = 10,
int lineThresholdBinarized = 8,
int suppressNonmaxSize = 5);
};
maxSize – maximum size of the features. The following values are supported: 4, 6, 8, 11, 12, 16, 22, 23, 32, 45, 46, 64, 90, 128. In the case of a different value the result is undefined.
responseThreshold – threshold for the approximated laplacian, used to eliminate weak features. The larger it is, the less features will be retrieved
lineThresholdProjected – another threshold for the laplacian to eliminate edges
lineThresholdBinarized – yet another threshold for the feature size to eliminate edges. The larger the 2nd threshold, the more points you get.
CV_WRAP static Ptr
*/
//************************************RGB2HSV函数详解*********************************************//
/*
在opencv中,H的范围为0~180,S的范围是0~255,V的范围是0~255
HSV颜色空间规定的取值范围,H是0~360,S的范围是0~1,V的范围是0~1
如果需要转换从opencv的结果到HSV颜色空间,需要转换一下,H*2,S/255,V/255
*/
//==================================膨胀:dilate函数详解===============================//
/*
void dilate(InputArray src,OutputArray dst, InputArray kernel, Point anchor = Point(-1, -1), int iterations = 1, const Scalar& borderValue = morphologyDefaultBorderValue());
【必选】
InputArray src, //输入图像,图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
OutputArray dst, //输出图像,和源图片有一样的尺寸和类型
InputArray kernel, //膨胀操作的核。若为NULL时,表示的是使用参考点位于中心3x3的核。
【可选】
Point anchor = Point(-1, -1), //锚的位置,其有默认值(-1,-1),表示锚位于中心。
int iterations = 1, //迭代使用erode()函数的次数,默认值为1
int borderType = BORDER_CONSTANT, //const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。
//需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
const Scalar& borderValue = morphologyDefaultBorderValue());
*/
/*
getStructuringElement(MORPH_RECT,Size(2 * g_nStructElementSize + 1, 2 * g_nStructElementSize + 1),Point(g_nStructElementSize, g_nStructElementSize));
第一个参数:内核的形状,我们可以选择如下三种形状之一:
矩形: MORPH_RECT
交叉形: MORPH_CROSS
椭圆形: MORPH_ELLIPSE
第二个参数:内核的尺寸
第三个参数:锚点的位置
*/
//==================================腐蚀:erode函数详解===============================//
/*
void erode(InputArray src,OutputArray dst,InputArray kernel,Point anchor=Point(-1,-1),int iterations=1,int borderType=BORDER_CONSTANT,
const Scalar& borderValue=morphologyDefaultBorderValue());
第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。图像通道的数量可以是任意的,但图像深度应为CV_8U,CV_16U,CV_16S,CV_32F或 CV_64F其中之一。
第二个参数,OutputArray类型的dst,即目标图像,需要和源图片有一样的尺寸和类型。
第三个参数,InputArray类型的kernel,腐蚀操作的内核。若为NULL时,表示的是使用参考点位于中心3x3的核。我们一般使用函数 getStructuringElement配合这个参数的使用。
getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。
第四个参数,Point类型的anchor,锚的位置,其有默认值(-1,-1),表示锚位于单位(element)的中心,我们一般不用管它。
第五个参数,int类型的iterations,迭代使用erode()函数的次数,默认值为1。
第六个参数,int类型的borderType,用于推断图像外部像素的某种边界模式。注意它有默认值BORDER_DEFAULT。
第七个参数,const Scalar&类型的borderValue,当边界为常数时的边界值,有默认值morphologyDefaultBorderValue(),一般我们不用去管他。
需要用到它时,可以看官方文档中的createMorphologyFilter()函数得到更详细的解释。
*/
//================================Blob特征检测Keypoints===============================//
/*
第一步通过一系列连续的阈值把输入的灰度图像转换为一个二值图像的集合,阈值范围为[T1,T2],步长为t,则所有阈值为:
T1,T1+t,T1+2t,T1+3t,……,T2 (1)
第二步是利用Suzuki提出的算法通过检测每一幅二值图像的边界的方式提取出每一幅二值图像的连通区域,我们可以认为由边界所围成的不同的连通区域就是该二值图像的斑点;
第三步是根据所有二值图像斑点的中心坐标对二值图像斑点进行分类,从而形成灰度图像的斑点,属于一类的那些二值图像斑点最终形成灰度图像的斑点,具体来说就是,灰度图像的斑点是由中心坐标间的距离小于阈值Tb的那些二值图像斑点所组成的,即这些二值图像斑点属于该灰度图像斑点;
最后就是确定灰度图像斑点的信息——位置和尺寸。位置是属于该灰度图像斑点的所有二值图像斑点中心坐标的加权和,
权值q等于该二值图像斑点的惯性率的平方,它的含义是二值图像的斑点的形状越接近圆形,越是我们所希望的斑点,因此对灰度图像斑点位置的贡献就越大。
尺寸则是属于该灰度图像斑点的所有二值图像斑点中面积大小居中的半径长度。
PS:::::::在第二步中,并不是所有的二值图像的连通区域都可以认为是二值图像的斑点,我们往往通过一些限定条件来得到更准确的斑点。这些限定条件包括颜色,面积和形状,斑点的形状又可以用圆度,偏心率,或凸度来表示。
对于二值图像来说,只有两种斑点颜色——白色斑点和黑色斑点,我们只需要一种颜色的斑点,通过确定斑点的灰度值就可以区分出斑点的颜色。
连通区域的面积太大和太小都不是斑点,所以我们需要计算连通区域的面积,只有当该面积在我们所设定的最大面积和最小面积之间时,该连通区域才作为斑点被保留下来。
*/
//================================Keypoints类===============================//
/*
class KeyPoint
{
Point2f pt; //特征点坐标
float size; //特征点邻域直径
float angle; //特征点的方向,值为0~360,负值表示不使用
float response; //特征点的响应强度,代表了该点是特征点的程度,可以用于后续处理中特征点排序
int octave; //特征点所在的图像金字塔的组
int class_id; //用于聚类的id
}
*/
//================================drawKeypoints===============================//
//void drawKeypoints( const Mat& image, const vector
// const Scalar& color = Scalar::all(-1), int flags = DrawMatchesFlags::DEFAULT );
/*
第一个参数image:原始图像,可以使三通道或单通道图像;
第二个参数keypoints:特征点向量,向量内每一个元素是一个KeyPoint对象,包含了特征点的各种属性信息;
第三个参数outImage:特征点绘制的画布图像,可以是原图像;
第四个参数color:绘制的特征点的颜色信息,默认绘制的是随机彩色;
第五个参数flags:特征点的绘制模式,其实就是设置特征点的那些信息需要绘制,那些不需要绘制,有以下几种模式可选:
DEFAULT:只绘制特征点的坐标点,显示在图像上就是一个个小圆点,每个小圆点的圆心坐标都是特征点的坐标。
DRAW_OVER_OUTIMG:函数不创建输出的图像,而是直接在输出图像变量空间绘制,要求本身输出图像变量就是一个初始化好了的,size与type都是已经初始化好的变量
NOT_DRAW_SINGLE_POINTS:单点的特征点不被绘制
DRAW_RICH_KEYPOINTS:绘制特征点的时候绘制的是一个个带有方向的圆,这种方法同时显示图像的坐标,size,和方向,是最能显示特征的一种绘制方式。
*/
//================================计算运行时间===============================//
/*
getTickCount():用于返回从操作系统启动到当前所经的计时周期数,看名字也很好理解,get Tick Count(s)。
getTickFrequency():用于返回CPU的频率。get CPU Tick Count(s)。这里的单位是秒。
总次数/一秒内重复的次数 = 时间(s)
总次数*1000/一秒内重复的次数 = 时间(ms)
double t1 = (double)getTickCount();
double t2 = (double)getTickCount();
cout << "time:" << (t2 - t1) * 1000 / getTickFrequency() << endl;
*/