在导入完图像后,接下来是选择参与的算法,GUIDE默认点击让该多选框的value改变,不用自己挨个写在callback函数里。
三、评估按钮(pushbutton3):
点击后会将右侧的图像和导入参考图像按钮隐藏掉(腾出空间显示结果和日志)。使用warning('off')关闭警告,使用tic,toc计时。使用diary('xxxx.txt')用来写入运行时在命令行窗口显示的中间结果、提示等,diary on开始写入 diary off停止。首先,对于全局图像I1(和I2),可能存在图像过大或过小的问题,首先将图像缩放到合适的大小,如果是全参考方法可能存在两个图不一样大的问题,首先在pushbutton3的callback中调用自定义函数prepossess进行图像的如预处理:
function prepossess(hObject, eventdata, handles)
if exist('rs.mat')
else
scale=1;
save('rs.mat','scale');
end
load('rs.mat')
global I1;
global I1_s;
global I2;
global I2_s;
scale_s=power(2,scale-1);
I1_s=imresize(I1,1/(scale_s));
if get(handles.radiobutton1,'Value')
I2_s=imresize(I2,1/(scale_s));
end
%将I2调整到I1 相同大小
% 大于1000或小于100则调整
[m1 n1 d1]=size(I1_s);
if m1<100
I1_s=imresize(I1_s,[500,500/m1*n1],'bicubic');
elseif m1>1000
I1_s=imresize(I1_s,[500,500/m1*n1],'bicubic');
end
if get(handles.radiobutton1,'Value')
[m2 n2 d2]=size(I2_s);
if sum(abs(size(I1_s)-size(I2_s)))~=0
if size(I1_s,1)
I1_s和I2_s为I1、2处理后的图像,后续评估的都是处理后的图像,该函数前半截先读取rs.mat(如果没有那就把1写入)数据先缩放(下采样)[在之后介绍],之后根据大小,如果高小于100或大于1000,那么等比例双三次插值(bicubic)缩放到高为500,如果两个图不一样大(sum(abs(size-size))~=0),那么高度稍大的图像会调整到另一图像等大。
2.调用完prepossess后,buttonpush3_callback函数使用全局图像I1_s,I2_s,
(1)如果是全参考(RF),添加路径(addpath,用于读取算法)检查checkbox的值,用来创建存放结果的元组stringcell【使用元组原因:每个元素的类型可以不同,每行的列数可以不同】。遍历时控件名的获取:
eval(strcat('handles.checkbox',num2str(i)))
如果该checkbox的value为1,则计数+1,(代码中出现i为多少,再次加1,是因为有的checkbox对应多个算法,即如果该checkbox打勾,就输出多个算法的结果)。
置stringcell的下标index为0,再次遍历每个checkbox是否打勾,由于每个checkbox对应的算法和处理结果不尽相同,所以是挨个写的。如果该checkbox打勾:index自增1,然后进度条更新:
mywaitbar(index/num,handles,['Start to Calculate MSE and PSNR ',num2str(floor(index*100/num)),'%']);
num为算法总数,[]内为显示的文字和百分数。
之后调用对应的算法(放于某目录下),获得返回值,写入stringcell的第index行,一共四列,分别为算法名,分数,最高分数(手动赋值),归一化后分数(线性映射到【0,1】区间,0最低,1最高)。
遍历结束后,执行mywaitbar(1,handles,'Calculate success.');显示计算完成。
(2)无参考同理(NF)
执行完后,toc计时结束,diary off写入结束。调用自定义函数possess处理
(3)possess:
大致目的:读取rs.mat【之后提到】,获得三个值radp(决定保留几位),ef(有效数字),fn(是否强制归一化),如果不存在mat则写入。对里边的数进行有效数字(vpa)和小数点后保留位数(round)的处理,并根据fn的值对一些不可归一化(最大值不存在或趋向于某个值)的归一化分数那列置为“---”,并在stringcell的第五列(新的一列)写入可修改的数值(其他列不可修改),数值为1/行数,也就是权值,默认为平均,用户可在界面修改。如果修改的数值不为数字,则会弹出提示并清空。最后显示在uitable。(令uitable的data为string_s),string_s为stringcell处理后的元组。
function possess(hObject, eventdata, handles,stringcell)
if exist('var.mat')%存在 则读入
else
radp=4;%小数点后保留几位数字
ef=8;%总有效数字
fn=1;%强制归一化
save('var.mat','radp','ef','fn');
end
load('var.mat')
[m,n]=size(stringcell);
string_s=cell(m,n+1);
for i=1:m
string_s{i,5}=1/m;
string_s{i,1}=stringcell{i,1};%名
if fn==0 &&(strcmp(string_s{i,1},'CNR_NSS') || strcmp(string_s{i,1},'UIQM') || strcmp(string_s{i,1},'PSNR') || strcmp(string_s{i,1},'JND'))%默认为归一化 如果 不采用强制归一化则把对应设置为'---'
string_s{i,2}=char(vpa(round(stringcell{i,2},radp),ef));
string_s{i,3}='---';
string_s{i,4}='---';
else
string_s{i,2}=char(vpa(round(stringcell{i,2},radp),ef));
if ischar(stringcell{i,3})
string_s{i,3}=stringcell{i,3};
else
string_s{i,3}=char(vpa(round(stringcell{i,3},radp),ef));
end
if ischar(stringcell{i,4})
string_s{i,4}=stringcell{i,4};
else
string_s{i,4}=char(vpa(round(stringcell{i,4},radp),ef));
end
end
end
set(handles.uitable,'Visible','on');
set(handles.uitable,'Data',string_s);
set(handles.uitable,'ColumnEditable',[false,false,false,false,true]);%只有最后一列可编辑
set(handles.pushbutton5,'Visible','on');
(4)总的push_callbutton3代码:
function pushbutton3_Callback(hObject, eventdata, handles)%执行按钮
set(handles.pushbutton2,'Visible','off');
warning('off');
diary('D:\log_run.txt');
diary on;
tic;
prepossess(hObject, eventdata, handles);
global I1_s;
global I2_s;
if get(handles.radiobutton1,'Value')%关闭第二个图的显示
set(handles.axes2,'Visible','off')
set(handles.pushbutton2,'Visible','off')
end
index=0;%stringcell 的 下索引
%%%%%RF%%%%%%%
if get(handles.radiobutton1,'Value')
addpath('RF')
addpath('RF/mallat')
addpath('RF/vif')
addpath('RF/sift2')
num=0;
for i=1:8 %检查8个checkbox打勾情况
if get(eval(strcat('handles.checkbox',num2str(i))),'Value')
num=num+1;
if i==1 || i==6
num=num+1;
end
end
end
stringcell=cell(num,4); %多行字符串 num行2列 第一列 算法名字 第二列 分数 第五列 可编辑的加权
if get(handles.checkbox1,'Value')%第一个方法
mywaitbar(index/num,handles,['Start to Calculate MSE and PSNR ',num2str(floor(index*100/num)),'%']);
[PSNR, MSE] = mseandpsnr(I1_s, I2_s);
index=index+1;
stringcell{index,1}='MSE';
stringcell{index,2}=MSE;
stringcell{index,3}=0;%mse 最高分 0 最低分 255*255
stringcell{index,4}=(65025-MSE)/65025;
index=index+1;
stringcell{index,1}='PSNR';
stringcell{index,2}=PSNR;
stringcell{index,3}=Inf;
stringcell{index,4}=strcat(num2str(PSNR),'/Inf');
mywaitbar(index/num,handles,['MSE and PSNR Get ',num2str(floor(index*100/num)),'%']);
end
%第二个方法
if get(handles.checkbox2,'Value')
mywaitbar(index/num,handles,['Start to Calculate JND ',num2str(floor(index*100/num)),'%']);
JND=mallat(I1_s);
JND2=mallat(I2_s);
index=index+1;
stringcell{index,1}='JND';
stringcell{index,2}=JND;
stringcell{index,3}=JND2;
stringcell{index,4}=JND/JND2;
mywaitbar(index/num,handles,['JND Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox4,'Value')
K = [0.05 0.05];
window = ones(8);
L = 100;
mywaitbar(index/num,handles,['Start to Calculate SSIM ',num2str(floor(index*100/num)),'%']);
[mssim, ~] = ssim(rgb2gray(I1_s), rgb2gray(I2_s), K, window, L);
index=index+1;
stringcell{index,1}='SSIM';
stringcell{index,2}=mssim;
stringcell{index,3}=1;
stringcell{index,4}=mssim;
mywaitbar(index/num,handles,['SSIM Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox5,'Value')
mywaitbar(index/num,handles,['Start to Calculate SIExt ',num2str(floor(index*100/num)),'%']);
[SIExT] = SIExt(I1_s,I2_s);
index=index+1;
stringcell{index,1}='SIExt';
stringcell{index,2}=SIExT;
stringcell{index,3}=1;
stringcell{index,4}=SIExT;
mywaitbar(index/num,handles,['SIExt Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox6,'Value')
mywaitbar(index/num,handles,['Start to Calculate QILV ',num2str(floor(index*100/num)),'%']);
[QILV] = qilv(I1_s,I2_s);
index=index+1;
stringcell{index,1}='QILV';
stringcell{index,2}=QILV;
stringcell{index,3}=1;
stringcell{index,4}=QILV;
mywaitbar(index/num,handles,['QILV Get ',num2str(floor(index*100/num)),'%']);
mywaitbar(index/num,handles,['Start to Calculate PCQI ',num2str(floor(index*100/num)),'%']);
[mpcqi,~] = PCQI(I1_s,I2_s);
index=index+1;
stringcell{index,1}='PCQI';
stringcell{index,2}=mpcqi;
stringcell{index,3}=1;
stringcell{index,4}=mpcqi;
mywaitbar(index/num,handles,['PCQI Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox7,'Value')
mywaitbar(index/num,handles,['Start to Calculate VIF ',num2str(floor(index*100/num)),'%']);
vif=FR_VIF(double(rgb2gray(I1_s)),double(rgb2gray(I2_s)));
index=index+1;
stringcell{index,1}='VIF';
stringcell{index,2}=vif;
stringcell{index,3}=1;
stringcell{index,4}=vif;
mywaitbar(index/num,handles,['VIF Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox8,'Value')
mywaitbar(index/num,handles,['Start to Calculate FD ',num2str(floor(index*100/num)),'%']);
fd=FD(I1_s,I2_s);
index=index+1;
stringcell{index,1}='FD';
stringcell{index,2}=fd;
stringcell{index,3}=1;
stringcell{index,4}=fd;
mywaitbar(index/num,handles,['FD Get ',num2str(floor(index*100/num)),'%']);
end
mywaitbar(1,handles,'Calculate success.');
end
%%%%NF%%%%
if get(handles.radiobutton2,'Value')
num=0;
for i=1:5 %检查4个checkbox打勾情况
if get(eval(strcat('handles.checkbox',num2str(i))),'Value')
num=num+1;
if i==5
num=num+3;
end
end
end
stringcell=cell(num,4);
addpath('NF')
addpath('NF/BRISQUE')
addpath('NF/ContrastNoReference_NSS');
if get(handles.checkbox1,'Value')%第一个方法BRISQUE
mywaitbar(index/num,handles,['Start to Calculate BRISQUE ',num2str(floor(index*100/num)),'%']);
[brisque] = BRI(I1_s);
index=index+1;
stringcell{index,1}='BRISQUE';
stringcell{index,2}=brisque;
stringcell{index,3}=1;
stringcell{index,4}=brisque;
mywaitbar(index/num,handles,['BRISQUE Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox2,'Value')%ContrastNoReference NSS
mywaitbar(index/num,handles,['Start to Calculate CNR_NSS ',num2str(floor(index*100/num)),'%']);
[nss] = CNR_NSS(I1_s);
index=index+1;
stringcell{index,1}='CNR_NSS';
stringcell{index,2}=nss;
stringcell{index,3}=4;
stringcell{index,4}=nss/4;
mywaitbar(index/num,handles,['CNR_NSS Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox4,'Value')%uciqe
mywaitbar(index/num,handles,['Start to Calculate UCIQE ',num2str(floor(index*100/num)),'%']);
Coe_Metric=[0.4680 0.2745 0.2576];
uciqe=UCIQE(I1_s,Coe_Metric);
index=index+1;
stringcell{index,1}='UCIQE';
stringcell{index,2}=uciqe;
stringcell{index,3}=1;
stringcell{index,4}=uciqe;
mywaitbar(index/num,handles,['UCIQE Get ',num2str(floor(index*100/num)),'%']);
end
if get(handles.checkbox5,'Value')%uciqe
mywaitbar(index/num,handles,['Start to Calculate UIQM ',num2str(floor(index*100/num)),'%']);
[UIQM_norm, colrfulness, sharpness, contrast] = UIQM(I1_s);
index=index+1;
stringcell{index,1}='UIQM';
stringcell{index,2}=UIQM_norm;
stringcell{index,3}=1.5;
stringcell{index,4}=UIQM_norm/1.5;
mywaitbar(index/num,handles,['UIQM Get ',num2str(floor(index*100/num)),'%']);
index=index+1;
stringcell{index,1}='UIQM:colorfulness';
stringcell{index,2}=colrfulness;
stringcell{index,3}='---';
stringcell{index,4}='---';
mywaitbar(index/num,handles,['colorfulness Get ',num2str(floor(index*100/num)),'%']);
index=index+1;
stringcell{index,1}='UIQM:sharpness';
stringcell{index,2}=sharpness;
stringcell{index,3}='---';
stringcell{index,4}='---';
mywaitbar(index/num,handles,['sharpness Get ',num2str(floor(index*100/num)),'%']);
index=index+1;
stringcell{index,1}='UIQM:contrast';
stringcell{index,2}=contrast;
stringcell{index,3}='---';
stringcell{index,4}='---';
mywaitbar(index/num,handles,['contrast Get ',num2str(floor(index*100/num)),'%']);
end
mywaitbar(1,handles,'Calculate success.');
end
toc;
diary off;
axes(handles.axes2);
cla;%关闭图二的显示
possess(hObject, eventdata, handles,stringcell);
四、显示运行日志(pushbutton4)
点击时,如果日志(text2)为开,那么将text2关闭,滑动条关闭(slider1),并置pushbutton4的String为“显示运行日志”,否则,显示text2和slider1,置string为“关闭工作日志”,从指定路径读取(fopen)txt,读取有点麻烦,在这里就不说了,需要配合slider,否则只能显示第一页。
function pushbutton4_Callback(hObject, eventdata, handles)% to display the running log
if strcmp(get(handles.text2,'Visible'),'off') %if close ,then open ;else close
set(handles.text2,'Visible','on')
set(handles.slider1,'Visible','on')
set(handles.pushbutton4,'String','关闭运行日志')
f=fopen('D:\log_run.txt');
ch=textscan(f,'%s');
global strcell;
strcell=cell(floor(length(ch{1})/10),1);
str='';
c=1;
len=0;
for i=1:length(ch{1})
str=strcat(str,32,ch{1}{i});
len=len+1;
if (len>10)
len=0;
strcell{c}=str;
str='';
c=c+1;
end
end
set(handles.text2,'HorizontalAlignment','left')
set(handles.text2,'String',strcell);
row=floor(length(ch{1})/10);
ex=strcell;
if row>18 % 24假设为最大显示行数
set(handles.text2,'String',ex(1:18));
% 设置滑动条位置 不能取整
x=18/row;
set(handles.slider1,'Value',(1-x));
else
set(handles.text2,'String',ex); % 设置滑动条位置
set(handles.slider1,'Value',0);
end
else
set(handles.text2,'Visible','off')
set(handles.slider1,'Visible','off')
set(handles.pushbutton4,'String','显示运行日志')
end
五、滑动条(slider1)
系统给的滑动条只能滑动,但无法根据滑动的大小来改变某个地方的显示,所以需要自己写函数,在这里就用简单地实现了一下:
function slider1_Callback(hObject, eventdata, handles)
f=fopen('D:\log_run.txt');
ch=textscan(f,'%s');
global strcell;
ex=strcell;
row=floor(length(ch{1})/10);
if row>18
% set(hObject,'Value',1-24/row);
slider = get(hObject,'Value');
x1=1-slider;
% 取得当前需要显示的数据量
num=round(row*x1);
if num>18
% 显示对应数据
set(handles.text2,'String',ex(num-18:num));
end
else
% 显示对应数据
set(handles.text2,'String',ex(1:row));
set(hObject,'Value',0);
end
% --- Executes during object creation, after setting all properties.
function slider1_CreateFcn(hObject, eventdata, handles)
if isequal(get(hObject,'BackgroundColor'), get(0,'defaultUicontrolBackgroundColor'))
set(hObject,'BackgroundColor',[.9 .9 .9]);
end
六、进度条(axes3模拟,通过mywaitbar函数调用):
x取值为[0,1],显示进度,varargin为列矩阵,存放标题名和进度数。
function mywaitbar(x,handles,varargin)
if nargin < 1
error('Input arguments not valid');
end
set(0,'CurrentFigure',gcf);
fAxes = handles.axes3;
set(gcf,'CurrentAxes',fAxes);
if nargin > 1
hTitle = handles.text3;
set(hTitle,'String',varargin{1});
end
fractioninput = x;
x = max(0,min(100*x,100));
if fractioninput == 0
cla
pause(0.5) % 暂停小会清除上次使用进度
xpatch = [0 x x 0];
ypatch = [0 0 1 1];
xline = [100 0 0 100 100];
yline = [0 0 1 1 0];
patch(xpatch,ypatch,'b','EdgeColor','b','EraseMode','none');
set(gcf,'UserData',fractioninput);
l = line(xline,yline,'EraseMode','none');
set(l,'Color',get(gca,'XColor'));
else
p = findobj(gcf,'Type','patch');
l = findobj(gcf,'Type','line');
if (get(gcf,'UserData') > fractioninput)
set(p,'EraseMode','normal');
end
xpatch = [0 x x 0];
set(p,'XData',xpatch);
xline = get(l,'XData');
set(l,'XData',xline);
end
drawnow;
七、计算综合权值按钮(pushbutton5):
点击时首先弹出提示来说明(errordlg),在外侧套用uiwait,需要关闭才会继续执行。如果修改的权值不为数字的话会变为nan【在uitable里设置第五列为数字的效果】,判断是否有nan,如果没有就计算,然后通过errordlg弹出最终结果,否则弹出错误提示,不计算。
function pushbutton5_Callback(hObject, eventdata, handles)
uiwait(errordlg('由于部分算法不可归一化,综合分数计算将使用原始算法分数。','Notice'))
data=get(handles.uitable,'Data');
[m,~]=size(data);
for i=1:m
%if isnan(str2double(data{i,5}))%检查是否不为数字
if isnan(data{i,5}) %最后一列设为数值 若不为数值会变为nan 如果采用默认格式则按上式判断
text=strcat('第',num2str(i),'个参数输入不为数字,请重新输入。');
errordlg(text);
return
end
end
score=0;
for i=1:m
score=score+data{i,5}*str2double(data{i,2});
end
text=strcat('最后的加权评估分数为',num2str(score));
errordlg(text,'结果');