直方图对比
视频讲解如下:
在本章节中给大家演示直方图对比,当前代码同样是在毛星云的代码基础上进行扩展优化的。
当前系列所有demo下载地址:
https://github.com/GaoRenBao/OpenCv4-Demo
不同编程语言对应的OpenCv版本以及开发环境信息如下:
语言 | OpenCv版本 | IDE |
C# | OpenCvSharp4.4.8.0.20230708 | Visual Studio 2022 |
C++ | OpenCv-4.5.5-vc14_vc15 | Visual Studio 2022 |
Python | OpenCv-Python (4.6.0.66) | PyCharm Community Edition 2022.1.3 |
首先呢,我们需要准备三张测试图片。(不想下载工程的童鞋,可直接复制下面的图片和代码)



以下从左到右依次是C#、C++、Python三种版本的执行结果对比:

C#版本代码如下:
C#版本需要安装“OpenCvSharp4”、“OpenCvSharp4.runtime.win”两个库才行。不然会报错。
如果需要使用“ BitmapConverter.ToBitmap”操作,则需要追加安装“OpenCvSharp4.Extensions”库。
using OpenCvSharp;
using System;
namespace demo
{
internal class Program
{
static void Main(string[] args)
{
// 【1】声明储存基准图像和另外两张对比图像的矩阵( RGB 和 HSV )
Mat srcImage_base = Cv2.ImRead("../../../images/book1.jpg");
Mat srcImage_test1 = Cv2.ImRead("../../../images/book2.jpg");
Mat srcImage_test2 = Cv2.ImRead("../../../images/book3.jpg");
//显示载入的3张图像
Cv2.ImShow("基准图像", srcImage_base);
Cv2.ImShow("测试图像1", srcImage_test1);
Cv2.ImShow("测试图像2", srcImage_test2);
// 【3】将图像由BGR色彩空间转换到 HSV色彩空间
Mat hsvImage_base = new Mat();
Mat hsvImage_test1 = new Mat();
Mat hsvImage_test2 = new Mat();
Cv2.CvtColor(srcImage_base, hsvImage_base, ColorConversionCodes.BGR2HSV);
Cv2.CvtColor(srcImage_test1, hsvImage_test1, ColorConversionCodes.BGR2HSV);
Cv2.CvtColor(srcImage_test2, hsvImage_test2, ColorConversionCodes.BGR2HSV);
//【4】创建包含基准图像下半部的半身图像(HSV格式)
Rect faces = new Rect()
{
X = 0,
Y = hsvImage_base.Rows / 2,
Width = hsvImage_base.Cols,
Height = hsvImage_base.Rows / 2,
};
Mat hsvImage_halfDown = new Mat(hsvImage_base, faces);
//【5】初始化计算直方图需要的实参
// 对hue通道使用30个bin,对saturatoin通道使用32个bin
int h_bins = 50;
int s_bins = 60;
int[] histSize = { h_bins, s_bins };
// hue的取值范围从0到256, saturation取值范围从0到180
Rangef h_ranges = new Rangef(0, 256);
Rangef s_ranges = new Rangef(0, 180);
Rangef[] ranges = { h_ranges, s_ranges };
// 使用第0和第1通道
int[] channels = { 0, 1 };
// 【6】创建储存直方图的 MatND 类的实例:
Mat baseHist = new Mat();
Mat halfDownHist = new Mat();
Mat testHist1 = new Mat();
Mat testHist2 = new Mat();
// 【7】计算基准图像,两张测试图像,半身基准图像的HSV直方图:
Cv2.CalcHist(new Mat[] { hsvImage_base }, channels, null, baseHist, 2, histSize, ranges, true, false);
Cv2.Normalize(baseHist, baseHist, 0, 1, NormTypes.MinMax, -1, null);
Cv2.CalcHist(new Mat[] { hsvImage_halfDown }, channels, null, halfDownHist, 2, histSize, ranges, true, false);
Cv2.Normalize(halfDownHist, halfDownHist, 0, 1, NormTypes.MinMax, -1, null);
Cv2.CalcHist(new Mat[] { hsvImage_test1 }, channels, null, testHist1, 2, histSize, ranges, true, false);
Cv2.Normalize(testHist1, testHist1, 0, 1, NormTypes.MinMax, -1, null);
Cv2.CalcHist(new Mat[] { hsvImage_test2 }, channels, null, testHist2, 2, histSize, ranges, true, false);
Cv2.Normalize(testHist2, testHist2, 0, 1, NormTypes.MinMax, -1, null);
//【8】按顺序使用4种对比标准将基准图像的直方图与其余各直方图进行对比:
for (int i = 0; i < 4; i++)
{
//进行图像直方图的对比
HistCompMethods compare_method = (HistCompMethods)i;
double base_base = Cv2.CompareHist(baseHist, baseHist, compare_method);
double base_half = Cv2.CompareHist(baseHist, halfDownHist, compare_method);
double base_test1 = Cv2.CompareHist(baseHist, testHist1, compare_method);
double base_test2 = Cv2.CompareHist(baseHist, testHist2, compare_method);
//输出结果
Console.WriteLine($"方法[{i}]的匹配结果如下");
Console.WriteLine($"【基准图 - 基准图】:{base_base}");
Console.WriteLine($"【基准图 - 半身图】:{base_half}");
Console.WriteLine($"【基准图 - 测试图1】:{base_test1}");
Console.WriteLine($"【基准图 - 测试图2】:{base_test2}\r\n");
}
Console.WriteLine("检测结束。");
Cv2.WaitKey(0);
}
}
}
C++版本代码如下:
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
using namespace cv;
int main()
{
//【1】声明储存基准图像和另外两张对比图像的矩阵( RGB 和 HSV )
Mat srcImage_base, hsvImage_base;
Mat srcImage_test1, hsvImage_test1;
Mat srcImage_test2, hsvImage_test2;
Mat hsvImage_halfDown;
//【2】载入基准图像(srcImage_base) 和两张测试图像srcImage_test1、srcImage_test2,并显示
srcImage_base = imread("../images/book1.jpg");
srcImage_test1 = imread("../images/book2.jpg");
srcImage_test2 = imread("../images/book3.jpg");
//显示载入的3张图像
imshow("基准图像", srcImage_base);
imshow("测试图像1", srcImage_test1);
imshow("测试图像2", srcImage_test2);
// 【3】将图像由BGR色彩空间转换到 HSV色彩空间
cvtColor(srcImage_base, hsvImage_base, COLOR_BGR2HSV);
cvtColor(srcImage_test1, hsvImage_test1, COLOR_BGR2HSV);
cvtColor(srcImage_test2, hsvImage_test2, COLOR_BGR2HSV);
//【4】创建包含基准图像下半部的半身图像(HSV格式)
hsvImage_halfDown = hsvImage_base(Range(hsvImage_base.rows / 2, hsvImage_base.rows - 1), Range(0, hsvImage_base.cols - 1));
//【5】初始化计算直方图需要的实参
// 对hue通道使用30个bin,对saturatoin通道使用32个bin
int h_bins = 50; int s_bins = 60;
int histSize[] = { h_bins, s_bins };
// hue的取值范围从0到256, saturation取值范围从0到180
float h_ranges[] = { 0, 256 };
float s_ranges[] = { 0, 180 };
const float* ranges[] = { h_ranges, s_ranges };
// 使用第0和第1通道
int channels[] = { 0, 1 };
// 【6】创建储存直方图的 MatND 类的实例:
MatND baseHist;
MatND halfDownHist;
MatND testHist1;
MatND testHist2;
// 【7】计算基准图像,两张测试图像,半身基准图像的HSV直方图:
calcHist(&hsvImage_base, 1, channels, Mat(), baseHist, 2, histSize, ranges, true, false);
normalize(baseHist, baseHist, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvImage_halfDown, 1, channels, Mat(), halfDownHist, 2, histSize, ranges, true, false);
normalize(halfDownHist, halfDownHist, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvImage_test1, 1, channels, Mat(), testHist1, 2, histSize, ranges, true, false);
normalize(testHist1, testHist1, 0, 1, NORM_MINMAX, -1, Mat());
calcHist(&hsvImage_test2, 1, channels, Mat(), testHist2, 2, histSize, ranges, true, false);
normalize(testHist2, testHist2, 0, 1, NORM_MINMAX, -1, Mat());
//【8】按顺序使用4种对比标准将基准图像的直方图与其余各直方图进行对比:
for (int i = 0; i < 4; i++)
{
//进行图像直方图的对比
int compare_method = i;
double base_base = compareHist(baseHist, baseHist, compare_method);
double base_half = compareHist(baseHist, halfDownHist, compare_method);
double base_test1 = compareHist(baseHist, testHist1, compare_method);
double base_test2 = compareHist(baseHist, testHist2, compare_method);
//输出结果
printf("方法 [%d] 的匹配结果如下:\r\n", i);
printf("【基准图 - 基准图】:%f:\r\n", base_base);
printf("【基准图 - 半身图】:%f:\r\n", base_half);
printf("【基准图 - 测试图1】:%f:\r\n", base_test1);
printf("【基准图 - 测试图2】:%f:\r\n\r\n", base_test2);
}
printf("检测结束。");
waitKey(0);
return 0;
}
Python版本代码如下:
import cv2
# 【1】载入基准图像(srcImage_base) 和两张测试图像srcImage_test1、srcImage_test2,并显示
srcImage_base = cv2.imread('../images/book1.jpg"')
srcImage_test1 = cv2.imread('../images/book2.jpg')
srcImage_test2 = cv2.imread('../images/book3.jpg')
# 【2】显示载入的3张图像
cv2.imshow('srcImage_base', srcImage_base)
cv2.imshow('srcImage_test1', srcImage_test1)
cv2.imshow('srcImage_test2', srcImage_test2)
# 【3】将图像由BGR色彩空间转换到 HSV色彩空间
hsvImage_base = cv2.cvtColor(srcImage_base, cv2.COLOR_BGR2HSV)
hsvImage_test1 = cv2.cvtColor(srcImage_test1, cv2.COLOR_BGR2HSV)
hsvImage_test2 = cv2.cvtColor(srcImage_test2, cv2.COLOR_BGR2HSV)
# 【4】创建包含基准图像下半部的半身图像(HSV格式)
x = 0
y = int(hsvImage_base.shape[0] / 2)
h = int(hsvImage_base.shape[0] / 2)
w = hsvImage_base.shape[1]
hsvImage_halfDown = hsvImage_base[y:y + h, x:x + w]
# 【5】初始化计算直方图需要的实参
histSize = [50, 60]
ranges = [0, 256, 0, 180]
# 使用第0和第1通道
channels = [0, 1]
# 【6】计算基准图像,两张测试图像,半身基准图像的HSV直方图:
baseHist = cv2.calcHist([hsvImage_base], channels, None, histSize, ranges)
cv2.normalize(baseHist, baseHist, 0, 1, cv2.NORM_MINMAX, -1);
halfDownHist = cv2.calcHist([hsvImage_halfDown], channels, None, histSize, ranges)
cv2.normalize(halfDownHist, halfDownHist, 0, 1, cv2.NORM_MINMAX, -1);
testHist1 = cv2.calcHist([hsvImage_test1], channels, None, histSize, ranges)
cv2.normalize(testHist1, testHist1, 0, 1, cv2.NORM_MINMAX, -1);
testHist2 = cv2.calcHist([hsvImage_test2], channels, None, histSize, ranges)
cv2.normalize(testHist2, testHist2, 0, 1, cv2.NORM_MINMAX, -1);
# 【7】按顺序使用4种对比标准将基准图像的直方图与其余各直方图进行对比:
for i in range(4):
# 进行图像直方图的对比
base_base = cv2.compareHist(baseHist, baseHist, i)
base_half = cv2.compareHist(baseHist, halfDownHist, i)
base_test1 = cv2.compareHist(baseHist, testHist1, i)
base_test2 = cv2.compareHist(baseHist, testHist2, i)
print("方法[", i, "]的匹配结果如下")
print("【基准图 - 基准图】:", base_base)
print("【基准图 - 半身图】:", base_half)
print("【基准图 - 测试图1】:", base_test1)
print("【基准图 - 测试图2】:", base_test2, "\n")
print("检测结束")
cv2.waitKey(0)