C#调用C++OpenCV直方图匹配

基于OpenCV4.4.0的直方图匹配

直方图比较

直方图比较,是用一定的标准来判断两个直方图的相似度方法;
OpenCV中提供的API是:
对输入的两张图像计算得到直方图H1与H2,归一化到相同的尺度空间然后可以通过计算H1与H2的之间的距离得到两个直方图的相似程度进而比较图像本身的相似程度。
Opencv提供的比较方法有四种:
1.Correlation 相关性比较
2.Chi - Square 卡方比较
3.Intersection 十字交叉性
4.Bhattacharyya distance 巴氏距离

相关系数的标准

相关系数的标准(method=CV_COMP_CORREL) 值越大,相关度越高,最大值为1,最小值为0

卡方系数的标准

卡方系数的标准(method=CV_COMP_CHISQR) 值越小,相关度越高,最大值无上界,最小值0

相交系数的标准

相交系数的标准(method=CV_COMP_INTERSECT)值越大,相关度越高,最大值为9.455319,最小值为0

巴氏系数的标准

巴氏系数的标准(method=CV_COMP_BHATTACHARYYA) 值越小,相关度越高,最大值为1,最小值为0

步骤

步骤:首先把图像从RGB色彩空间转换到HSV色彩空间cvtColor
计算图像的直方图,然后归一化到[0~1]之间calcHist和normalize;
使用上述四种比较方法之一进行比较compareHist

直方图匹配原理

Alt

任何一种颜色都是由红绿蓝三原色(RGB)构成的,所以上图共有4张直方图(三原色直方图 + 最后合成的直方图)。
如果每种原色都可以取256个值,那么整个颜色空间共有1600万种颜色(256的三次方)。针对这1600万种颜色比较直方图,计算量实在太大了,因此需要采用简化方法。可以将0~255分成四个区:0~63为第0区,64~127为第1区,128~191为第2区,192~255为第3区。这意味着红绿蓝分别有4个区,总共可以构成64种组合(4的3次方)。
任何一种颜色必然属于这64种组合中的一种,这样就可以统计每一种组合包含的像素数量。
Alt
上图是某张图片的颜色分布表,将表中最后一栏提取出来,组成一个64维向量(7414, 230, 0, 0, 8, …, 109, 0, 0, 3415, 53929)。这个向量就是这张图片的特征值或者叫"指纹"。

于是,寻找相似图片就变成了找出与其最相似的向量。

直方图匹配优缺点

优点:根据像素进行匹配,对于分辨率不同的图片效果较好,实验数据如下。
缺点:对于不同图片,但是相似,或是些许形变的图片,匹配效果并不好。

C++代码

//直方图比较
#include<opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include<opencv2/highgui/highgui_c.h>
using namespace cv;
using namespace std;

