Visual Words Opencv实现

 

1.主要步骤:

1.调用opencv获取文件夹下所有图片路径

glob(pattern, fn, true);

得到:

data\graf\img1.ppm

data\graf\img2.ppm

data\graf\img3.ppm

...........

2.依次读入每幅图像,并计算sift特征点和特征向量(128维)

sift(img, mascara, key_points, descriptors);//执行sift运算

这里每张图片获取前1000个特征点

 

3.对所有图片的特征向量进行kmeans聚类,生成k个聚类中心,即k个visual words

K - Means ++ 算法

选择初始seeds的基本思想是:初始的聚类中心之间的相互距离要尽可能的远。

 

1 从输入的数据点集合中随机选择一个点作为第一个聚类中心;

2 对于数据集中的每一个点x,计算它与最近聚类中心(指已选择的聚类中心)的距离D(x);

3 选择D(x)最大的数据点作为新的聚类中心,可以使初始聚类中心尽可能分散

4 重复2和3直到k个聚类中心被选出来。

利用这k个初始的聚类中心来运行标准的k - means算法。

 

聚类数量k的选择——手肘法

手肘法的核心指标是SSE(sum of the squared errors,误差平方和)。手肘法的核心思想是:随着聚类数k的增大,样本划分会更加精细,每个簇的聚合程度会逐渐提高,那么误差平方和SSE自然会逐渐变小。并且,当k小于真实聚类数时,由于k的增大会大幅增加每个簇的聚合程度,故SSE的下降幅度会很大,而当k到达真实聚类数时,再增加k所得到的聚合程度回报会迅速变小,所以SSE的下降幅度会骤减,然后随着k值的继续增大而趋于平缓,也就是说SSE和k的关系图是一个手肘的形状,而这个肘部对应的k值就是数据的真实聚类数。当然,这也是该方法被称为手肘法的原因。

 

使用Python编程计算不同k值的SSE:

def test_kmeans(sift_features):
    '利用SSE选择k'
    SSE = []  # 存放每次结果的误差平方和
    for k in range(50, 1500,50):
        estimator = KMeans(n_clusters=k)  # 构造聚类器
        estimator.fit(sift_features)
        SSE.append(estimator.inertia_)

    X = range(50, 1500,50)
    plt.xlabel('k')
    plt.ylabel('SSE')
    plt.plot(X, SSE, 'o-')
    plt.show()

输出如下,我们选取k=1000

4.给出一幅图像,计算其visual words直方图,从所有图像中检索与该图像直方图相似的前5张图像,统计Precision/Recall(该数据集每类图像只有6张)

//计算一张图片的单词直方图,并归一化

vector<float>  calHistogram(int imgId) {

         vector<float> imgH;

         long total = 0;

         for (int i = 0; i < WORDS_NUM; i++) {

                  int count = 0;

                  for (int j = 0; j < words_table[i].size(); j++) {

                          if (words_table[i][j] == imgId)

                                   count++;

                  }

                  total += count;

                  imgH.push_back(count);

         }

         //归一化

         for (int i = 0; i < imgH.size(); i++) {

                  imgH[i] /= total;

         }

         return imgH;

}

2.实验结果

随机选择10张图片,检索最相近的5张统计Precision/Recall

 

3.部分代码

主函数:

int main() {

	bool isReadDictionary = true;
	//bool isReadDictionary = false;
	
	vector<int> num_list;//存储每幅图片的特征点数目

	if(!isReadDictionary){
		
		img_path_list = getImgPath("data/*.p?m");

		cout << "total images:" << img_path_list.size() << endl;

		vector<Mat> feature_list;
		

		//计算sift特征值并存储在文件
		num_list=calSiftAndSave();

		int total_feature = 0;
		for (int i = 0; i < num_list.size(); i++) {
			total_feature += num_list[i];
		}
		int total_img = num_list.size();
		//存储所有图片的sift特征
		Mat featureMat = Mat::zeros(total_feature, 128, CV_32FC1);

		// 从文件中读取内容到 Mat 中
		FILE* sift_file = fopen(sift_file_path.c_str(), "rb");
		if (sift_file == NULL) {
			printf("打开sift_file失败");
			return -1;
		}
		fread(featureMat.data, featureMat.elemSize(), 128 * total_feature, sift_file);
		fclose(sift_file);


		//kmeans聚类,label.type()=CV_32S
		Mat labels = calKmeans(featureMat);

		// Calculate the inverted file index
		int* pData = (int*)labels.data;

		for (int i = 0; i < num_list.size(); i++)
		{
			for (int j = 0; j < num_list[i]; j++) {
				words_table[*(pData)].push_back(i);
				pData++;
			}
		}
		//保存字典便于以后查询
		saveWordsTable();
	}else {
		//读取每个图片路径和特征点数量
		FILE* info_file = fopen(info_file_path.c_str(), "rb");
		if (info_file == NULL) {
			printf("打开info失败");
			return -1;
		}
		while (!feof(info_file)) {
			int num;
			string path;
			path.resize(100);
			fscanf(info_file, "%s\n%d\n", &path[0],&num);
			img_path_list.push_back(path);
			num_list.push_back(num);
		}
		fclose(info_file);

		readWordsTable();//读取字典
	}

	vector<int> imgs = getSimilarImg(33);

	for (int i = 0; i < imgs.size() && i < 10; i++) {
		imshow("SimilarImg", imread(img_path_list[imgs[i]]));
		waitKey(0);
	}

 	waitKey(0);
	return 0;
}

