#include "ContribDemo.h"
#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2\opencv.hpp>
#include <opencv2/aruco.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/video/tracking.hpp>
#include <opencv2/img_hash.hpp>
#include <opencv2/intensity_transform.hpp>
#include <opencv2/saliency.hpp>
#include <opencv2/quality.hpp>
using namespace cv;
using namespace std;
using namespace aruco;
using namespace cv::img_hash;
using namespace cv::intensity_transform;
using namespace saliency;
ContribDemo::ContribDemo()
{
}
ContribDemo::~ContribDemo()
{
}
/* Marker检测 */
void ContribDemo::test1()
{
Mat image = imread("../x64/Debug/picture/48.jpg", 1);
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
Ptr<aruco::DetectorParameters> params = aruco::DetectorParameters::create();
params->cornerRefinementMethod = aruco::CORNER_REFINE_NONE;
Mat imageCopy;
image.copyTo(imageCopy);
vector<int> ids;
vector<vector<Point2f>> corners, rejected;
aruco::detectMarkers(image, dictionary, corners, ids, params);
//如果检测到至少一个标记
if (ids.size() > 0) {
aruco::drawDetectedMarkers(imageCopy, corners, ids);
vector< vector<Point2f>> rejectedCandidates;
imshow("test", imageCopy);
}
}
/* 姿态估计 */
void ContribDemo::test2()
{
Ptr<aruco::DetectorParameters> parameters = aruco::DetectorParameters::create();
VideoCapture captrue("../x64/Debug/video/14.mp4");
if (!captrue.isOpened()) {
return;
}
Mat img;
Mat cameraMatrix = (Mat_<float>(3, 3) << 6.0097346876749680e+02, 0., 3.2031703441387049e+02, 0.,
6.0225171288478975e+02, 2.3947860507800820e+02, 0., 0., 1.);
Mat distCoeffs = (Mat_<float>(5, 1) << -1.3711862233491057e-01, 2.7024113413198220e-01,
9.1669430855309032e-04, -3.3899628828653657e-03, 0.);
while (captrue.read(img)) {
vector<int> markerIds;
vector< vector<Point2f> > markerCorners, rejectedCandidates;
Ptr<aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(aruco::DICT_6X6_250);
aruco::detectMarkers(img, dictionary, markerCorners, markerIds, parameters, rejectedCandidates);
if (markerIds.size() > 0) {
aruco::drawDetectedMarkers(img, markerCorners, markerIds);
vector<Vec3d> rvecs, tvecs;
aruco::estimatePoseSingleMarkers(markerCorners, 0.05, cameraMatrix, distCoeffs, rvecs, tvecs);
for (int i = 0; i < markerIds.size(); i++) {
cout << tvecs[i] << endl;
}
}
imshow("ss", img);
char c = waitKey(100);
if (c == 27) {
break;
}
}
}
/**
* 人脸定位
* @param net 人脸检测网络
* @param frame 检测图像
* @param conf_threshold 阈值
* @return tuple<Mat, vector<vector<int>>> 元组容器,可返回多个值
*/
tuple<Mat, vector<vector<int>>> getFaceBox(dnn::Net net, Mat &frame, double conf_threshold)
{
Mat frameOpenCVDNN = frame.clone();
int frameHeight = frameOpenCVDNN.rows;
int frameWidth = frameOpenCVDNN.cols;
double inScaleFactor = 1.0; //缩放尺寸
Size size = Size(300, 300);
Scalar meanVal = Scalar(104, 117, 123);
Mat inputBlob;
inputBlob = dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, size, meanVal, true, false);
net.setInput(inputBlob, "data");
Mat detection = net.forward("detection_out"); //四维矩阵输出
Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>()); //提取结果信息
vector<vector<int>> bboxes;
for (int i = 0; i < detectionMat.rows; i++) {
float confidence = detectionMat.at<float>(i, 2); //预测概率
if (confidence > conf_threshold) {
//左上角点,坐标被归一化
int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frameWidth);
int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frameHeight);
//右下角角点,坐标被归一化
int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frameWidth);
int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frameHeight);
vector<int> box = { x1, y1, x2, y2 };
bboxes.push_back(box); //人脸坐标
rectangle(frameOpenCVDNN, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2, 4); //图像框选
}
}
return make_tuple(frameOpenCVDNN, bboxes);
}
/* dnn年纪性别识别 */
void ContribDemo::test3()
{
//人脸模型
string faceProto = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector.pbtxt";
string faceModel = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector_uint8.pb";
//年龄模型
string ageProto = "E:/opencv/sources/samples/dnn/face_detector/age_deploy.prototxt";
string ageModel = "E:/opencv/sources/samples/dnn/face_detector/age_net.caffemodel";
//性别模型
string genderProto = "E:/opencv/sources/samples/dnn/face_detector/gender_deploy.prototxt";
string genderModel = "E:/opencv/sources/samples/dnn/face_detector/gender_net.caffemodel";
//均值
Scalar MODEL_MEAN_VALUES = Scalar(78.4263377603, 87.7689143744, 114.895847746);
//年龄段标签
vector<string> ageList = {"(0-2)", "(4-6)", "(8-12)", "(15-20)", "(25-32)", "(38-43)", "(48-53)", "(60-100)"};
//性别标签
vector<string> genderList = {"Man", "Waman"};
//导入网络
dnn::Net ageNet = dnn::readNet(ageProto, ageModel);
dnn::Net genderNet = dnn::readNet(genderProto, genderModel);
dnn::Net faceNet = dnn::readNetFromTensorflow(faceModel, faceProto);
VideoCapture cap;
cap.open("../x64/Debug/video/11.mp4");
int padding = 20;
while (waitKey(1) < 0) {
Mat frame;
cap.read(frame);
if (frame.empty()) {
waitKey();
break;
}
vector<vector<int>> bboxes; //人脸坐标
Mat frameFace; //人脸检测结果图
tie(frameFace, bboxes) = getFaceBox(faceNet, frame, 0.7); //人脸定位,tie()函数解包frameFace和bboxes
if (bboxes.size() == 0) { //人脸判断
cout << "No face detected, checking next frame." << endl;
continue;
}
//逐个提取人脸检测
for (auto it = begin(bboxes); it != end(bboxes); ++it) {
Rect rec(it->at(0) - padding, it->at(1) - padding, it->at(2) - it->at(0) + 2 * padding, it->at(3) - it->at(1) + 2 * padding); //框选人脸
rec.width = ((rec.x + rec.width) > frame.cols) ? (frame.cols - rec.x - 1) : rec.width; //避免人脸框选超过图像边缘
rec.height = ((rec.y + rec.height) > frame.rows) ? (frame.rows - rec.y - 1) : rec.height;
Mat face = frame(rec); //原图中提取人脸
//性别检测
Mat blob;
blob = dnn::blobFromImage(face, 1, Size(227, 227), MODEL_MEAN_VALUES, false);
genderNet.setInput(blob);
vector<float> genderPreds = genderNet.forward(); //获取前向传播softmax结果
int iMaxGender = std::distance(genderPreds.begin(), max_element(genderPreds.begin(), genderPreds.end())); //返回最大值和第一个值下标的距离
string gender = genderList[iMaxGender]; //获得检测结果
cout << "Gender: " << gender << endl;
//年龄识别
ageNet.setInput(blob);
vector<float> agePreds = ageNet.forward();
int iMaxAge = std::distance(agePreds.begin(), max_element(agePreds.begin(), agePreds.end())); //找到年龄预测最大下表
string age = ageList[iMaxAge];
cout << "Age: " << age << endl;
string label = gender + ", " + age; //label输出标签
putText(frameFace, label, Point(it->at(0), it->at(1) - 15), cv::FONT_HERSHEY_SIMPLEX, 0.9, Scalar(0, 255, 255), 2, cv::LINE_AA); //在人脸定位图上显示结果
}
imshow("Frame", frameFace);
}
}
/*人脸检测haar级联
* @param frame 原图
* @param faceCascadePath 模型文件
*/
Mat detectFaceHaar(Mat frame, string faceCascadePath)
{
//图像缩放
auto inHeight = 300;
auto inWidth = 0;
if (!inWidth) {
inWidth = (int)(((float)frame.cols / (float)frame.rows) * inHeight);
}
resize(frame, frame, Size(inWidth, inHeight));
//转换为灰度图
Mat frameGray = frame.clone();
cvtColor(frame, frameGray, COLOR_BGR2GRAY);
//级联分类器
CascadeClassifier faceCascade;
faceCascade.load(faceCascadePath);
std::vector<Rect> faces;
faceCascade.detectMultiScale(frameGray, faces);
for (size_t i = 0; i < faces.size(); i++) {
int x1 = faces[i].x;
int y1 = faces[i].y;
int x2 = faces[i].x + faces[i].width;
int y2 = faces[i].y + faces[i].height;
Rect face_rect(Point2i(x1, y1), Point2i(x2, y2));
rectangle(frame, face_rect, Scalar(0, 255, 0), 2, 4);
}
return frame;
}
/* 人脸检测
* @param frame 原图
* @param configFile 模型结构定义文件
* @param weightFile 模型文件 */
Mat detectFaceOpenCVDNN(Mat frame, string configFile, string weightFile)
{
const size_t inWidth = 300;
const size_t inHeight = 300;
const double inScaleFactor = 1.0; //缩放比例
const double confidenceThreshold = 0.7; //阈值
const Scalar meanVal(104.0, 177.0, 123.0); //均值
Mat frameOpenCVDNN = frame.clone();
dnn::Net net;
Mat inputBlob;
int frameHeight = frameOpenCVDNN.rows;
int frameWidth = frameOpenCVDNN.cols;
string suffixStr = configFile.substr(configFile.find_last_of('.') + 1); //获取文件后缀
if (suffixStr == "prototxt") { //判断是caffe模型还是tensorflow模型
net = dnn::readNetFromCaffe(configFile, weightFile);
inputBlob = dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, Size(inWidth, inHeight), meanVal, false, false);
}
else {
net = dnn::readNet(configFile, weightFile);
inputBlob = dnn::blobFromImage(frameOpenCVDNN, inScaleFactor, Size(inWidth, inHeight), meanVal, true, false);
}
//读图检测
net.setInput(inputBlob, "data");
Mat detection = net.forward("detection_out");
Mat detectionMat(detection.size[2], detection.size[3], CV_32F, detection.ptr<float>());
for (int i = 0; i < detectionMat.rows; i++) { //分类精度
float confidence = detectionMat.at<float>(i, 2);
if (confidence > confidenceThreshold) {
//左上角坐标
int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frameWidth);
int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frameHeight);
//右下角坐标
int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frameWidth);
int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frameHeight);
rectangle(frameOpenCVDNN, Point(x1, y1), Point(x2, y2), Scalar(0, 255, 0), 2, 4);
}
}
return frameOpenCVDNN;
}
/* 三种找脸方法对比 */
void ContribDemo::test4()
{
string strFace = "E:/opencv/sources/data/haarcascades/haarcascade_frontalface_default.xml";
string strCaffe = "E:/opencv/sources/samples/dnn/face_detector/deploy.prototxt";
string strWeight = "E:/opencv/sources/samples/dnn/face_detector/res10_300x300_ssd_iter_140000_fp16.caffemodel";
string strConfig = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector.pbtxt";
string strTensorWeight = "E:/opencv/sources/samples/dnn/face_detector/opencv_face_detector_uint8.pb";
Mat frame = imread("../x64/Debug/picture/test1/1404.png");
Mat maHarr = detectFaceHaar(frame, strFace);
imshow("111", maHarr);
Mat maOpenCVCaffe = detectFaceOpenCVDNN(frame, strCaffe, strWeight);
imshow("222", maOpenCVCaffe);
Mat maOpenCVTf = detectFaceOpenCVDNN(frame, strConfig, strTensorWeight);
imshow("333", maOpenCVTf);
}
/* dnn文本检测 */
void ContribDemo::test5()
{
auto inpWidth = 320;
auto inpHeight = 320;
auto confThreshold = 0.8; //置信度阈值
auto nmsThreshold = 0.2; //非极大值抑制算法阈值
string strModel = "E:/opencv/sources/samples/dnn/face_detector/frozen_east_text_detection.pb";
dnn::Net net = dnn::readNet(strModel); //读取模型
Mat srcImg = imread("../x64/Debug/picture/52.png");
vector<Mat> output;
vector<String> outputLayers(2);
outputLayers[0] = "feature_fusion/Conv_7/Sigmoid";
outputLayers[1] = "feature_fusion/concat_3";
//检测图像
Mat frame, blob;
frame = srcImg.clone();
dnn::blobFromImage(frame, blob, 1.0, Size(inpWidth, inpHeight), Scalar(123.68, 116.78, 103.94), true, false); //获取深度学习模型的输入
net.setInput(blob);
net.forward(output, outputLayers); //输出结果
Mat scores = output[0]; //置信度
Mat geometry = output[1]; //位置参数
//对检测框进行解码,获取文本框位置方向
vector<RotatedRect> boxes; //文本框位置参数
vector<float> confidences; //文本框置信度
boxes.clear();
//判断是不是符合提取要求
CV_Assert(scores.dims == 4);
CV_Assert(geometry.dims == 4);
CV_Assert(scores.size[0] == 1);
CV_Assert(geometry.size[0] == 1);
CV_Assert(scores.size[1] == 1);
CV_Assert(geometry.size[1] == 5);
CV_Assert(scores.size[2] == geometry.size[2]);
CV_Assert(scores.size[3] == geometry.size[3]);
const int height = scores.size[2];
const int width = scores.size[3];
for (int y = 0; y < height; ++y) {
const float *scoresData = scores.ptr<float>(0, 0, y); //识别概率
const float *x0_data = geometry.ptr<float>(0, 0, y); //文本框坐标
const float *x1_data = geometry.ptr<float>(0, 1, y);
const float *x2_data = geometry.ptr<float>(0, 2, y);
const float *x3_data = geometry.ptr<float>(0, 3, y);
const float *anglesData = geometry.ptr<float>(0, 4, y); //文本框角度
for (int x = 0; x < width; ++x) { //遍历所有检测到的检测框
float score = scoresData[x];
if (score < confThreshold) { //低于阈值忽略该检测框
continue;
}
//解码预测。乘以4,因为特征图比输入图像少4倍
float offsetX = x * 4.0f, offsetY = y * 4.0f;
//角度及相关正余弦计算
float angle = anglesData[x];
float cosA = cos(angle);
float sinA = sin(angle);
float h = x0_data[x] + x2_data[x];
float w = x1_data[x] + x3_data[x];
Point2f offset(offsetX + cosA * x1_data[x] + sinA * x2_data[x], offsetY - sinA * x1_data[x] + cosA * x2_data[x]);
Point2f p1 = Point2f(-sinA * h, -cosA * h) + offset;
Point2f p3 = Point2f(-cosA * w, sinA * w) + offset;
RotatedRect r(0.5f * (p1 + p3), Size2f(w, h), -angle * 180.0f / (float)CV_PI); //旋转矩形,分别输入中心点坐标,图像宽高,角度
boxes.push_back(r); //保存检测框
confidences.push_back(score); //保存检测框的置信度
}
}
vector<int> indices; //符合要求的文本框
dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); //应用非极大性抑制算法
//输出预测
Point2f ratio((float)frame.cols / inpWidth, (float)frame.rows / inpHeight); //缩放比例
for (size_t i = 0; i < indices.size(); ++i) {
RotatedRect &box = boxes[indices[i]];
Point2f vertices[4];
box.points(vertices);
for (int j = 0; j < 4; ++j) { //还原坐标点
vertices[j].x *= ratio.x;
vertices[j].y *= ratio.y;
}
for (int j = 0; j < 4; ++j) { //画框
line(frame, vertices[j], vertices[(j + 1) % 4], Scalar(0, 255, 0), 2, LINE_AA);
}
}
//投放效率信息,时间
vector<double> layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
string label = format("Inference time: %.2f ms", t);
putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
imshow("result", frame);
}
vector<string> classes1; //类别参数
/** 画框,classId 类别,conf 置信度 */
void drawPred(int classId, float conf, int left, int top, int right, int bottom, Mat& frame)
{
rectangle(frame, Point(left, top), Point(right, bottom), Scalar(255, 178, 50), 3);
//获取类名及其置信度的标签
string label = format("%.2f", conf);
if (!classes1.empty()) {
CV_Assert(classId < (int)classes1.size());
label = classes1[classId] + ":" + label;
}
//在每个框左上角标上标签
int baseLine;
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);
top = max(top, labelSize.height);
rectangle(frame, Point(left, top - round(1.5*labelSize.height)), Point(left + round(1.5*labelSize.width), top + baseLine), Scalar(255,255,255), FILLED);
putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 0), 1);
}
/**基于非极大性抑制去除边框,frame 视频图像,outs 输出层结果 */
void postprocess(Mat& frame, const vector<Mat>& outs)
{
float confThreshold = 0.5; //Confidence threshold 置信度阈值
float nmsThreshold = 0.4; //非极大性抑制阈值
vector<int> classIds; //输出类
vector<float> confidences; //置信度
vector<Rect> boxes;
for (size_t i = 0; i < outs.size(); ++i) { //遍历所有的输出层
//扫描所有来自网络的边界框输出,只保留具有高置信度分数的边界框。将框的类标签指定为框得分最高的类。
float* data = (float*)outs[i].data; //读取框
for (int j = 0; j < outs[i].rows; ++j, data += outs[i].cols) {
Mat scores = outs[i].row(j).colRange(5, outs[i].cols);
Point classIdPoint;
double confidence;
minMaxLoc(scores, 0, &confidence, 0, &classIdPoint); //获取置信度和位置参数
if (confidence > confThreshold) { //如果大于置信度阈值
//获取坐标
int centerX = (int)(data[0] * frame.cols);
int centerY = (int)(data[1] * frame.rows);
int width = (int)(data[2] * frame.cols);
int height = (int)(data[3] * frame.rows);
int left = centerX - width / 2;
int top = centerY - height / 2;
classIds.push_back(classIdPoint.x);
confidences.push_back((float)confidence);
boxes.push_back(Rect(left, top, width, height));
}
}
}
vector<int> indices; //输出非极大性抑制结果,按置信度从大到小输出
dnn::NMSBoxes(boxes, confidences, confThreshold, nmsThreshold, indices); //非极大性抑制
for (size_t i = 0; i < indices.size(); ++i) { //绘图
int idx = indices[i];
Rect box = boxes[idx];
drawPred(classIds[idx], confidences[idx], box.x, box.y, box.x + box.width, box.y + box.height, frame);
}
}
/* 获取输出层 */
vector<String> getOutputsNames(const dnn::Net& net)
{
vector<String> names;
if (names.empty()) {
vector<int> outLayers = net.getUnconnectedOutLayers(); //获取输出层的索引,即具有未连接输出的层
vector<String> layersNames = net.getLayerNames(); //获取网络中所有层的名称
names.resize(outLayers.size()); // 在名称中获取输出层的名称
for (size_t i = 0; i < outLayers.size(); ++i) {
names[i] = layersNames[outLayers[i] - 1];
}
}
return names;
}
/* 使用YOLOv3和OpenCV进行基于深度学习的目标检测(识别物体) */
void ContribDemo::test6()
{
int inpWidth = 416; //检测图像宽高
int inpHeight = 416;
//模型参数文件
String modelConfiguration = "E:/opencv/sources/data/haarcascades/yolov3.cfg";
String modelWeights = "E:/opencv/sources/data/haarcascades/yolov3.weights";
//读取分类类名
string classesFile = "E:/opencv/sources/data/haarcascades/coco.names";
ifstream ifs(classesFile.c_str());
string line;
while (getline(ifs, line)) {
classes1.push_back(line);
}
//导入网络
dnn::Net net = dnn::readNetFromDarknet(modelConfiguration, modelWeights);
net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(dnn::DNN_TARGET_CPU); //仅仅使用CPU
Mat blob;
clock_t start, finish;
Mat frame = imread("../x64/Debug/picture/53.jpg");
start = clock();
dnn::blobFromImage(frame, blob, 1 / 255.0, Size(inpWidth, inpHeight), Scalar(0, 0, 0), true, false); //创建神经网络输入图像
net.setInput(blob); //设置输出
vector<Mat> outs;
net.forward(outs, getOutputsNames(net)); //获取输出层结果
postprocess(frame, outs); //删除置信度低的边界框
finish = clock();
cout << "time is " << double(finish - start) / CLOCKS_PER_SEC << endl;
//输出前向传播的时间
vector<double> layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
string label = format("Inference time for a frame : %.2f ms", t);
putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 0, 255));
imshow("result", frame);
}
/* 进行手部关键点检测 */
void ContribDemo::test7()
{
//各个部位连接线坐标,比如(0,1)表示第0特征点和第1特征点连接线为拇指
const int POSE_PAIRS[20][2] = {
{0,1}, {1,2}, {2,3}, {3,4}, //拇指
{0,5}, {5,6}, {6,7}, {7,8}, //索引
{0,9}, {9,10}, {10,11}, {11,12}, //中间
{0,13}, {13,14}, {14,15}, {15,16}, //环
{0,17}, {17,18}, {18,19}, {19,20} //小型
};
int nPoints = 21;
string protoFile = "E:/opencv/sources/samples/dnn/face_detector/pose_deploy.prototxt"; //模型文件位置
string weightsFile = "E:/opencv/sources/samples/dnn/face_detector/pose_iter_102000.caffemodel";
string imageFile = "../x64/Debug/picture/56.jpg";
Mat frame = imread(imageFile);
Mat frameCopy = frame.clone();
int frameWidth = frame.cols;
int frameHeight = frame.rows;
float thresh = 0.01;
float aspect_ratio = frameWidth / (float)frameHeight; //原图宽高比
int inHeight = 368;
int inWidth = (int(aspect_ratio*inHeight) * 8) / 8; //缩放图像
double t = (double)getTickCount();
//调用caffe模型
dnn::Net net = dnn::readNetFromCaffe(protoFile, weightsFile);
Mat inpBlob = dnn::blobFromImage(frame, 1.0 / 255, Size(inWidth, inHeight), Scalar(0, 0, 0), false, false);
net.setInput(inpBlob);
Mat output = net.forward();
int H = output.size[2];
int W = output.size[3];
//找到各点的位置
vector<Point> points(nPoints);
for (int n = 0; n < nPoints; n++) {
Mat probMap(H, W, CV_32F, output.ptr(0, n)); //第一个特征点的预测矩阵
resize(probMap, probMap, Size(frameWidth, frameHeight)); //放大预测矩阵
Point maxLoc;
double prob;
minMaxLoc(probMap, 0, &prob, 0, &maxLoc); //寻找预测矩阵,最大值概率以及最大值的坐标位置
if (prob > thresh) {
circle(frameCopy, Point((int)maxLoc.x, (int)maxLoc.y), 8, Scalar(0, 255, 255), -1); //画图
putText(frameCopy, format("%d", n), Point((int)maxLoc.x, (int)maxLoc.y), FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2);
}
points[n] = maxLoc; //保存特征点的坐标
}
int nPairs = sizeof(POSE_PAIRS) / sizeof(POSE_PAIRS[0]); //获取要画的骨架线个数
for (int n = 0; n < nPairs; n++) { //连接点,画骨架
//查找2个连接的身体/手部
Point2f partA = points[POSE_PAIRS[n][0]];
Point2f partB = points[POSE_PAIRS[n][1]];
if (partA.x <= 0 || partA.y <= 0 || partB.x <= 0 || partB.y <= 0) {
continue;
}
//画骨条线
line(frame, partA, partB, Scalar(0, 255, 255), 8);
circle(frame, partA, 8, Scalar(0, 0, 255), -1);
circle(frame, partB, 8, Scalar(0, 0, 255), -1);
}
//计算运行时间
t = ((double)getTickCount() - t) / getTickFrequency();
cout << "Time Taken = " << t << endl;
imshow("Output-Keypoints", frameCopy);
imshow("Output-Skeleton", frame);
}
vector<string> classes;
vector<Scalar> colors;
/* 绘制预测的边界框,着色并在图像上显示遮罩 */
void drawBox(Mat &frame, int classId, float conf, Rect box, Mat &objectMask)
{
float maskThreshold = 0.3; //掩模阈值
rectangle(frame, Point(box.x, box.y), Point(box.x + box.width, box.y + box.height), Scalar(255, 178, 50), 3); //画预测框
string label = format("%.2f", conf); //置信度获取
if (!classes.empty()) { //获取标签
CV_Assert(classId < (int)classes.size());
label = classes[classId] + ":" + label;
}
int baseLine; //在边界框顶部显示标签
Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine); //标签,字体,文本大小的倍数,文本粗细,文本最低点对应的纵坐标
box.y = max(box.y, labelSize.height);
//画框打标签
rectangle(frame, Point(box.x, box.y - round(1.5 * labelSize.height)), Point(box.x + round(1.5 * labelSize.width), box.y + baseLine), Scalar(255, 255, 255), FILLED);
putText(frame, label, Point(box.x, box.y), FONT_HERSHEY_SIMPLEX, 0.75, Scalar(0, 0, 0), 1);
Scalar color = colors[classId % colors.size()]; //填充颜色
resize(objectMask, objectMask, Size(box.width, box.height)); //重置大小
Mat mask = (objectMask > maskThreshold);
Mat coloredRoi = (0.3 * color + 0.7 * frame(box)); //叠加获得颜色掩模
coloredRoi.convertTo(coloredRoi, CV_8UC3);
//画轮廓
vector<Mat> contours;
Mat hierarchy;
mask.convertTo(mask, CV_8U);
findContours(mask, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
drawContours(coloredRoi, contours, -1, color, 5, LINE_8, hierarchy, 100);
coloredRoi.copyTo(frame(box), mask);
}
/* 提取每张图像的预测框和掩模 */
void postprocess1(Mat &frame, const vector<Mat> &outs)
{
float confThreshold = 0.5; //置信度阈值
Mat outDetections = outs[0]; //预测框结果
Mat outMasks = outs[1]; //掩模结果
const int numDetections = outDetections.size[2]; //预测的框个数
const int numClasses = outMasks.size[1]; //类别数
outDetections = outDetections.reshape(1, outDetections.total() / 7);
for (int i = 0; i < numDetections; ++i) { //筛选预测框数
float score = outDetections.at<float>(i, 2); //提取预测框置信度
if (score > confThreshold) { //超过阈值
//类别
int classId = static_cast<int>(outDetections.at<float>(i, 1));
int left = static_cast<int>(frame.cols * outDetections.at<float>(i, 3));
int top = static_cast<int>(frame.rows * outDetections.at<float>(i, 4));
int right = static_cast<int>(frame.cols * outDetections.at<float>(i, 5));
int bottom = static_cast<int>(frame.rows * outDetections.at<float>(i, 6));
//防止框画在外面
left = max(0, min(left, frame.cols - 1));
top = max(0, min(top, frame.rows - 1));
right = max(0, min(right, frame.cols - 1));
bottom = max(0, min(bottom, frame.rows - 1));
Rect box = Rect(left, top, right - left + 1, bottom - top + 1);
Mat objectMask(outMasks.size[2], outMasks.size[3], CV_32F, outMasks.ptr<float>(i, classId)); //提取掩模
drawBox(frame, classId, score, box, objectMask); //绘制边界框,着色并在图像上显示遮罩
}
}
}
/* 使用Mask R-CNN进行对象检测和实例分割 */
void ContribDemo::test8()
{
//0-image,1-video,2-camera
int read_file = 1;
//导入分类名文件
string classesFile = "../x64/Debug/xml/mscoco_labels.names";
ifstream ifs(classesFile.c_str());
string line;
while (getline(ifs, line)) {
classes.push_back(line);
}
//导入颜色类文件
string colorsFile = "../x64/Debug/xml/colors.txt";
ifstream colorFptr(colorsFile.c_str());
while (getline(colorFptr, line)) {
char *pEnd;
double r, g, b;
//字符串转换成浮点数
r = strtod(line.c_str(), &pEnd);
g = strtod(pEnd, NULL);
b = strtod(pEnd, NULL);
Scalar color = Scalar(r, g, b, 255.0);
colors.push_back(Scalar(r, g, b, 255.0));
}
//给出模型的配置和重量文件
String textGraph = "E:/opencv/sources/samples/dnn/face_detector/mask_rcnn_inception_v2_coco.pbtxt";
String modelWeights = "E:/opencv/sources/samples/dnn/face_detector/frozen_inference_graph.pb";
//导入网络
dnn::Net net = dnn::readNetFromTensorflow(modelWeights, textGraph);
net.setPreferableBackend(dnn::DNN_BACKEND_OPENCV);
net.setPreferableTarget(dnn::DNN_TARGET_CPU); //只使用CPU
//打开视频文件、图像文件或相机流
string str;
VideoCapture cap;
VideoWriter video;
Mat frame, blob;
//输出文件,默认是视频
if (read_file == 0) {
str = "../x64/Debug/picture/57.jpg";
ifstream ifile(str);
if (!ifile) {
throw("error");
}
frame = imread(str);
}
else if (read_file == 1) {
str = "../x64/Debug/video/17.mp4";
ifstream ifile(str);
if (!ifile) {
throw("error");
}
cap.open(str);
}
else { //打开摄像头
cap.open(0);
}
//显示窗口
static const string kWinName = "object detection";
while (waitKey(1) < 0) {
//如果是视频
if (read_file != 0) {
cap >> frame;
}
//如果图像不存在
if (frame.empty()) {
cout << "Done processing !!!" << endl;
waitKey(0);
break;
}
//获得深度学习的输入图像
dnn::blobFromImage(frame, blob, 1.0, Size(frame.cols, frame.rows), Scalar(), true, false);
net.setInput(blob); //设置输入
//获得输出层
std::vector<String> outNames(2);
outNames[0] = "detection_out_final";
outNames[1] = "detection_masks";
vector<Mat> outs;
net.forward(outs, outNames);
//提取预测框和掩模
postprocess1(frame, outs);
//输入效率信息。函数getPerfProfile返回推理的总时间(t)和每个层的时间(以layersTimes为单位)
vector<double> layersTimes;
double freq = getTickFrequency() / 1000;
double t = net.getPerfProfile(layersTimes) / freq;
string label = format("Mask-RCNN Inference time for a frame : %0.0f ms", t);
putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255, 255, 0));
//保存结果
Mat detectedFrame;
frame.convertTo(detectedFrame, CV_8U);
imshow(kWinName, frame);
if (waitKey(10) == 27) {
break;
}
}
cap.release();
//释放生成的视频
if (read_file != 0) {
video.release();
}
}
/* 实现选择性搜索算法 */
void ContribDemo::test9()
{
setUseOptimized(true); //使用多线程,开启CPU的硬件指令优化功能
setNumThreads(4);
Mat im = imread("../x64/Debug/picture/dogs.jpg");
int newHeight = 200;
int newWidth = im.cols*newHeight / im.rows;
resize(im, im, Size(newWidth, newHeight));
Ptr<ximgproc::segmentation::SelectiveSearchSegmentation> ss = ximgproc::segmentation::createSelectiveSearchSegmentation(); //默认参数生成选择性搜索类
ss->setBaseImage(im); //要进行分割的图像
//ss->switchToSelectiveSearchFast(); //快速搜索(速度快,召回率低)
ss->switchToSelectiveSearchQuality(); //精准搜索(速度慢,召回率高)
std::vector<Rect> rects;
ss->process(rects); //保存搜索到的框,按可能性从高到低排名
std::cout << "Total Number of Region Proposals: " << rects.size() << std::endl;
int numShowRects = 10; //在图像中保存多少框
Mat imOut = im.clone(); //做一份图像图像拷贝
for (int i = 0; i < numShowRects; i++) { //画框前numShowRects个
rectangle(imOut, rects[i], Scalar(0, 255, 0));
}
imshow("Output", imOut);
}
/* 实现红眼去除 */
void ContribDemo::test10()
{
Mat img = imread("../x64/Debug/picture/red_eyes.jpg", IMREAD_COLOR);
Mat imgOut = img.clone();
imshow("Red Eyes", img);
vector<Rect> eyes;
CascadeClassifier eyesCascade("E:/opencv/sources/data/haarcascades/haarcascade_eye.xml");
eyesCascade.detectMultiScale(img, eyes, 1.3, 4, 0 | CASCADE_SCALE_IMAGE, Size(100, 100));
for (size_t i = 0; i < eyes.size(); i++) { //每只眼睛都进行处理
Mat eye = img(eyes[i]); //提取眼睛图像
imshow("Eyes0", eye);
vector<Mat>bgr(3);
split(eye, bgr); //颜色分离
Mat mask = (bgr[2] > 150) & (bgr[2] > (bgr[1] + bgr[0])); //红眼检测器,获得结果掩模
Mat maskFloodfill = mask.clone();
imshow("Eyes1", mask);
floodFill(maskFloodfill, Point(0, 0), Scalar(255)); //漫水填充
imshow("Eyes2", maskFloodfill);
Mat mask2;
bitwise_not(maskFloodfill, mask2); //反色
imshow("Eyes3", mask2);
mask = (mask2 | mask); //或运算
imshow("Eyes4", mask);
dilate(mask, mask, Mat(), Point(-1, -1), 3, 1, 1); //扩充孔洞
imshow("Eyes5", mask);
Mat mean = (bgr[0] + bgr[1]) / 2; //计算b通道和g通道的均值
imshow("Eyes6", mean);
mean.copyTo(bgr[2], mask); //用该均值图像覆盖原图掩模部分图像
mean.copyTo(bgr[0], mask);
mean.copyTo(bgr[1], mask);
Mat eyeOut;
merge(bgr, eyeOut); //图像合并
imshow("Eyes7", eyeOut);
eyeOut.copyTo(imgOut(eyes[i])); //眼部图像替换
}
imshow("Red Eyes Removed", imgOut);
}
/* 图像孔洞填充 */
void ContribDemo::test11()
{
Mat im_in = imread("../x64/Debug/picture/nickel.jpg", IMREAD_GRAYSCALE);
Mat im_th;
threshold(im_in, im_th, 220, 255, THRESH_BINARY_INV); //阈值分割
Mat im_floodfill = im_th.clone();
floodFill(im_floodfill, cv::Point(0,0), Scalar(255)); //(0, 0) 以点(0,0)为种子点,进行漫水填充
Mat im_floodfill_inv;
bitwise_not(im_floodfill, im_floodfill_inv); //反转图像
Mat im_out = (im_th | im_floodfill_inv); //获得前景
imshow("Thresholded Image", im_th);
imshow("Floodfilled Image", im_floodfill);
imshow("Inverted Floodfilled Image", im_floodfill_inv);
imshow("Foreground", im_out);
}
/* 生成字典图像 */
void ContribDemo::test12()
{
Mat markerImage;
Ptr<cv::aruco::Dictionary> dictionary = aruco::getPredefinedDictionary(cv::aruco::DICT_6X6_100); // 生成字典
aruco::drawMarker(dictionary, 33, 200, markerImage, 1); //生成图像,参数分别为字典,第几个标识,图像输出大小为200X200,输出图像,标记边框的宽度
imshow("cow1.jpg", markerImage);
}
/* ArUco标记的增强现实 */
void ContribDemo::test13()
{
//取边框放在新图或者视频上
string str;
VideoCapture cap;
VideoWriter video;
Mat frame, blob;
Mat im_src = imread("../x64/Debug/picture/new_scenery.jpg"); // 新场景图像
String detectPath = "../x64/Debug/video/test13.mp4";
try {
str = detectPath;
ifstream ifile(str);
if (!ifile) {
throw("error");
}
cap.open(str);
}
catch (...) {
cout << "Could not open the input image/video stream" << endl;
return;
}
namedWindow("Augmented", WINDOW_NORMAL);
while (waitKey(100) < 0) {
cap >> frame;
if (frame.empty()) { // 如果到了视频的结尾
cout << "Done processing !!!" << endl;
waitKey(3000);
break;
}
vector<int> markerIds;
Ptr<Dictionary> dictionary = getPredefinedDictionary(DICT_6X6_250); //加载用于标记的词典
vector<vector<Point2f>> markerCorners, rejectedCandidates; //声明标记到的角点和没有被标记到的角点
Ptr<DetectorParameters> parameters = DetectorParameters::create(); //使用默认值初始化检测器参数
detectMarkers(frame, dictionary, markerCorners, markerIds, parameters, rejectedCandidates); //检测标记
if (markerCorners.empty()) {
break;
}
vector<Point> pts_dst; //使用检测到的标记,在目标帧上定位要显示新场景的四边形
float scalingFac = 0.02; //计算缩减距离
Point refPt1, refPt2, refPt3, refPt4;
auto it = std::find(markerIds.begin(), markerIds.end(), 25); //寻找目标四边形的左上角点,查找字典中id为25的标志,返回一个vector
int index = std::distance(markerIds.begin(), it); //返回markerIds中25的下标
refPt1 = markerCorners.at(index).at(1); //返回markerIds中25的左上角坐标
it = std::find(markerIds.begin(), markerIds.end(), 33); //求目标四边形的右上角点,查找字典中id为33的标志,返回一个vector
index = std::distance(markerIds.begin(), it); //返回markerIds中33的下标
refPt2 = markerCorners.at(index).at(2); //返回markerIds中33的右上角坐标
float distance = norm(refPt1 - refPt2); //返回欧式距离
pts_dst.push_back(Point(refPt1.x - round(scalingFac * distance), refPt1.y - round(scalingFac * distance))); //将缩减后的坐标放入标记点容器
pts_dst.push_back(Point(refPt2.x + round(scalingFac * distance), refPt2.y - round(scalingFac * distance)));
// 求目标四边形的右下角点
it = std::find(markerIds.begin(), markerIds.end(), 30);
index = std::distance(markerIds.begin(), it);
refPt3 = markerCorners.at(index).at(0);
pts_dst.push_back(Point(refPt3.x + round(scalingFac * distance), refPt3.y + round(scalingFac * distance)));
// 寻找目标四边形的左下角点
it = std::find(markerIds.begin(), markerIds.end(), 23);
index = std::distance(markerIds.begin(), it);
refPt4 = markerCorners.at(index).at(0);
pts_dst.push_back(Point(refPt4.x - round(scalingFac * distance), refPt4.y + round(scalingFac * distance)));
// 全新图像的角点,从左上角开始顺时针存入pts_src中
vector<Point> pts_src;
pts_src.push_back(Point(0, 0));
pts_src.push_back(Point(im_src.cols, 0));
pts_src.push_back(Point(im_src.cols, im_src.rows));
pts_src.push_back(Point(0, im_src.rows));
Mat h = cv::findHomography(pts_src, pts_dst); //计算单应性矩阵
Mat warpedImage; //仿射变换后的图像
warpPerspective(im_src, warpedImage, h, frame.size(), INTER_CUBIC); //基于单应性矩阵映射图像
Mat mask = Mat::zeros(frame.rows, frame.cols, CV_8UC1); //准备一个表示要从仿射图像图像复制到原始帧中的区域的遮罩
fillConvexPoly(mask, pts_dst, Scalar(255, 255, 255), LINE_AA); //计算单应性矩阵
Mat element = getStructuringElement(MORPH_RECT, Size(5, 5)); //侵蚀mask以不复制仿射图像的边界效果
erode(mask, mask, element);
//将仿射的图像复制到遮罩区域中的原始帧中
Mat imOut = frame.clone();
warpedImage.copyTo(imOut, mask);
Mat concatenatedOutput;
hconcat(frame, imOut, concatenatedOutput); //并排显示原始图像和新输出图像
imshow("result", concatenatedOutput);
imshow("111", frame);
}
cap.release();
video.release();
}
template <typename T>
inline void test_one(const std::string &title, const Mat &a, const Mat &b)
{
cout << "=== " << title << " ===" << endl;
TickMeter tick;
Mat hashA, hashB;
Ptr<ImgHashBase> func;
func = T::create();
tick.reset();
tick.start();
func->compute(a, hashA); //计算图a的哈希值
tick.stop();
cout << "compute1: " << tick.getTimeMilli() << " ms" << endl;
tick.reset();
tick.start();
func->compute(b, hashB); //计算图b的哈希值
tick.stop();
cout << "compute2: " << tick.getTimeMilli() << " ms" << endl;
cout << "compare: " << func->compare(hashA, hashB) << endl << endl; //比较两张图像哈希值的距离
}
/* 实现图像哈希算法 */
void ContribDemo::test14()
{
Mat input = imread("../x64/Debug/picture/11.jpg");
Mat target = imread("../x64/Debug/picture/15.jpg");
// 通过不同方法比较图像相似性
test_one<AverageHash>("AverageHash", input, target);
test_one<PHash>("PHash", input, target);
test_one<MarrHildrethHash>("MarrHildrethHash", input, target);
test_one<RadialVarianceHash>("RadialVarianceHash", input, target);
test_one<BlockMeanHash>("BlockMeanHash", input, target);
test_one<ColorMomentHash>("ColorMomentHash", input, target);
}
// 计算对比度
double rmsContrast(Mat srcImg)
{
Mat dstImg, dstImg_mean, dstImg_std; //灰度化
cvtColor(srcImg, dstImg, COLOR_BGR2GRAY); //计算图像均值和方差
meanStdDev(dstImg, dstImg_mean, dstImg_std); //获得图像对比度
double contrast = dstImg_std.at<double>(0, 0);
return contrast;
}
namespace
{
Mat g_image; //gamma变换变量
int g_gamma = 40;
const int g_gammaMax = 500;
Mat g_imgGamma;
const std::string g_gammaWinName = "Gamma Correction";
//对比度拉伸
Mat g_contrastStretch;
int g_r1 = 70;
int g_s1 = 15;
int g_r2 = 120;
int g_s2 = 240;
const std::string g_contrastWinName = "Contrast Stretching";
//创建gamma变换滑动条
static void onTrackbarGamma(int, void*)
{
float gamma = g_gamma / 100.0f;
gammaCorrection(g_image, g_imgGamma, gamma);
imshow(g_gammaWinName, g_imgGamma);
cout << g_gammaWinName << ": " << rmsContrast(g_imgGamma) << endl;
}
//创建对数变换滑动条
static void onTrackbarContrastR1(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("对比度拉伸", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
static void onTrackbarContrastS1(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("对比度拉伸", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
static void onTrackbarContrastR2(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("对比度拉伸", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
static void onTrackbarContrastS2(int, void*)
{
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2);
imshow("对比度拉伸", g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
}
/* 图像强度变换实现图像对比度均衡 */
void ContribDemo::test15()
{
//g_image = imread("../x64/Debug/picture/car.png");
//g_image = imread("../x64/Debug/picture/indicator.jpg");
g_image = imread("../x64/Debug/picture/tree.jpg");
//g_image = imread("../x64/Debug/picture/xray.jpg");
namedWindow(g_gammaWinName);
createTrackbar("Gamma value", g_gammaWinName, &g_gamma, g_gammaMax, onTrackbarGamma); //创建gamma变换筛选方法
//对比度拉伸 Contrast Stretching
namedWindow(g_contrastWinName);
createTrackbar("Contrast R1", g_contrastWinName, &g_r1, 256, onTrackbarContrastR1);
createTrackbar("Contrast S1", g_contrastWinName, &g_s1, 256, onTrackbarContrastS1);
createTrackbar("Contrast R2", g_contrastWinName, &g_r2, 256, onTrackbarContrastR2);
createTrackbar("Contrast S2", g_contrastWinName, &g_s2, 256, onTrackbarContrastS2);
//应用强度转换
Mat imgAutoscaled, imgLog;
autoscaling(g_image, imgAutoscaled); //自动伸缩功能
gammaCorrection(g_image, g_imgGamma, g_gamma / 100.0f); //gamma变换
logTransform(g_image, imgLog); //对数变换
contrastStretching(g_image, g_contrastStretch, g_r1, g_s1, g_r2, g_s2); //对比度拉伸
imshow("Original Image", g_image);
cout << "Original Image: " << rmsContrast(g_image) << endl;
imshow("Autoscale", imgAutoscaled);
cout << "Autoscale: " << rmsContrast(imgAutoscaled) << endl;
imshow(g_gammaWinName, g_imgGamma);
cout << g_gammaWinName << ": " << rmsContrast(g_imgGamma) << endl;
imshow("Log Transformation", imgLog);
cout << "Log Transformation: " << rmsContrast(imgLog) << endl;
imshow(g_contrastWinName, g_contrastStretch);
cout << g_contrastWinName << ": " << rmsContrast(g_contrastStretch) << endl;
}
/* 视觉显著性检测 */
void ContribDemo::test16()
{
//显著性检测算法,可选:SPECTRAL_RESIDUAL,FINE_GRAINED,BING,BinWangApr2014
String saliency_algorithm = "SPECTRAL_RESIDUAL";
String training_path = "D:/Opencv4.6.0/opencv/opencv_contrib-4.6.0/modules/saliency/samples/ObjectnessTrainedModel"; //模型路径
VideoCapture cap;
cap.open("../x64/Debug/video/vtest.avi");
cap.set(CAP_PROP_POS_FRAMES, 0);
Mat binaryMap; // 二值化检测结果
Mat image; // 检测图像
Mat frame;
cap >> frame;
if (frame.empty()) {
return;
}
frame.copyTo(image);
//根据输入的方法确定检测类型
Ptr<Saliency> saliencyAlgorithm;
if (saliency_algorithm.find("SPECTRAL_RESIDUAL") == 0) { //静态显著性谱残差,检测结果,白色区域表示显著区域
Mat saliencyMap;
saliencyAlgorithm = StaticSaliencySpectralResidual::create();
//计算显著性
double start = static_cast<double>(getTickCount());
bool success = saliencyAlgorithm->computeSaliency(image, saliencyMap);
double duration = ((double)getTickCount() - start) / getTickFrequency();
cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;
if (success) {
StaticSaliencySpectralResidual spec;
//二值化图像
double start = static_cast<double>(getTickCount());
spec.computeBinaryMap(saliencyMap, binaryMap);
double duration = ((double)getTickCount() - start) / getTickFrequency();
cout << "computeBinaryMap cost time is: " << duration * 1000 << "ms" << endl;
imshow("Original Image", image);
imshow("Saliency Map", saliencyMap);
imshow("Binary Map", binaryMap);
waitKey(0);
}
}
else if (saliency_algorithm.find("FINE_GRAINED") == 0) { //静态显著性细粒度
Mat saliencyMap;
saliencyAlgorithm = StaticSaliencyFineGrained::create();
// 计算显著性
double start = static_cast<double>(getTickCount());
bool success = saliencyAlgorithm->computeSaliency(image, saliencyMap);
double duration = ((double)getTickCount() - start) / getTickFrequency();
cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;
if (success) {
StaticSaliencyFineGrained spec;
// 二值化图像
double start = static_cast<double>(getTickCount());
spec.computeBinaryMap(saliencyMap, binaryMap);
double duration = ((double)getTickCount() - start) / getTickFrequency();
cout << "computeBinaryMap cost time is: " << duration * 1000 << "ms" << endl;
imshow("Saliency Map", saliencyMap);
imshow("Original Image", image);
imshow("Binary Map", binaryMap);
waitKey(0);
}
}
else if (saliency_algorithm.find("BING") == 0) { //ObjectnessBING
// 判断模型是否存在
if (training_path.empty()) {
cout << "Path of trained files missing! " << endl;
return;
}
else {
saliencyAlgorithm = ObjectnessBING::create();
vector<Vec4i> saliencyMap;
saliencyAlgorithm.dynamicCast<ObjectnessBING>()->setTrainingPath(training_path); //提取模型文件参数
saliencyAlgorithm.dynamicCast<ObjectnessBING>()->setBBResDir("Results"); //将算法检测结果保存在Results文件夹内
saliencyAlgorithm.dynamicCast<ObjectnessBING>()->setNSS(50); //设置非极大值抑制,值越大检测到的目标越少,检测速度越快
// 计算显著性
double start = static_cast<double>(getTickCount());
//基于三个颜色空间进行检测,可以只检测一个空间,把training_path下其他空间模型删除即可,如只保留ObjNessB2W8MAXBGR前缀的文件,算法耗时只有原来的一半
bool success = saliencyAlgorithm->computeSaliency(image, saliencyMap);
double duration = ((double)getTickCount() - start) / getTickFrequency();
cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;
if (success) {
//saliencyMap获取检测到的目标个数
int ndet = int(saliencyMap.size());
std::cout << "Objectness done " << ndet << std::endl;
//目标按可能性从大到小排列,maxd为显示前5个目标,step设置颜色,jitter设置矩形框微调
int maxd = 5, step = 255 / maxd, jitter = 9;
Mat draw = image.clone();
for (int i = 0; i < std::min(maxd, ndet); i++) {
Vec4i bb = saliencyMap[i]; //获得矩形框坐标点
Scalar col = Scalar(((i*step) % 255), 50, 255 - ((i*step) % 255)); //设定颜色
Point off(theRNG().uniform(-jitter, jitter), theRNG().uniform(-jitter, jitter)); //矩形框微调
rectangle(draw, Point(bb[0] + off.x, bb[1] + off.y), Point(bb[2] + off.x, bb[3] + off.y), col, 2);
rectangle(draw, Rect(20, 20 + i * 10, 10, 10), col, -1);
}
imshow("BING", draw);
waitKey();
}
}
}
else if (saliency_algorithm.find("BinWangApr2014") == 0) { //BinWangApr2014
saliencyAlgorithm = MotionSaliencyBinWangApr2014::create();
saliencyAlgorithm.dynamicCast<MotionSaliencyBinWangApr2014>()->setImagesize(image.cols, image.rows);
saliencyAlgorithm.dynamicCast<MotionSaliencyBinWangApr2014>()->init();
bool paused = false;
int i = 0;
for (;;) {
if (!paused) {
cap >> frame;
if (frame.empty()) {
return;
}
Mat srcImg = frame.clone();
cvtColor(frame, frame, COLOR_BGR2GRAY);
Mat saliencyMap;
double start = static_cast<double>(getTickCount());
saliencyAlgorithm->computeSaliency(frame, saliencyMap);
double duration = ((double)getTickCount() - start) / getTickFrequency();
cout << "computeSaliency cost time is: " << duration * 1000 << "ms" << endl;
imshow("image", frame);
imshow("saliencyMap", saliencyMap * 255);
i++;
}
char c = (char)waitKey(2);
}
}
destroyAllWindows();
}
// 计算结果均值
double calMEAN(Scalar result)
{
int i = 0;
double sum = 0;
// 计算总和
for (auto val : result.val) {
if (0 == val || isinf(val)) {
break;
}
sum += val;
i++;
}
return sum / i;
}
/* 图像质量评价 */
void ContribDemo::test17()
{
// img1为基准图像,img2为检测图像
Mat img1, img2;
img1 = imread("../x64/Debug/picture/61.jpg");
img2 = imread("../x64/Debug/picture/61_1.jpg");
//MSE结果越小,检测图像和基准图像的差距越小
Mat quality_map;
Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map);
cout << "*************MSE:" << calMEAN(result_static) << endl;
//PSNR结果越小,检测图像和基准图像的差距越大
Mat quality_map2;
result_static = quality::QualityPSNR::compute(img1, img2, quality_map2, 255.0);
cout << "*************PSNR:" << calMEAN(result_static) << endl;
//GMSD结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
Mat quality_map3;
result_static = quality::QualityGMSD::compute(img1, img2, quality_map3);
cout << "*************GMSD:" << calMEAN(result_static) << endl;
//SSIM结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
Mat quality_map4;
result_static = quality::QualitySSIM::compute(img1, img2, quality_map4);
cout << "*************SSIM:" << calMEAN(result_static) << endl;
//BRISQUE不需要基准图像,结果为一个0到100之间的数,越小表示检测图像质量越好
Mat quality_map5;
String model_path = "D:/Opencv4.6.0/opencv/opencv_contrib-4.6.0/modules/quality/samples/brisque_model_live.yml";
String range_path = "D:/Opencv4.6.0/opencv/opencv_contrib-4.6.0/modules/quality/samples/brisque_range_live.yml";
result_static = quality::QualityBRISQUE::compute(img2, model_path, range_path);
cout << "*************BRISQUE:" << calMEAN(result_static) << endl;
}
opencv_contrib自测程序(4.6.0)
于 2024-01-04 16:49:46 首次发布