基于上周的工作,我继续研究了三种图像检索方法。
第三种方法是大津法,通过确定灰度图阈值,对灰度图上的像素实现前景/背景的二分类,对于这个二分类的结果行程一个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= minRes) break;
}
if (res
经过试验表明,大津法的检索速度还行,但是由于需要对所有图片进行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(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 minstr = split(line, ",");
getline(in, line, '\n');
vector 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 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;
}
灰度共生矩阵的效果相当不错,而且检索时间较短,可以达到检索需要。