string convertToString(double d);
int main(int argc, char* argv)
{
	Mat base, test1, test2;
	Mat hsvbase, hsvtest1, hsvtest2;
	base = imread("C:/Users/wenhaofu/Desktop/picture/NBA1.png");
	test1 = imread("C:/Users/wenhaofu/Desktop/picture/NBA2.png");
	test2 = imread("C:/Users/wenhaofu/Desktop/picture/NBA3.png");
	if (!base.data)
	{
		printf("could not load image...\n");
		return -1;
	}
	//步骤一:从RGB空间转换到HSV空间
	cvtColor(base, hsvbase, CV_BGR2HSV);
	cvtColor(test1, hsvtest1, CV_BGR2HSV);
	cvtColor(test2, hsvtest2, CV_BGR2HSV);

	//步骤二:计算直方图与归一化
	int h_bins = 50;
	int s_bins = 60;
	int histsize[] = { h_bins,s_bins };
	//hue varies from 0 to 179,saturation from 0 to 255
	float h_ranges[] = { 0,180 };
	float s_ranges[] = { 0,256 };
	const float* histRanges[] = { h_ranges,s_ranges };
	//use the 0-th and 1-st channels
	int channels[] = { 0,1 };
	MatND hist_base;
	MatND hist_test1;
	MatND hist_test2;
	//计算直方图
	calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histsize, histRanges, true, false);

	//归一化
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());//归一化
	normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
	normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());

	//步骤三:比较直方图,并返回值
	double basebase = compareHist(hist_base, hist_base, CV_COMP_BHATTACHARYYA);//比较直方图
	double basetest1 = compareHist(hist_base, hist_test1, CV_COMP_BHATTACHARYYA);
	double basetest2 = compareHist(hist_base, hist_test2, CV_COMP_BHATTACHARYYA);
	double test1test2 = compareHist(hist_test1, hist_test2, CV_COMP_BHATTACHARYYA);
	printf("test1 with test2 correlation value :%f", test1test2);

	//在原图中显示相关性参数
	putText(base, convertToString(basebase), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);
	putText(test1, convertToString(basetest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);
	putText(test2, convertToString(basetest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);
	putText(test2, convertToString(test1test2), Point(100, 100), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);

	namedWindow("base", CV_WINDOW_AUTOSIZE);
	namedWindow("test1", CV_WINDOW_AUTOSIZE);
	namedWindow("test2", CV_WINDOW_AUTOSIZE);

	imshow("base", base);
	imshow("test1", test1);
	imshow("test2", test2);

	waitKey(0);
	return 0;
}

//由于comparehist计算出来的相关性的值是一个double型,这个函数就是把double转变为string
string convertToString(double d)
{
	ostringstream os;
	if (os << d)
		return os.str();
	return "invalid conversion";
}

C++代码转化为C#

将C++代码编译成一个dll文件

(1)在之前的C++代码上,在解决方案资源管理器中新建一个头文件(.h)
在这里插入图片描述

(2)在新建的头文件中,输入以下内容:

#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include <iostream>
#include <math.h>
#include <opencv2/highgui/highgui_c.h>
#include <string>
using namespace std;
extern "C" __declspec(dllexport) std::string convertToString(double d);
extern "C" __declspec(dllexport) void histogram(char* s1, char* s2, char* s3);//因为c#与c++之间,string不能直接转换

extern "C"外部声明,表示函数和变量是按照C语言的方式编译和链接的。

_decspec(dllexport)的目的是为了将对应的函数放入到DLL动态库中。

extern “C” _declspec(dllexport)的目的是为了使用DllImport调用非托管C++的DLL文件。因为使用DllImport只能调用由C语言函数做的DLL。

c#与c++对应:

将string转为IntPtr:IntPtr
System.Runtime.InteropServices.Marshal.StringToCoTaskMemAuto(string)
将IntPtr转为string:string
System.Runtime.InteropServices.MarshalPtrToStringAuto(IntPtr) c++
<----------> c# BSTR --------- StringBuilder LPCTSTR ---------
StringBuilder LPCWSTR --------- IntPtr handle---------IntPtr
hwnd-----------IntPtr char *----------string int * -----------ref int
int &-----------ref int void *----------IntPtr unsigned char *-----ref
byte Struct需要在C#里重新定义一个Struct CallBack回调函数需要封装在一个委托里,delegate static
extern int FunCallBack(string str); 注意在每个函数的前面加上public static extern
+返回的数据类型,如果不加public ,函数默认为私有函数,调用就会出错。

(3)在之前的C++文件中,直接修改代码
至于C++如何配置环境;如何写;具体的怎么做可以看VS2019+OpenCV4.4.0安装及整合详细步骤

//直方图比较

#include "Histogram.h"
using namespace cv;
using namespace std;

//由于comparehist计算出来的相关性的值是一个double型,这个函数就是把double转变为string
std::string convertToString(double d)
{
	ostringstream os;
	if (os << d)
		return os.str();
	return "invalid conversion";
}
void histogram(char *s1, char *s2, char *s3) {
	Mat base, test1, test2;
	Mat hsvbase, hsvtest1, hsvtest2;
	base = imread(s1,1);
	test1 = imread(s2,1);
	test2 = imread(s3,1);
	if (!base.data || !test1.data || !test2.data)
	{
		printf("could not load image...\n");
		return;
	}
	//步骤一:从RGB空间转换到HSV空间
	cvtColor(base, hsvbase, CV_BGR2HSV);
	cvtColor(test1, hsvtest1, CV_BGR2HSV);
	cvtColor(test2, hsvtest2, CV_BGR2HSV);

	//步骤二:计算直方图与归一化
	int h_bins = 50;
	int s_bins = 60;
	int histsize[] = { h_bins,s_bins };
	//hue varies from 0 to 179,saturation from 0 to 255
	float h_ranges[] = { 0,180 };
	float s_ranges[] = { 0,256 };
	const float* histRanges[] = { h_ranges,s_ranges };
	//use the 0-th and 1-st channels
	int channels[] = { 0,1 };
	MatND hist_base;
	MatND hist_test1;
	MatND hist_test2;
	//计算直方图
	calcHist(&hsvbase, 1, channels, Mat(), hist_base, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest1, 1, channels, Mat(), hist_test1, 2, histsize, histRanges, true, false);
	calcHist(&hsvtest2, 1, channels, Mat(), hist_test2, 2, histsize, histRanges, true, false);

	//归一化
	normalize(hist_base, hist_base, 0, 1, NORM_MINMAX, -1, Mat());//归一化
	normalize(hist_test1, hist_test1, 0, 1, NORM_MINMAX, -1, Mat());
	normalize(hist_test2, hist_test2, 0, 1, NORM_MINMAX, -1, Mat());

	//步骤三:比较直方图,并返回值
	double basebase = compareHist(hist_base, hist_base, CV_COMP_CORREL);//比较直方图
	double basetest1 = compareHist(hist_base, hist_test1, CV_COMP_CORREL);
	double basetest2 = compareHist(hist_base, hist_test2, CV_COMP_CORREL);
	double test1test2 = compareHist(hist_test1, hist_test2, CV_COMP_CORREL);
	printf("test1 with test1 correlation value :%f", basebase);
	cout << endl;
	printf("test1 with test2 correlation value :%f", basetest1);
	cout << endl;
	printf("test1 with test3 correlation value :%f", basetest2);
	cout << endl;
	printf("test2 with test3 correlation value :%f", test1test2);
	//在原图中显示相关性参数
	putText(base, convertToString(basebase), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);
	putText(test1, convertToString(basetest1), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);
	putText(test2, convertToString(basetest2), Point(50, 50), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);
	putText(test2, convertToString(test1test2), Point(100, 100), CV_FONT_HERSHEY_COMPLEX, 1, Scalar(0, 0, 255), 2, CV_AA);

	namedWindow("base", CV_WINDOW_AUTOSIZE);
	namedWindow("test1", CV_WINDOW_AUTOSIZE);
	namedWindow("test2", CV_WINDOW_AUTOSIZE);

	imshow("base", base);
	imshow("test1", test1);
	imshow("test2", test2);

	waitKey(0);
}


(4)在源文件文件夹下新建模块定义文件(.def),输入以下内容:

LIBRARY Histogram
EXPORTS histogram
EXPORTS convertToString

EXPORTS后面的就是你要调用的方法名,生成.dll文件的同时也会生成.lib库。
(5)生成解决方案,这里可能生成没问题,但是最后运行C#会报错:

System.DllNotFoundException: Unable to load DLL 'XX.dll': 找不到指定的模块。 (Exception from HRESULT:

直接在这里解决了
这是运行库设置的问题,几个工程的运行库设置不一样。解决方法:
打开所引用的dll源码的项目属性页面,配置属性 -> C/C++ -> 代码生成 -> 运行库,将该项设置为 多线程调试 (/MTd),然后再重新生成解决方案,使用新的dll即可解决问题。

(6)再重新生成整个解决方案,可能又会出现以下错误:

这是因为输出文件拓展名的问题,解决方法如下:
【项目属性】→ 【配置属性】→【常规】→【目标文件扩展名】,将.exe,改为.dll
再重新生成解决方案 😃
(7)找到解决方案所在目录,在Debug目录下即可找到生成的 .dll 文件。要注意的是,要到整个解决方案的目录下找Debug文件夹,如下图:
在这里插入图片描述

C#调用dll文件

(1)新建一个C#项目。
在这里插入图片描述

(2)把Histogram.dll文件复制到该工程的Debug目录下。
在这里插入图片描述

(3)在C#文件中引用该 .dll 文件,代码如下:

using Microsoft.AspNetCore.Http;
using System;

using System.Runtime.InteropServices;
namespace ConsoleApp4
{
    class Program
    {
        [DllImport("Histogram.dll")]
        //[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
        public static extern void histogram(string s1,string s2,string s3);
     
        static void Main(string[] args)
        {
            Console.WriteLine("C#调用dll");
            
            string s1 = "C:/Users/wenhaofu/Desktop/picture/tubiao3.png";
            string s2 = "C:/Users/wenhaofu/Desktop/picture/tubiao2.png";
            string s3 = "C:/Users/wenhaofu/Desktop/picture/tubiao1.png";
            histogram(s1,s2,s3);
            Console.Read();
        }
    }
}

(5)点击运行:
Alt

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要在C#调用C++编写的OpenCV dll,可以使用Platform Invoke(P/Invoke)技术。下面是一个简单的示例,演示如何在C#调用C++编写的OpenCV dll: 1. 创建一个新的C#控制台应用程序。 2. 在项目文件夹中创建一个名为“opencv”的子文件夹。 3. 将OpenCV dll文件复制到该子文件夹中。 4. 在Visual Studio中打开项目,并添加以下代码: ```C# using System; using System.Runtime.InteropServices; class Program { [DllImport("opencv\\opencv_core320.dll", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr cvCreateImage( [MarshalAs(UnmanagedType.Struct)] CvSize size, int depth, int channels); static void Main(string[] args) { // 创建一个256x256的8位单通道图像 var size = new CvSize(256, 256); var image = cvCreateImage(size, 8, 1); // 在控制台中输出图像信息 Console.WriteLine("Image created: {0}x{1}, depth={2}, channels={3}", size.Width, size.Height, 8, 1); Console.ReadKey(); } } [StructLayout(LayoutKind.Sequential)] public struct CvSize { public int Width; public int Height; public CvSize(int width, int height) { Width = width; Height = height; } } ``` 上面的代码创建了一个256x256的8位单通道图像,并在控制台中输出了图像信息。 在上面的代码中,我们使用DllImport属性来指定要导入的OpenCV dll的名称和调用约定。在本例中,我们使用Cdecl调用约定。 我们还定义了一个结构体CvSize,用于传递图像大小参数。在C++中,CvSize结构体定义在opencv_core.hpp头文件中。我们在C#中重新定义了这个结构体,以便我们可以在C#中使用它来传递参数。 需要注意的是,由于C++C#使用不同的内存管理机制,因此您需要确保在C#中正确处理从C++返回的指针。在本例中,我们使用IntPtr类型来表示从C++返回的指针,并使用Marshal类中的相关方法来管理它们。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值