计算并保存sift特征值 

vector<int> calSiftAndSave() {
	
	FILE* sift_file = fopen(sift_file_path.c_str(), "wb");
	vector<int> num_list;
	if (sift_file == NULL) {
		printf("写文件sift_file失败!\n");
		return num_list;
	}

	for (int i = 0; i < img_path_list.size(); i++) {
		cout << img_path_list[i] << "  sift: ";
		Mat features = calSIFT(img_path_list[i], feature_num_per_img);
		num_list.push_back(features.rows);

		cout << "feature  num:" << features.rows << "   dim:" << features.cols << endl;

		//0000 4040 0000 5041 0000 3041 0000 0040  一行存四个CV_32FC1数据,一个vector有128维
		fwrite(features.data, features.elemSize(), features.cols * features.rows, sift_file);

	}
	fclose(sift_file);

	FILE* info_file = fopen(info_file_path.c_str(), "wb");
	if (info_file == NULL){
		printf("写文件info_file失败!\n");
		return num_list;
	}//存储图片路径和特征点个数
	for (int i = 0; i < num_list.size(); i++) {
		fprintf(info_file, "%s\n%d\n", img_path_list[i].c_str(),num_list[i]);
	}
	fclose(info_file);

	return num_list;
}

对sift特征向量进行kmeans聚类

Mat calKmeans(Mat data) {

	Mat labels; //索引
	int attempts = 5; //是否合适?
	Mat centers; //中心
	Mat centerColor(1, WORDS_NUM, data.type()); //存储每一聚类的颜色
	Mat percent;
	percent = cv::Mat::zeros(1, WORDS_NUM, CV_32F); //每一聚类占的比例
	
	double compactness = cv::kmeans(data, WORDS_NUM, labels, TermCriteria(CV_TERMCRIT_ITER | CV_TERMCRIT_EPS, 0.0001, 10000),
		attempts, KMEANS_PP_CENTERS, centers);

	cout << "attempts:" << attempts << endl;
	cout << "compactness" << compactness << endl;

	return labels;
}
//计算一张图片的单词直方图,并归一化 
vector<float>  calHistogram(int imgId) {
	vector<float> imgH;
	long total = 0;
	for (int i = 0; i < WORDS_NUM; i++) {
		int count = 0;
		for (int j = 0; j < words_table[i].size(); j++) {
			if (words_table[i][j] == imgId)
				count++;
		}
		total += count;
		imgH.push_back(count);
	}
	//归一化
	for (int i = 0; i < imgH.size(); i++) {
		imgH[i] /= total;
	}

	return imgH;
}



//计算每幅图像的words直方图,返回最相似的图片id
vector<int> getSimilarImg(int imgId) {
	vector<float> imgHist = calHistogram(imgId);

	vector<vector<float>> imgHistList;

	map<float, int> distMap;
	for (int i = 0; i < img_path_list.size(); i++) {
		imgHistList.push_back(calHistogram(i));
		float dis=calEuclideanDis(imgHist, imgHistList[i]);
		distMap[dis] = i;
	}

	vector<int> imgs;
	for (auto it = distMap.begin(); it != distMap.end(); it++) {
		imgs.push_back(it->second);
		printf("img=%s\nsimilarity = %lf\n", img_path_list[it->second].c_str(),it->first);
	}

	return imgs;

}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值