gzh上这篇文章正在抽奖赠书:截止日期2023年1月9日12:00(周一)
MATLAB | 文末赠书 | 如何从热图中提取数据
赠送3本由北京大小出版社提供的《SPSS统计分析大全》
这期做了个可能有用的小工具,一般论文中热图很少给出数据,于是就想写个小工具通过热图上的颜色估计出数据值来,目前写了个初版的工具分享给大家!
工具函数
由于只是初版,要手动改的地方还是不少的,要设置好要提取数据的图片地址,结果存储地址,数据热图行数、列数以及colorbar的范围。
function getHeatmapData
% 要提取的图像地址
picPath='1.png';
% 提取结果存储路径
HMPath='data1.mat';
% 热图行数、列数
heatMapSize=[8,8];
% colorbar所表示范围
climColorbar=[-1,1];
% 提取colorbar上颜色点数量
pntNumColorBar=20;
% 数据计算所用点数(数值越大越精确)
pntNumInterp=1000;
% figure窗口初始化
pic=imread(picPath);
figure()
ax=gca;hold on
ax.XLim=[0,size(pic,2)];
ax.YLim=[0,size(pic,1)];
imshow(pic)
% 绘制红蓝色点的句柄
baHdl=plot(ax,0,0,'b+','MarkerSize',12,'LineWidth',1.5);
rxHdl=plot(ax,0,0,'rx','MarkerSize',12,'LineWidth',1.5);
pntSet=zeros(0,2);
colorSet=zeros(4,3);
set(gcf,'WindowButtonDownFcn',@buttondown)
function buttondown(~,~)
xy=get(gca,'CurrentPoint');
xp=xy(1,2);yp=xy(1,1);pos=[yp,xp];
if strcmp(get(gcf,'SelectionType'),'normal')
pntSet=[pntSet;pos];
elseif size(pntSet,1)>0
pntSet(end,:)=[];
end
pntSet=round(pntSet);
pntSet(pntSet<1)=1;
if size(pntSet,1)>4
set(gcf,'WindowButtonDownFcn',[])
saveColor();
close all
else
% 标记采样点位置和数值
baHdl.XData=pntSet(:,1);baHdl.YData=pntSet(:,2);
rxHdl.XData=pntSet(:,1);rxHdl.YData=pntSet(:,2);
delete(findobj('type','text'))
for i=1:size(pntSet,1)
colorSet(i,:)=[pic(pntSet(i,2),pntSet(i,1),1),...
pic(pntSet(i,2),pntSet(i,1),2),...
pic(pntSet(i,2),pntSet(i,1),3)];
text(ax,pntSet(i,1),pntSet(i,2),[' ',num2str(i),':[',num2str(colorSet(i,1)),',',...
num2str(colorSet(i,2)),',',num2str(colorSet(i,3)),']'])
end
end
end
function saveColor(~,~)
% 获取colorbar上颜色
cbPntSet=pntSet(1:2,:);
cbPntSetdiff=abs(cbPntSet(1,:)-cbPntSet(2,:));
if cbPntSetdiff(1)>cbPntSetdiff(2)
tPntSet=cbPntSet(1,1)+linspace(0,1,pntNumColorBar)'.*(cbPntSet(2,1)-cbPntSet(1,1));
tPntSet=round(tPntSet);
CMap=[pic(round((cbPntSet(1,2)+cbPntSet(2,2))/2),tPntSet,1)',...
pic(round((cbPntSet(1,2)+cbPntSet(2,2))/2),tPntSet,2)',...
pic(round((cbPntSet(1,2)+cbPntSet(2,2))/2),tPntSet,3)'];
else
tPntSet=cbPntSet(1,2)+linspace(0,1,pntNumColorBar)'.*(cbPntSet(2,2)-cbPntSet(1,2));
tPntSet=round(tPntSet);
CMap=[pic(tPntSet,round((cbPntSet(1,1)+cbPntSet(2,1))/2),1),...
pic(tPntSet,round((cbPntSet(1,1)+cbPntSet(2,1))/2),2),...
pic(tPntSet,round((cbPntSet(1,1)+cbPntSet(2,1))/2),3)];
end
CMap=double(CMap);
% -----------------------------------------------------------------
% 提取heatmap上颜色
hmPntSet=pntSet(3:4,:);
hmXLim=[min(hmPntSet(:,1)),max(hmPntSet(:,1))];
hmYLim=[min(hmPntSet(:,2)),max(hmPntSet(:,2))];
YList=linspace(hmYLim(1),hmYLim(2),heatMapSize(1));
XList=linspace(hmXLim(1),hmXLim(2),heatMapSize(2));
[XMesh,YMesh]=meshgrid(round(XList),round(YList));
CMesh=zeros(heatMapSize);
for i=1:heatMapSize(1)
for j=1:heatMapSize(2)
CMesh(i,j,1)=pic(YMesh(i,j),XMesh(i,j),1);
CMesh(i,j,2)=pic(YMesh(i,j),XMesh(i,j),2);
CMesh(i,j,3)=pic(YMesh(i,j),XMesh(i,j),3);
end
end
% 计算heatmap数值范围
CMesh=double(CMesh);
t1=linspace(climColorbar(1),climColorbar(2),pntNumColorBar)';
t2=linspace(climColorbar(1),climColorbar(2),pntNumInterp)';
CMapInterp=[interp1(t1,CMap(:,1),t2,'linear'),interp1(t1,CMap(:,2),t2,'linear'),interp1(t1,CMap(:,3),t2,'linear')];
Data=zeros(heatMapSize);
for i=1:heatMapSize(1)
for j=1:heatMapSize(2)
tRGB=[CMesh(i,j,1),CMesh(i,j,2),CMesh(i,j,3)];
tnorm2=sum((CMapInterp-tRGB).^2,2);
[ind,~]=find(tnorm2==min(tnorm2),1);
Data(i,j)=t2(ind);
end
end
% 存储数值
% timestr=char(datetime('now'));
% timestr(timestr==' ')='_';
% timestr(timestr==':')='_';
% nowStr=[timestr,'.mat'];
CMapInterp=CMapInterp./255;
CMap=CMap./255;
save(HMPath,'Data','CMap','CMapInterp','climColorbar')
end
end
基本使用
假设有这么一张图(压缩包内提供的示例图7)
要提取的数据8行8列,colorbar范围-1到1,因此将工具函数最前面的代码做出如此改动:
% 要提取的图像地址
picPath='7.png';
% 提取结果存储路径
HMPath='data7.mat';
% 热图行数、列数
heatMapSize=[8,8];
% colorbar所表示范围
climColorbar=[-1,1];
% 提取colorbar上颜色点数量
pntNumColorBar=20;
% 数据计算所用点数(数值越大越精确)
pntNumInterp=1000;
依次点击colorbar最小值处,最大值处,以及热图的对角线两个点处,之后随便点击图片任意处(共点击五次),就能将数据存储在mat文件中:
大概这样取四个点,并随便点击图像某处(若是点错了可鼠标右键撤回):
之后查看mat文件:
其中:
- Data 提取的热图数据
- CMap colormap
- CMapInterp 插值后colormap
- climColor colorbar范围
获取了这些数据后,可以很轻松的靠以下代码复刻出来图像:
HM=load('data7.mat');
HMHdl=heatmap(HM.Data);
colormap(HM.CMapInterp)
% 新版本可以将caxis换为clim
caxis(HM.climColorbar)
% 不显示数值
HMHdl.CellLabelColor='none';
看起来提取的很完美,虽然肯定有误差,但是对于一般的论文复现已经足够了:
行列数不相同
几乎完全相同的处理手段,比如此图是15行20列,colorbar范围是-0.6到0.8,将最前面的代码修改为:
% 要提取的图像地址
picPath='6.png';
% 提取结果存储路径
HMPath='data6.mat';
% 热图行数、列数
heatMapSize=[15,20];
% colorbar所表示范围
climColorbar=[-.6,.8];
% 提取colorbar上颜色点数量
pntNumColorBar=20;
% 数据计算所用点数(数值越大越精确)
pntNumInterp=1000;
运行后进行取点:
之后还是运行绘图代码:
HM=load('data6.mat');
HMHdl=heatmap(HM.Data);
colormap(HM.CMapInterp)
% 新版本可以将caxis换为clim
caxis(HM.climColorbar)
% 不显示数值
HMHdl.CellLabelColor='none';
数据提取结果:
非方形色块热图
依旧依据热图性质修改一下参数:
% 要提取的图像地址
picPath='4.png';
% 提取结果存储路径
HMPath='data4.mat';
% 热图行数、列数
heatMapSize=[11,11];
% colorbar所表示范围
climColorbar=[-1,1];
% 提取colorbar上颜色点数量
pntNumColorBar=20;
% 数据计算所用点数(数值越大越精确)
pntNumInterp=1000;
这种圆点形状的尽量要取点时取在正中央
:
数据提取结果:
色块上有文本
依旧依据热图性质修改一下参数:
% 要提取的图像地址
picPath='8.png';
% 提取结果存储路径
HMPath='data8.mat';
% 热图行数、列数
heatMapSize=[8,8];
% colorbar所表示范围
climColorbar=[-1,1];
% 提取colorbar上颜色点数量
pntNumColorBar=20;
% 数据计算所用点数(数值越大越精确)
pntNumInterp=1000;
有文本的热图,在热图色块上点击时,尽量俩点点击在同一位置且不要碰到文字,比如俩色块都点击同一边角(下图所示即为点击两个色块左下角):
提取效果:
完
以上已给出完整代码,想要更多例子练练手:
【热图数据提取】
更新时会跟进更新以下连接:
【链接】:
https://pan.baidu.com/s/1zoStpQ54LqmR7fSXMWWyOA?pwd=slan
【提取码】:slan