基于上周的工作,我继续研究了三种图像检索方法。
第三种方法是大津法,通过确定灰度图阈值,对灰度图上的像素实现前景/背景的二分类,对于这个二分类的结果行程一个0或1 组成的指纹。核心代码如下:
int Retrieval_Color_otsu() { ifstream in; in.open("color_otsu.txt", ios::in); if (in.fail()) return -1; string otsu = Color_otsu(-1, TEST_MODE); string line; int len = otsu.size(); int minRes = 1e4; int winner = -1; int res = 0; for (int i = 1; i <= MAX_PICNNUM; i++) { getline(in, line, '\n'); // cout << line << endl; res = 0; for (int j = 0; j<len; j++) { if (otsu[j] != line[j]) { res++; } if (res >= minRes) break; } if (res<minRes) { // cout << minRes << endl; minRes = res; winner = i; } } cout << "otsu算法选出的图片是:" << winner << endl; cout << "差异是" << minRes << endl; return winner; }
经过试验表明,大津法的检索速度还行,但是由于需要对所有图片进行resize,失去了原来的部分空间信息,检索效果并不好。
第四种方法,基于灰度共生矩阵。灰度共生矩阵GLCM通过横向、纵向、对角线、逆对角线四个方向的相邻像素检测,构造四个方向上的特征矩阵,对于每个矩阵求出能量、熵、对比度、逆差矩四个特征向量,从而作为每张图片的特征表示。
string Texture_GLCM(int srcPicNum, int mode) { Mat src; const int size = 60; if (mode == 0) { src = imread(getPath(srcPicNum),CV_LOAD_IMAGE_COLOR); } else { src = UserChosenMat; } resize(src, src, Size(size, size)); Mat gray; cvtColor(src, gray, CV_RGB2GRAY); int tempMat[size][size]; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { int value = gray.at<uchar>(i, j); value /= 16; tempMat[i][j] = value; //cout << value << " "; } //cout << endl; } int dst[16][16]; int total = 0; double dstNorm[16][16]; //feature[0]:energy 能量 //feature[1]:entropy 熵 //feature[2]:contrast 对比度 //feature[3]:idMomont 逆差矩 double feature[4]; memset(feature, 0.0, 4 * sizeof(double)); //水平方向,从左指向右 memset(dst, 0, 16 * 16 * sizeof(int)); memset(dstNorm, 0.0, 16 * 16 * sizeof(double)); total = 0; for (int i = 0; i < size; i++) { for (int j = 0; j < size - 1; j++) { int row = tempMat[i][j]; int col = tempMat[i][j + 1]; dst[row][col]++; total++; } }//下面高斯正则化,并计算四个特征 for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { //cout << dst[i][j] << " "; dstNorm[i][j] = (double)dst[i][j] / (double)total; //cout << dstNorm[i][j] << endl; feature[0] += dstNorm[i][j] * dstNorm[i][j]; if(dstNorm[i][j]>0) feature[1] -= dstNorm[i][j] * log(dstNorm[i][j]); feature[2] += (double)(i - j)*(double)(i - j)*dstNorm[i][j]; feature[3] += dstNorm[i][j] / (1 + (double)(i - j)*(double)(i - j)); } //cout << endl; } //竖直方向,从左指向右 memset(dst, 0, 16 * 16 * sizeof(int)); memset(dstNorm, 0.0, 16 * 16 * sizeof(double)); total = 0; for (int i = 0; i < size - 1; i++) { for (int j = 0; j < size; j++) { int row = tempMat[i][j]; int col = tempMat[i+1][j]; dst[row][col]++; total++; } } for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { //cout << dst[i][j] << " "; dstNorm[i][j] = (double)dst[i][j] / (double)total; feature[0] += dstNorm[i][j] * dstNorm[i][j]; if (dstNorm[i][j]>0) feature[1] -= dstNorm[i][j] * log(dstNorm[i][j]); feature[2] += (double)(i - j)*(double)(i - j)*dstNorm[i][j]; feature[3] += dstNorm[i][j] / (1 + (double)(i - j)*(double)(i - j)); } //cout << endl; } //45°方向,从左上指向右下 memset(dst, 0, 16 * 16 * sizeof(int)); memset(dstNorm, 0.0, 16 * 16 * sizeof(double)); total = 0; for (int i = 0; i < size - 1; i++) { for (int j = 0; j < size - 1; j++) { int row = tempMat[i][j]; int col = tempMat[i + 1][j + 1]; dst[row][col]++; total++; } } for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { dstNorm[i][j] = (double)dst[i][j] / (double)total; feature[0] += dstNorm[i][j] * dstNorm[i][j]; if (dstNorm[i][j]>0) feature[1] -= dstNorm[i][j] * log(dstNorm[i][j]); feature[2] += (double)(i - j)*(double)(i - j)*dstNorm[i][j]; feature[3] += dstNorm[i][j] / (1 + (double)(i - j)*(double)(i - j)); } } //135°方向,从右上指向左下 memset(dst, 0, 16 * 16 * sizeof(int)); memset(dstNorm, 0.0, 16 * 16 * sizeof(double)); total = 0; for (int i = 0; i < size - 1; i++) { for (int j = 1; j < size; j++) { int row = tempMat[i][j]; int col = tempMat[i + 1][j - 1]; dst[row][col]++; total++; } } for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { dstNorm[i][j] = (double)dst[i][j] / (double)total; feature[0] += dstNorm[i][j] * dstNorm[i][j]; if (dstNorm[i][j]>0) feature[1] -= dstNorm[i][j] * log(dstNorm[i][j]); feature[2] += (double)(i - j)*(double)(i - j)*dstNorm[i][j]; feature[3] += dstNorm[i][j] / (1 + (double)(i - j)*(double)(i - j)); } } string result; for (int i = 0; i < 4; i++) { feature[i] /= 4; if (feature[i] < min_glcm_feature[i]) min_glcm_feature[i] = feature[i]; if (feature[i] > max_glcm_feature[i]) max_glcm_feature[i] = feature[i]; result.append(to_string(feature[i])); //cout << feature[i] << endl; if (i != 3) result.push_back(','); } //cout << endl << endl << endl; /*cout << endl << endl << result << endl;*/ return result; }
对于GLCM的检索,由于四个特征每个特征的取值范围不同,需要记录一下所有图片中所有特征的最大值和最小值,用min - max的方法进行归一化。
int Retrieval_Texture_glcm() { ifstream in; string line; double min[4], range[4]; in.open("texture_glcm_minmax.txt", ios::in); getline(in, line, '\n'); vector<string> minstr = split(line, ","); getline(in, line, '\n'); vector<string> maxstr = split(line, ","); for (int i = 0; i < 4; i++) { //cout << minstr[i] << endl; min[i] = stod(minstr[i]); range[i] = stod(maxstr[i]) - min[i]; } in.close(); in.open("texture_glcm.txt", ios::in); if (in.fail()) return -1; string gclm_feature = Texture_GLCM(-1, TEST_MODE); vector<string> strvec = split(gclm_feature, ","); double vec[4]; double alen = 0; for (int i = 0; i < 4; i++) { vec[i] = (stod(strvec[i]) - min[i]) / range[i]; alen += vec[i] * vec[i]; } alen = sqrt(alen); double maxRes = -2; int winner = -1; double cosRes = 0; double curvec[4]; double blen = 0; double product = 0; for (int i = 1; i <= MAX_PICNNUM; i++) { if (i % 100 == 0) cout << i << " 处理完成"<< endl; getline(in, line, '\n'); cosRes = 0; blen = 0; product = 0; strvec.clear(); strvec = split(line, ","); for (int i = 0; i < 4; i++) { curvec[i] = (stod(strvec[i]) - min[i]) / range[i]; product += curvec[i] * vec[i]; blen += curvec[i] * curvec[i]; } blen = sqrt(blen); cosRes = product / (alen*blen); //cout << i << "图片: "<< cosRes << endl; if (cosRes > maxRes) { cout << setprecision(14) << maxRes << endl; maxRes = cosRes; winner = i; } } cout << "glcm算法选出的图片是:" << winner << endl; cout << setprecision(14) << "余弦相似度是" << maxRes << endl; return winner; }
灰度共生矩阵的效果相当不错,而且检索时间较短,可以达到检索需要。