| Original Image (原画像) | Emphasized Image (錯視強調後) |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]()
Humans, in general, tend to underestimate things that change less or slow. In visual perception, humans underestimate sizes of objects and areas with small fluctuations, so that humans recognize sights with shrinking blank areas and enlarging areas that wear detailed textures. This paper produces a simulation of such subjective visual perception.
人間の感覚と認知は、物理刺激を線形的に知覚するのではなく、物理量を歪めて評価する、いわゆる錯覚が含まれている。空間の認知では、間隔の距離を過小評価する錯覚傾向が知られている。間隔長さの知覚は、知覚にせよ触覚にせよ、長い間隔ほど実際より短めに感じる傾向がある。
つまり、画像の変化が乏しい領域の幅は短く見える傾向がある。一方、変化の激しい領域は実際の大きさ以上に大きいと感じやすい。これを Oppel-Kundt 錯視という。
この特性を模擬するには、画像の変化量が激しい部分から近隣の画像領域に対して斥力を、逆に変化の乏しい部分からは引力を発して、画像を弾性的に変形すればよい。
実験では、画素ごとのラプラシアン(輝度値の空間2次微分)のノルムを画像の変化量として採用した。引力と斥力の大きさは、画素間の距離の自乗に反比例し、画像の変化量に比例する値として計算した。比例定数は画像ごとに適宜調節した。
この変形の結果、画像全体でのラプラシアンの分布は平均化される。
人間の空間知覚と認知は、観測器としてはかなり不正確であるから、見間違えや見落としなどのヒューマンエラーの原因になる。こうしたミスを防ぐためにも、画面や操作盤のヒューマンインタフェースは工夫してデザインする必要がある。
そこで、あるデザインが人間にはどう見えているかというシミュレーションができれば、見やすいデザインの製作の役に立つであろう。単にシミュレーションをするだけでなく、錯覚を大きく強調することで、デザインの弱点もより明らかに発見できるだろう。
| Original Image (原画像) | Emphasized Image (錯視強調後) |
|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
|
![]() |
![]() |
function [] = henkei(ImageFile,ForceMag,CalculationWidth)
% example: henkei('aaa.jpg', 0.3, 20)
% (C) Toru Nakata 2010
%%%% Load image
OriginalPicture = imread(ImageFile);
CCMotoRed = OriginalPicture(:,:,1);
CCMotoGreen = OriginalPicture(:,:,2);
CCMotoBlue = OriginalPicture(:,:,3);
[SizeX SizeY] = size(CCMotoRed);
MotoRed = zeros(SizeX, SizeY);
MotoGreen = zeros(SizeX, SizeY);
MotoBlue = zeros(SizeX, SizeY);
for iii = 1:SizeX
for jjj = 1:SizeY
MotoRed(iii,jjj) = CCMotoRed(iii,jjj);
end;
end;
for iii = 1:SizeX
for jjj = 1:SizeY
MotoGreen(iii,jjj) = CCMotoGreen(iii,jjj);
end;
end;
for iii = 1:SizeX
for jjj = 1:SizeY
MotoBlue(iii,jjj) = CCMotoBlue(iii,jjj);
end;
end;
%%%%%%%%%%%%%%% calcurate Laplacian %%%%%%%%%%%%%%%%%%%%%
LapRed = del2(MotoRed);
LapGreen = del2(MotoGreen);
LapBlue = del2(MotoBlue);
LapRed = abs(LapRed);
LapGreen = abs(LapGreen);
LapBlue = abs(LapBlue);
TotalLapMat = LapRed + LapGreen + LapBlue;
%%%%%%%%% Low Pass Filter for Laplacian
LPFMat = TotalLapMat;
for iii = 2:SizeX-1
for jjj = 2:SizeY-1
LPFMat(iii,jjj) = 0.125 * (TotalLapMat(iii,jjj-1) ...
+ TotalLapMat(iii-1,jjj) ...
+ 4 * TotalLapMat(iii,jjj) ...
+ TotalLapMat(iii+1,jjj) ...
+ TotalLapMat(iii,jjj+1));
end;
end;
TotalLapMat = LPFMat;
%%%%%%%%% Low Pass Filter for Laplacian
LPFMat = TotalLapMat;
for iii = 2:SizeX-1
for jjj = 2:SizeY-1
LPFMat(iii,jjj) = 0.125 * (TotalLapMat(iii,jjj-1) ...
+ TotalLapMat(iii-1,jjj) ...
+ 4 * TotalLapMat(iii,jjj) ...
+ TotalLapMat(iii+1,jjj) ...
+ TotalLapMat(iii,jjj+1));
end;
end;
TotalLapMat = LPFMat;
%%%%%%%%% Low Pass Filter for Laplacian
LPFMat = TotalLapMat;
for iii = 2:SizeX-1
for jjj = 2:SizeY-1
LPFMat(iii,jjj) = 0.125 * (TotalLapMat(iii,jjj-1) ...
+ TotalLapMat(iii-1,jjj) ...
+ 4 * TotalLapMat(iii,jjj) ...
+ TotalLapMat(iii+1,jjj) ...
+ TotalLapMat(iii,jjj+1));
end;
end;
TotalLapMat = LPFMat;
%%%%% modfy Laplacian to Log %%%%%%%%%%%%%%%%%%%%%%%
LogLaplacianMat = TotalLapMat + ones(SizeX,SizeY);
LogLaplacianMat = log(LogLaplacianMat);
OneMat = ones(SizeX,SizeY);
AverageLogLap = mean(mean(LogLaplacianMat));
LogLaplacianMat = LogLaplacianMat - AverageLogLap * OneMat;
%%%%% calc Forces %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ForceX = zeros(SizeX,SizeY);
ForceY = zeros(SizeX,SizeY);
Kirisute = mean(std(LogLaplacianMat));
%ForceOffset = 0;
ForceOffset = ForceOffset * Kirisute;
Kirisute = Kirisute * 0.3;
for iii = 1:SizeX
for jjj = 1:SizeY
MotoForce = LogLaplacianMat(iii,jjj) + ForceOffset;
if abs(MotoForce) > Kirisute
startX = iii - CalculationWidth;
if startX <= 0
startX = 1;
end;
endX = iii + CalculationWidth;
if endX > SizeX
endX = SizeX;
end;
for kkk = startX:endX
startY = jjj - CalculationWidth;
if startY <= 0
startY = 1;
end;
endY = jjj + CalculationWidth;
if endY > SizeY
endY = SizeY;
end;
for lll = startY:endY
difX = kkk - iii;
difY = lll - jjj;
if (difX == 0) & (difY == 0)
facfac = 0;
else
% difnorm = sqrt(difX * difX + difY * difY);
% facfac = MotoForce / (difnorm * difnorm * difnorm);
facfac = MotoForce / (difX * difX + difY * difY);% 1-order
end;
ForceX(kkk,lll) = ForceX(kkk,lll) + facfac * difX;
ForceY(kkk,lll) = ForceY(kkk,lll) + facfac * difY;
end;
end;
end;
end;
if mod(iii, 20) == 0
ProccessingSoFar = iii/SizeX
end
end;
%%%%% Move Pixels %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
ForceX = round(ForceMag * ForceX);
ForceY = round(ForceMag * ForceY);
GridX = zeros(SizeX,SizeY);
GridY = zeros(SizeX,SizeY);
for iii = 1:SizeX
for jjj = 1:SizeY
GridX(iii,jjj) = iii;
GridY(iii,jjj) = jjj;
end;
end;
GridX = GridX + ForceX;%Sekiryoku
GridY = GridY + ForceY;%Sekiryoku
SekiryokuMap = zeros(SizeX,SizeY,3);
SekiryokuPointConde = zeros(SizeX,SizeY);
for iii = 1:SizeX
for jjj = 1:SizeY
nowX = round(GridX(iii,jjj));
nowY = round(GridY(iii,jjj));
if (nowX > 0) & (nowX <= SizeX) & (nowY > 0) & (nowY <= SizeY)
SekiryokuMap(nowX,nowY,1) = SekiryokuMap(nowX,nowY,1) + MotoRed(iii,jjj);
SekiryokuMap(nowX,nowY,2) = SekiryokuMap(nowX,nowY,2) + MotoGreen(iii,jjj);
SekiryokuMap(nowX,nowY,3) = SekiryokuMap(nowX,nowY,3) + MotoBlue(iii,jjj);
SekiryokuPointConde(nowX,nowY) = SekiryokuPointConde(nowX,nowY) + 1;
end;
end;
end;
tempval = 0;
for iii = 1:SizeX
for jjj = 1:SizeY
if SekiryokuPointConde(iii,jjj) > 1
tempval = SekiryokuPointConde(iii,jjj);
SekiryokuMap(iii,jjj,1) = SekiryokuMap(iii,jjj,1) / tempval;
SekiryokuMap(iii,jjj,2) = SekiryokuMap(iii,jjj,2) / tempval;
SekiryokuMap(iii,jjj,3) = SekiryokuMap(iii,jjj,3) / tempval;
SekiryokuPointConde(iii,jjj) = 1;
end;
end;
end;
%%%%%%%%%%%%%%%%%%% Fill blank pixels %%%%%%%%%%%%%%%%%%%%%%
tempval = 0;
for kkk = 1:8
for iii = 2:SizeX-1
for jjj = 2:SizeY-1
if SekiryokuPointConde(iii,jjj) == 0
memmem = ...
SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuPointConde(iii+1,jjj+1);
if memmem > 9-kkk
tempval = ...
SekiryokuMap(iii-1,jjj-1,1) * SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuMap(iii-1,jjj ,1) * SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuMap(iii-1,jjj+1,1) * SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuMap(iii ,jjj-1,1) * SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuMap(iii ,jjj+1,1) * SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuMap(iii+1,jjj-1,1) * SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuMap(iii+1,jjj ,1) * SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuMap(iii+1,jjj+1,1) * SekiryokuPointConde(iii+1,jjj+1);
SekiryokuMap(iii,jjj,1) = tempval / memmem;
tempval = ...
SekiryokuMap(iii-1,jjj-1,2) * SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuMap(iii-1,jjj ,2) * SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuMap(iii-1,jjj+1,2) * SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuMap(iii ,jjj-1,2) * SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuMap(iii ,jjj+1,2) * SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuMap(iii+1,jjj-1,2) * SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuMap(iii+1,jjj ,2) * SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuMap(iii+1,jjj+1,2) * SekiryokuPointConde(iii+1,jjj+1);
SekiryokuMap(iii,jjj,2) = tempval / memmem;
tempval = ...
SekiryokuMap(iii-1,jjj-1,3) * SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuMap(iii-1,jjj ,3) * SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuMap(iii-1,jjj+1,3) * SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuMap(iii ,jjj-1,3) * SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuMap(iii ,jjj+1,3) * SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuMap(iii+1,jjj-1,3) * SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuMap(iii+1,jjj ,3) * SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuMap(iii+1,jjj+1,3) * SekiryokuPointConde(iii+1,jjj+1);
SekiryokuMap(iii,jjj,3) = tempval / memmem;
SekiryokuPointConde(iii,jjj) = 1;
end;
end;
end;
end;
end;
%%%%% finishing filling %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for kkk = 1:8
for iii = 2:SizeX-1
for jjj = 2:SizeY-1
if SekiryokuPointConde(iii,jjj) == 0
memmem = ...
SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuPointConde(iii+1,jjj+1);
if memmem > 0
tempval = ...
SekiryokuMap(iii-1,jjj-1,1) * SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuMap(iii-1,jjj ,1) * SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuMap(iii-1,jjj+1,1) * SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuMap(iii ,jjj-1,1) * SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuMap(iii ,jjj+1,1) * SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuMap(iii+1,jjj-1,1) * SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuMap(iii+1,jjj ,1) * SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuMap(iii+1,jjj+1,1) * SekiryokuPointConde(iii+1,jjj+1);
SekiryokuMap(iii,jjj,1) = tempval / memmem;
tempval = ...
SekiryokuMap(iii-1,jjj-1,2) * SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuMap(iii-1,jjj ,2) * SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuMap(iii-1,jjj+1,2) * SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuMap(iii ,jjj-1,2) * SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuMap(iii ,jjj+1,2) * SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuMap(iii+1,jjj-1,2) * SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuMap(iii+1,jjj ,2) * SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuMap(iii+1,jjj+1,2) * SekiryokuPointConde(iii+1,jjj+1);
SekiryokuMap(iii,jjj,2) = tempval / memmem;
tempval = ...
SekiryokuMap(iii-1,jjj-1,3) * SekiryokuPointConde(iii-1,jjj-1) + ...
SekiryokuMap(iii-1,jjj ,3) * SekiryokuPointConde(iii-1,jjj ) + ...
SekiryokuMap(iii-1,jjj+1,3) * SekiryokuPointConde(iii-1,jjj+1) + ...
SekiryokuMap(iii ,jjj-1,3) * SekiryokuPointConde(iii ,jjj-1) + ...
SekiryokuMap(iii ,jjj+1,3) * SekiryokuPointConde(iii ,jjj+1) + ...
SekiryokuMap(iii+1,jjj-1,3) * SekiryokuPointConde(iii+1,jjj-1) + ...
SekiryokuMap(iii+1,jjj ,3) * SekiryokuPointConde(iii+1,jjj ) + ...
SekiryokuMap(iii+1,jjj+1,3) * SekiryokuPointConde(iii+1,jjj+1);
SekiryokuMap(iii,jjj,3) = tempval / memmem;
SekiryokuPointConde(iii,jjj) = 1;
end;
end;
end;
end;
end;
%%%%% Processing for Picture Edges %%%%%%%%%%%%%%%%%%%%%%%%%%%
for iii = 1:SizeX
if SekiryokuPointConde(iii,1) == 0;
SekiryokuMap(iii,1,1) = SekiryokuMap(iii,2,1);
SekiryokuMap(iii,1,2) = SekiryokuMap(iii,2,2);
SekiryokuMap(iii,1,3) = SekiryokuMap(iii,2,3);
end;
end;
for iii = 1:SizeX
if SekiryokuPointConde(iii,SizeY) == 0;
SekiryokuMap(iii,SizeY,1) = SekiryokuMap(iii,SizeY-1,1);
SekiryokuMap(iii,SizeY,2) = SekiryokuMap(iii,SizeY-1,2);
SekiryokuMap(iii,SizeY,3) = SekiryokuMap(iii,SizeY-1,3);
end;
end;
for jjj = 1:SizeY
if SekiryokuPointConde(1,jjj) == 0;
SekiryokuMap(1,jjj,1) = SekiryokuMap(2,jjj,1);
SekiryokuMap(1,jjj,2) = SekiryokuMap(2,jjj,2);
SekiryokuMap(1,jjj,3) = SekiryokuMap(2,jjj,3);
end;
end;
for jjj = 1:SizeY
if SekiryokuPointConde(SizeX,jjj) == 0;
SekiryokuMap(SizeX,jjj,1) = SekiryokuMap(SizeX-1,jjj,1);
SekiryokuMap(SizeX,jjj,2) = SekiryokuMap(SizeX-1,jjj,2);
SekiryokuMap(SizeX,jjj,3) = SekiryokuMap(SizeX-1,jjj,3);
end;
end;
%%%%%%%%%%%%% format for picture %%%%%%%%%%%%%%%%%%%%%%%%%%%%%
SekiResultPic = zeros(SizeX,SizeY,3,'uint8');
for iii = 1:SizeX
for jjj = 1:SizeY
SekiResultPic(iii,jjj,1) = round(SekiryokuMap(iii,jjj,1));
SekiResultPic(iii,jjj,2) = round(SekiryokuMap(iii,jjj,2));
SekiResultPic(iii,jjj,3) = round(SekiryokuMap(iii,jjj,3));
end;
end;
%%% save %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
imwrite(SekiResultPic,'SekiResultPic.png','PNG');
%%%%%%% show %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
figure(1)
subplot(2,1,1)
imshow(OriginalPicture);
subplot(2,1,2)
imshow(SekiResultPic);
%%%% end