API

- equalizeHist();//直方图均衡化
- split();//分离通道
- calcHist();//参数dims,bin,range
- waitkey();

- mixChannels//分离通道, 与split小有差别,建议看官方文档
- cvtColer//使用此api转换到hsv色彩空间
- calcHist//接受单通道图像计算直方图, 参数略微复杂
- normalize//归一化,常用
- compareHist//比较直方图,可以得到两张图片的相似程度,但是对光非常敏感
- backProject();//直方图反向投影

笔记

这里是几个关于直方图的总结, 这一堆实在是不太好懂, 并且映射和统计接触到了很多数学方面的东西,重要的是理解思路以及了解API, 这里的东西要是有不理解的都建议去看官方的教程文档

  • 直方图均衡化

好像在人脸识别的项目总结里写过了, API并不难理解

  • HSV模型

HSV是一种比较直观的颜色模型,所以在许多图像编辑工具中应用比较广泛,这个模型中颜色的参数分别是:色调(H, Hue),饱和度(S,Saturation),明度(V, Value)

  • 直方图比较
compareHist();

API比较方便,输入两个直方图,比较他们的相似程度

这两个直方图通常会是图像的HS直方图(虽然我现在还不太懂HS为什么可以变成一张直方图,到时候要用再看原理好了),用HS猜测是要降低算法对光线的敏感度?(实测好像对光线还是很敏感)

OpenCV提供了一共四种方法

  • 相关性计算:{-1, 1} //1最强
  • 卡方计算:{0, ∞} //0最强
  • 十字交叉
  • 巴氏距离计算:{0, 1} //0最强

其中比较推荐的是相关性计算和巴氏距离计算(不需要归一化了hhhhhhhhh)

数学公式不列出了(反正我又不会去看)

  • 直方图反向投影
mixChannels();
backProject();

直方图反向投影是一种基于色彩的对象识别技术,通过该方法可以定位图像中已知物体的位置,反应直方图在目标图像中的分布

主要思路是提取已知图像的Hue(色相)空间,做出直方图,再反向找到这些色相在目标图片中的分布,已知图像中越多的色相,在backProject中就会(看起来)更亮,利用这点加上一些二值化,就可以得到一张目标物体的掩膜,覆盖到目标图片上就可以得到完成的图像了,如下


源//读取TIM图标, 在桌面截图上找到他

// OpenCV_Template.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include "opencv2/highgui.hpp"
#include <iostream>


using namespace cv;
using namespace std;

Mat hue;
Mat target_hue;
int bins = 25;
Mat src_mask_globle;
Mat target;

void Hist_and_Backproj(int, void*);

int main(int argc, char* argv[])
{
    //读入src
    CommandLineParser parser(argc, argv, "{@input |D:/WorkSpace/Projects/OpenCV Learning/ImageHub/Tim.png  | input image}");
    Mat src = imread(parser.get<String>("@input"));
    if (src.empty())
    {
        cout << "Could not open or find the image!\n" << endl;
        cout << "Usage: " << argv[0] << " <Input image>" << endl;
        return -1;
    }
    
    target = imread("D:/WorkSpace/Projects/OpenCV Learning/ImageHub/屏幕.png");
    pyrDown(target, target, Size(target.cols / 2, target.rows / 2));
    pyrDown(target, target, Size(target.cols / 2, target.rows / 2));
    //pyrDown(src, src, Size(src.cols / 2, src.rows / 2));

    //二值化取覆膜
    Mat src_gray;
    cvtColor(src, src_gray, COLOR_BGR2GRAY);
    Mat src_mask;
    threshold(src_gray, src_mask, 0, 255, THRESH_TRIANGLE);
    src_mask_globle = src_mask;
    imshow("BinMask", src_mask);

    //色彩空间转换
    //src = Mat(src.size(), src.type(), Scalar(255, 0, 0));
    Mat hsv;
    cvtColor(src, hsv, COLOR_BGR2HSV);
    Mat target_hsv;
    cvtColor(target, target_hsv, COLOR_BGR2HSV);

    //创建Hue空间通道图像
    hue.create(hsv.size(), hsv.depth());
    target_hue.create(target_hsv.size(), target_hsv.depth());
    int ch[] = { 0, 0 };
    mixChannels(&hsv, 1, &hue, 1, ch, 1);
    mixChannels(&target_hsv, 1, &target_hue, 1, ch, 1);
    //cout << hue;

    //创建滑块并BackProjection
    const char* window_image = "Source image";
    namedWindow(window_image);
    createTrackbar("* Hue  bins: ", window_image, &bins, 180, Hist_and_Backproj);
    Hist_and_Backproj(0, 0);
    imshow(window_image, src);

    // Wait until user exits the program
    waitKey(0);

    return 0;
}

void Hist_and_Backproj(int, void*)
{
    //防止bins太小
    int histSize = MAX(bins, 2);

    //计算Hue空间直方图
    float hue_range[] = { 0, 180 };
    const float* ranges = { hue_range };
    Mat hist;
    calcHist(&hue, 1, 0, src_mask_globle, hist, 1, &histSize, &ranges, true, false);
    normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());

    //BackProjection
    Mat backproj;
    calcBackProject(&target_hue, 1, 0, hist, backproj, &ranges, 1, true);
    threshold(backproj, backproj, 0, 255, THRESH_OTSU);
    imshow("BackProj", backproj);

    //绘制直方图
    int w = 400, h = 400;
    int bin_w = cvRound((double)w / histSize);
    Mat histImg = Mat::zeros(h, w, CV_8UC3);
    for (int i = 0; i < bins; i++)
    {
        rectangle(histImg, Point(i * bin_w, h), Point((i + 1) * bin_w, h - cvRound(hist.at<float>(i) * h / 255.0)),
            Scalar(0, 0, 255), FILLED);
    }
    imshow("Histogram", histImg);

    Mat focus;
    target.copyTo(focus, backproj);
    imshow("focus", focus);
}

// 运行程序: Ctrl + F5 或调试 >“开始执行(不调试)”菜单
// 调试程序: F5 或调试 >“开始调试”菜单

// 入门使用技巧: 
//   1. 使用解决方案资源管理器窗口添加/管理文件
//   2. 使用团队资源管理器窗口连接到源代码管理
//   3. 使用输出窗口查看生成输出和其他消息
//   4. 使用错误列表窗口查看错误
//   5. 转到“项目”>“添加新项”以创建新的代码文件,或转到“项目”>“添加现有项”以将现有代码文件添加到项目
//   6. 将来,若要再次打开此项目,请转到“文件”>“打开”>“项目”并选择 .sln 文件