目录
一、实验目的、思路、环境
二、数据集
三、实验步骤
四、实验结果
五、总结
六、拓展
目的:实现“以图搜图”的功能,给定一张测试图像,在训练集中找出最接近的10张图像(实际上是检索类别相同的图像)
基本思路:对训练集中的已有图像进行特征提取,对所提取的特征建立索引;在检索时,对测试图像进行同样的特征提取,然后将测试图像所提取的特征和训练集图像的特征进行匹配,检索出匹配度最高的10张图像
实验环境:MATLAB
所用的数据集是cifar100,包含60000张32*32尺寸的彩色图像,这些图像被分为100个类,每个类包含600张图像,其中500张作为训练图像,100张作为测试图像;这100个类又被分为20个超类,每张图像带有一个类标签(细分类标签)和一个超类标签(粗分类标签),每张图像的具体数据格式可以参考表格,整个数据集的格式可以查阅别的资料,关键字是一样的,维度就是图像数量*每张图像该关键字维度,比如训练集的data的维度是50000*3072,filenams的维度是50000*1
关键字 | 示例值 | 说明 |
data | (R...R, G...G, B...B) | 每张图像的数据,维度是32*32*3=3072 |
filenames | quercus_cerris_s_000351.png | 每张图像的文件名 |
fine_labels | 17 | 每张图像的细分类标签,取值范围是[0,20] |
coarse_labels | 52 | 每张图像的粗分类标签,取值范围是[0,99] |
说明:由于这里是对一张图像做图像检索,而不是对整个测试集的每张图像都做检索,所以在实现时先提取测试图像的特征,再在对训练集图像进行特征提取时一一与测试图像的特征进行匹配,保存相似度,最后对相似度进行排序得到最相似的10张图像
首先写一个ImgExtract函数,用于将每张图像的data数据转成能显示的图像格式,它的输入是图像序号(用户指定)和cifar100的测试集或训练集的data数据,输出是image,根据cifar100的图片格式进行reshape就可以了,因为是彩色图像所以是3通道,最后获取这个输出直接用imshow(image)就可以显示图片:
% 根据图片序号提取图片
function[image] = ImgExtract(img_id, data)
image(:,:,1) = reshape(data(img_id, 1:1024), [32 32])';
image(:,:,2) = reshape(data(img_id, 1025:2048), [32 32])';
image(:,:,3) = reshape(data(img_id, 2049:end), [32 32])';
end
下面这段代码是根据id获取指定测试图像的相关信息并保存,其中q_img就是通过调用ImgExtract函数获得的,然后新建一个窗口,分成3行5列,让测试图像显示在第一行的中间一列,命名为“粗分类标签,细分类标签”:
%输入查询图像
q_imgid = input('input query image id (1~10000):'); % 输入查询id
load test.mat % 读取测试集
q_img = ImgExtract(q_imgid, data); % 根据id获取测试图像
q_name = filenames(q_imgid); % 获取测试图像的文件名
q_coarselabel = coarse_labels(q_imgid); % 获取测试图像的粗分类标签
q_finelabel = fine_labels(q_imgid); % 获取测试图像的细分类标签
figure(); % 新建窗口
subplot(3,5,3); % 指定测试图像显示位置
imshow(q_img); % 显示测试图像
title([num2str(q_coarselabel), ', ', num2str(q_finelabel)]); % 命名
然后提取颜色特征,这里先提取颜色直方图作为特征。直接在RGB颜色空间操作是不行的,因为它是不均匀的颜色空间,两种颜色之间的知觉差异不能通过空间中两个颜色点之间的距离来表示,而在对彩色图像进行特征提取时,需要用数量来描述颜色的差别,所以需要将RGB空间转到更符合人类视觉的HSV空间,MATLAB中只需调用rgb2hsv函数进行实现即可。
通常情况下,一幅图像的颜色非常多,尤其是彩色图像,因此直方图矢量的维数会非常多。如果在将RGB颜色空间转成HSV颜色空间后不做处理,计算量会非常大。因此可以先对H,S,V这3个分量按照人的颜色感知进行非等间隔的量化后再计算直方图。量化公式可以查一下资料,这里选取的是较为常用的量化方法,将H分为8级,S分为3级,V分为3级。量化后再按公式L = 9H + 3S + V 进行合成,可以算出L中最大的数是9*7+3*2+2=71,最小的数是0,因此直方图的维数是72,再获取颜色直方图,由于这里每张图片的大小是一致的,所以可以不用进行归一化,代码实现如下:
% 色彩空间的转换以及量化,构造直方图
function[img_hist] = HSVHist(img)
[img_h, img_s, img_v] = rgb2hsv(img);
img_h = img_h * 360;
[H, S, V] = deal(img_h, img_s, img_v);
H(img_h >= 0 & img_h <= 20) = 0;
H(img_h >= 316 & img_h <= 360) = 0;
H(img_h >= 21 & img_h <= 40) = 1;
H(img_h >= 41 & img_h <= 75) = 2;
H(img_h >= 76 & img_h <= 155) = 3;
H(img_h >= 156 & img_h <= 190) = 4;
H(img_h >= 191 & img_h <= 270) = 5;
H(img_h >= 271 & img_h <= 295) = 6;
H(img_h >= 296 & img_h <= 315) = 7;
S(img_s >= 0 & img_s <= 0.2) = 0;
S(img_s > 0.2 & img_s <= 0.7) = 1;
S(img_s > 0.7 & img_s <= 1) = 2;
V(img_v >= 0 & img_v <= 0.2) = 0;
V(img_v > 0.2 & img_v <= 0.7) = 1;
V(img_v > 0.7 & img_v <= 1) = 2;
L = 9 * H + 3 * S + V;
% 计算直方图
for i = 0:71
% Hist(i + 1) = size(find(L == i), 1) / (img_height * img_width);
Hist(i + 1) = size(find(L == i), 1);
end
img_hist = Hist;
end
也可以使用累积直方图,但我试了发现效果更差,计算累积直方图的代码如下:
% 计算累积直方图
for i = 1:71
Hist(i+1) = Hist(i+1) + Hist(i);
end
也可以使用颜色矩作为特征,颜色信息主要分布于低阶矩中,所以主要利用一阶矩(均值)、二阶矩(方差)、三阶矩来表达图像的颜色分布,公式可以查阅别的资料。这里提取图像H、S、V三个分量的前三阶颜色矩组成一个9维向量color_moment作为特征,代码如下:
% 色彩空间的转换以及量化,构造直方图
function[color_moment] = ColorMoment(img)
[img_height, img_width, c] = size(img);
[img_h, img_s, img_v] = rgb2hsv(img);
% 提取一阶矩,均值
h_mean = mean2(img_h);
s_mean = mean2(img_s);
v_mean = mean2(img_v);
% 提取二阶矩,方差
img_size = img_height * img_width;
h_var = sqrt(sum(sum((img_h - h_mean).^2)) / img_size);
s_var = sqrt(sum(sum((img_s - s_mean).^2)) / img_size);
v_var = sqrt(sum(sum((img_v - v_mean).^2)) / img_size);
% 提取三阶矩
h_third = (sum(sum((img_h - h_mean).^3)) / img_size).^1/3;
s_third = (sum(sum((img_s - s_mean).^3)) / img_size).^1/3;
v_third = (sum(sum((img_v - v_mean).^3)) / img_size).^1/3;
color_moment = [h_mean , s_mean , v_mean, h_var, s_var, v_var, h_third, s_third, v_third];
end
然后选定一个特征表示,选用颜色直方图或者选颜色矩都可以,提取测试图像的特征:
q_imghist = HSVHist(q_img); % 使用颜色直方图
q_imghist = ColorMoment(q_img); % 使用颜色矩
然后对训练集的每张图像提取特征并与测试图像的特征进行匹配,度量方法有直方图相交法和欧氏距离法:
直方图相交法的公式如下,其中和分别为查询图像Q和数据库图像D的直方图,L表示直方图的灰度级数,两幅图像之间的相似度P(Q,D)的值越高说明相似性越大:
欧氏距离的公式如下,值越低说明相似度越大:
提取训练集图像的特征并进行相似度计算的代码如下,可根据需要对代码进行修改,下面的代码是提取颜色直方图并计算欧氏距离,p(i)保存了第i张训练图像与测试图像的相似度:
load train.mat
for i = 1:size(data, 1)
train_img = ImgExtract(i, data);
train_imghist = HSVHist(train_img); % 提取颜色直方图作为特征
% train_imghist = ColorMoment(train_img); % 提取颜色矩作为特征
% p(i) = sum(min(q_imghist, train_imghist)) / sum(q_imghist); % 直方图相交法
diff = (q_imghist - train_imghist).^2; % 计算欧氏距离
p(i) = sqrt(sum(diff(:)));
end
接下来就是对p中的所有值进行排序,注意直方图相交法的值是越大越好,所以是进行降序排序,而欧氏距离的值是越小越好,所以是进行升序排序,最后取前10个,显示前10张图像的思路和之前显示测试图像的思路是一致的,这里的cnt_fine是统计10张图像中匹配测试图像细分类标签的个数,cnt_coarse是统计匹配测试图像粗分类标签的个数,代码如下:
% [B, IX] = sort(p, 2, 'descend'); % 降序排列
[B, IX] = sort(p, 2); % 升序排列
B = B(1:10);
IX = IX(1:10); % 获取前10张图像的下标
similar_coarse = coarse_labels(IX); % 获取前10张图像的粗分类标签
cnt_coarse = length(find(similar_coarse == q_coarselabel)); % 求匹配粗分类标签的个数
similar_fine = fine_labels(IX); % 获取前10张图像的细分类标签
cnt_fine = length(find(similar_fine == q_finelabel)); % 求匹配细分类标签的个数
% 显示前10张最相似的图像
for i = 1:10
subplot(3,5,i+5);
imshow(ImgExtract(IX(i), data));
name_str=[num2str(coarse_labels(IX(i))),', ',num2str(fine_labels(IX(i)))];
title(name_str);
end
先看一个检索效果比较好的例子:对图像quercus_cerris_s_000351.png进行检索(输入是143),测试图像的粗分类标签是17,细分类标签是52:
用颜色直方图做特征并欧氏距离进行度量的结果如图,可以看到10张图像中有6张匹配测试图像的粗分类标签(分别排名1/2/3/4/5/9),2张匹配细分类标签(分别排名1/9):
同样的图像用颜色直方图做特征并用直方图相交法进行度量的结果如图,可以看到10张图像中有5张匹配测试图像的粗分类标签(分别排名1/2/3/8/10),2张匹配细分类标签(分别排名1/3):
用颜色矩做特征并用欧氏距离进行度量的结果如图,可以看到10张图像中有6张匹配测试图像的粗分类标签(分别排名1/2/3/5/6/7),4张匹配细分类标签(分别排名1/2/5/7):
再看一个检索效果较差的例子:对图像lamp_s_001350.png进行检索(输入是15),测试图像的粗分类标签是5,细分类标签是40:
用颜色直方图做特征并欧氏距离进行度量的结果如图,可以看到10张图像中没有一张能匹配测试图像的粗分类和细分类标签,但检索出来的10张图像在颜色上都和测试图像很接近:
用颜色直方图做特征并直方图相交法进行度量的结果如图,同样没有能匹配的:
用颜色矩做特征并用欧氏距离进行度量的结果如图,也没有能匹配的:
以上是2个特殊的例子,一般的检索结果中会有1到2张能匹配测试图像的粗分类标签,有0到1张能匹配细分类标签,可以自己跑一下程序看结果
三种方法(颜色直方图+欧氏距离、颜色直方图+直方图相交法、颜色矩+欧氏距离)的效果差别不大,最后检索到的图像都是和测试图像颜色上比较接近的,基于直方图的方法在颜色上会更相似一点,检索效果也好一些,但精度都不高
从实验结果中可以看出,基于颜色特征的检索效果取决于测试图像的颜色特征是否具有代表性,如果数据集中不同类别的图像颜色差异大,用颜色特征做检索易于实现,效果也还可以;但颜色特征无法描述图像中的具体对象,也提取不到图像的高层语义信息(高层语义信息可以通过深度学习的方法提取),如果数据集不同类别的图像颜色都比较接近,不太适合用颜色特征做检索
如果保存了测试图像,不想通过输入id来观察,可以直接从文件夹中获取测试图像,调用库函数uigetfile,例如
[filename, pathname] = uigetfile({'*.jpg'; '*.png'}, 'Choose a picture');
之后可以通过filename获取标签、data等;也可以换成自己的数据集进行操作,需要修改相应代码