提取圖像特徵描述子(HOG,LBP)並利用餘弦相似度的計算——C++實現

餘弦相似度的計算(C++代碼實現)

一.餘弦計算

1.幾何中,夾角餘弦可用來衡量兩個向量方向的差異;機器學習中,借用這一概念來衡量樣本向量之間的差異。

2.二維空間中向量A(x1,y1)與向量B(x2,y2)的夾角餘弦公式:
在這裏插入圖片描述
3.兩個n維樣本點a(x11,x12,…,x1n)和b(x21,x22,…,x2n)的夾角餘弦爲:
在這裏插入圖片描述
即:
在這裏插入圖片描述
夾角餘弦取值範圍爲[-1,1]。餘弦越大表示兩個向量的夾角越小,餘弦越小表示兩向量的夾角越大。當兩個向量的方向重合時餘弦取最大值1,當兩個向量的方向完全相反餘弦取最小值-1。

參考博客:https://blog.csdn.net/weixin_33743248/article/details/91946456

二.餘弦計算(c++代碼實現)

1,opencv中封裝了矩陣運算,可以直接調用


float getSimilarity(const cv::Mat& first,const cv::Mat& second)
    {
        double dotSum=first.dot(second);//內積
        double normFirst=cv::norm(first);//取模
        double normSecond=cv::norm(second); 
        if(normFirst!=0 && normSecond!=0)
        {
            return dotSum/(normFirst*normSecond);
        }
    }

2,如果特徵向量不是Mat類型,是vector類型

   float getMold(const vector<float>& vec)
   {   //求向量的模長
        int n = vec.size();
        float sum = 0.0;
        for (int i = 0; i<n; ++i)
            sum += vec[i] * vec[i];
        return sqrt(sum);
    }

    float getSimilarity(const vector<float>& lhs, const vector<float>& rhs)
    {
        int n = lhs.size();
        assert(n == rhs.size());
        float tmp = 0.0;  //內積
        for (int i = 0; i<n; ++i)
            {
            	tmp += lhs[i] * rhs[i];
            }
        return tmp / (getMold(lhs)*getMold(rhs));
    }

參考博客:https://blog.csdn.net/qq_17278169/article/details/77093303

3.Matlab計算夾角餘弦(Matlab中的pdist(X, ‘cosine’)得到的是1減夾角餘弦的值):

X=[1 1;1 2;2 5;1 -4];
  d=1-pdist(X,'cosine')
  d=
    0.9487    0.9191   -0.5145    0.9965   -0.7593   -0.8107

二.利用餘弦距離計算圖像特徵之間的相似度(c++代碼實現)

1.利用opencv提取hog特徵,然後使用hog特徵的特徵描述子進行兩張圖像之間的相似度計算

float* get_hog_feature(cv::Mat img)
{
	
	cv::HOGDescriptor hog = cv::HOGDescriptor(cvSize(20, 20), cvSize(10, 10), cvSize(5, 5), cvSize(5, 5), 9);
	cv::resize(img, img, cv::Size(30, 30), (0, 0), (0, 0), cv::INTER_LINEAR);
	std::vector<float> descriptors;
	// float *descriptors;
	hog.compute(img, descriptors, cv::Size(20, 20), cv::Size(0, 0));
	float *feature_float = (float*)malloc(descriptors.size() * sizeof(float));
	assert(feature_float);
	for (int i = 0; i < 128; i++)
	{
		feature_float[i] = descriptors[i * 2];
	}

	return feature_float;
}

bool getRectsHogFeature(const cv::Mat& img, DETECTIONS& d)
{
	std::vector<cv::Mat> mats;
	int feature_dim = 128;
	for (DETECTION_ROW& dbox : d)
	{
		cv::Rect rc = cv::Rect(int(dbox.tlwh(0)), int(dbox.tlwh(1)), int(dbox.tlwh(2)), int(dbox.tlwh(3)));
		rc.x = (rc.x >= 0 ? rc.x : 0);
		rc.y = (rc.y >= 0 ? rc.y : 0);
		rc.width = (rc.x + rc.width <= img.cols ? rc.width : (img.cols - rc.x));
		rc.height = (rc.y + rc.height <= img.rows ? rc.height : (img.rows - rc.y));
		cv::Mat mattmp = img(rc).clone();
		//cv::resize(mattmp, mattmp, cv::Size(64, 128));
		
		float *feature_float = get_hog_feature(mattmp);

		for (int i=0;i<feature_dim;i++)
		{
			dbox.feature[i] = feature_float[i];
		}
		
	}
	return true;
}

2.提取圖像LBP特徵描述子進行兩張圖像之間的相似度計算

bool getRectsLBPFeature(const cv::Mat& img, DETECTIONS& d)
{
	std::vector<cv::Mat> mats;
	int feature_dim = 128;
	for (DETECTION_ROW& dbox : d)
	{
		cv::Rect rc = cv::Rect(int(dbox.tlwh(0)), int(dbox.tlwh(1)), int(dbox.tlwh(2)), int(dbox.tlwh(3)));
		rc.x = (rc.x >= 0 ? rc.x : 0);
		rc.y = (rc.y >= 0 ? rc.y : 0);
		rc.width = (rc.x + rc.width <= img.cols ? rc.width : (img.cols - rc.x));
		rc.height = (rc.y + rc.height <= img.rows ? rc.height : (img.rows - rc.y));
		cv::Mat mattmp = img(rc).clone();
		//cv::resize(mattmp, mattmp, cv::Size(64, 128));
		vector<float> LBPFrature= get_LBP_feature(mattmp);

		for (int i = 0; i < feature_dim; i++)
		{
			dbox.feature[i] = LBPFrature[i];
		}

	}
	return true;

}


vector<float> get_LBP_feature(cv::Mat img)
{
	cv::resize(img, img, cv::Size(55, 55));
	Mat LBPFeatureImg, GrayImg;
	cv::cvtColor(img, GrayImg, CV_BGR2GRAY);
	LBPFeatureExtract LBPFeature;
	//等價灰度不變LBP(58)特徵
	LBPFeature.NormalLBPFeature(GrayImg, Size(50, 50), LBPFeatureImg);
	float *dataOfFeatureVector = (float *)LBPFeatureImg.data;
	vector<float>feature_float;
	feature_float.reserve(128);
	for (int i = 0; i < 128; i++)
	{
		feature_float.push_back(dataOfFeatureVector[i * 2]);
	}
	return feature_float;
}

void LBPFeatureExtract::NormalLBPFeature(const Mat &srcImage, Size cellSize, Mat &featureVector)
{
	// 參數檢查,內存分配
	CV_Assert(srcImage.depth() == CV_8U && srcImage.channels() == 1);

	Mat LBPImage;
	NormalLBPImage(srcImage, LBPImage);

	// 計算cell個數
	int widthOfCell = cellSize.width;
	int heightOfCell = cellSize.height;
	int numberOfCell_X = srcImage.cols / widthOfCell;// X方向cell的個數
	int numberOfCell_Y = srcImage.rows / heightOfCell;

	// 特徵向量的個數
	int numberOfDimension = 256 * numberOfCell_X*numberOfCell_Y;
	featureVector.create(1, numberOfDimension, CV_32FC1);
	featureVector.setTo(Scalar(0));

	// 計算LBP特徵向量
	int stepOfCell = srcImage.cols;
	int pixelCount = cellSize.width*cellSize.height;
	float *dataOfFeatureVector = (float *)featureVector.data;
	vector<float>LBPFeature;
	
	// cell的特徵向量在最終特徵向量中的起始位置
	int index = -256;
	for (int y = 0; y <= numberOfCell_Y - 1; ++y)
	{
		for (int x = 0; x <= numberOfCell_X - 1; ++x)
		{
			index += 256;

			// 計算每個cell的LBP直方圖
			Mat cell = LBPImage(Rect(x * widthOfCell, y * heightOfCell, widthOfCell, heightOfCell));
			uchar *rowOfCell = cell.data;
			for (int y_Cell = 0; y_Cell <= cell.rows - 1; ++y_Cell, rowOfCell += stepOfCell)
			{
				uchar *colOfCell = rowOfCell;
				for (int x_Cell = 0; x_Cell <= cell.cols - 1; ++x_Cell, ++colOfCell)
				{
					++dataOfFeatureVector[index + colOfCell[0]];
				}
			}

			// 一定要歸一化!否則分類器計算誤差很大
			for (int i = 0; i <= 255; ++i)
			{
				dataOfFeatureVector[index + i] /= pixelCount;
				LBPFeature.push_back(dataOfFeatureVector[index + i]);
			}
				
		}
	}

}

//灰度不變常規LBP(256)
void LBPFeatureExtract::NormalLBPImage(const Mat &srcImage, Mat &LBPImage)
{
	CV_Assert(srcImage.depth() == CV_8U && srcImage.channels() == 1);
	LBPImage.create(srcImage.size(), srcImage.type());


	Mat extendedImage;
	copyMakeBorder(srcImage, extendedImage, 1, 1, 1, 1, BORDER_DEFAULT);

	// 計算LBP特徵圖
	int heightOfExtendedImage = extendedImage.rows;
	int widthOfExtendedImage = extendedImage.cols;
	int widthOfLBP = LBPImage.cols;
	uchar *rowOfExtendedImage = extendedImage.data + widthOfExtendedImage + 1;
	uchar *rowOfLBPImage = LBPImage.data;
	for (int y = 1; y <= heightOfExtendedImage - 2; ++y, rowOfExtendedImage += widthOfExtendedImage, rowOfLBPImage += widthOfLBP)
	{
		// 列
		uchar *colOfExtendedImage = rowOfExtendedImage;
		uchar *colOfLBPImage = rowOfLBPImage;
		for (int x = 1; x <= widthOfExtendedImage - 2; ++x, ++colOfExtendedImage, ++colOfLBPImage)
		{
			// 計算LBP值
			int LBPValue = 0;
			if (colOfExtendedImage[0 - widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 128;
			if (colOfExtendedImage[0 - widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 64;
			if (colOfExtendedImage[0 - widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 32;
			if (colOfExtendedImage[0 + 1] >= colOfExtendedImage[0])
				LBPValue += 16;
			if (colOfExtendedImage[0 + widthOfExtendedImage + 1] >= colOfExtendedImage[0])
				LBPValue += 8;
			if (colOfExtendedImage[0 + widthOfExtendedImage] >= colOfExtendedImage[0])
				LBPValue += 4;
			if (colOfExtendedImage[0 + widthOfExtendedImage - 1] >= colOfExtendedImage[0])
				LBPValue += 2;
			if (colOfExtendedImage[0 - 1] >= colOfExtendedImage[0])
				LBPValue += 1;
			colOfLBPImage[0] = LBPValue;
		}
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章