function mtfcurve(varargin) % version 04/30/2002 % Visualize the effects of MTF (modulation transfer function) % in film, lenses, scanners, and sharpening using simulation. Format: % mtfcurve f50 % f50 is the 50% MTF density of film in lines/mm, typically 20 - 60. % mtfcurve f50 fboost % fboost is the frequency shift of the 2nd order equalizer. % mtfcurve f50 fboost flens % flens is the 50% MTF density of the lens. % mtfcurve f50 fboost flens lord % lord is lens rolloff order (default = 2). % mtfcurve f50 fboost flens lord dscan % dscan is scanner pixels per mm (dpmm). % mtfcurve f50 fboost flens lord dscan ksharp % ksharp is sharpening boost. % % mtfcurve 45 13 61 2 % test example % % Parameters default to 0 if not entered, except lord=2. % If f50<0, Create printer test targets. |f50| is the 50% film density. % If fboost>0, a boost is applied by shifting the peak frequency of the rolloff. % normal: 1/(1+(f/f50)^2) % with fboost: k1/(1+((f-fboost)/f50a)^2) , where k1 normalizes response to 1 % at f=0 and f50a is set so f50 remains the 50% MTF density. fboost < f50/2. % If flens>0, a simplified lens MTF is added to the transfer, using 1/(1+(f/flens)^2) % If abs(dscan)>0, a scanner with dscan dpmm (pixels/mm) is added. % If dscan > 0, scanner MTF follows |sinc|^3 (appropriate for a film scanner). % If dscan < 0, scanner MTF follows |sinc|^2 (appropriate for digital camera); % If ksharp>0, sharpening equivalent to a cosine boost is applied, where ksharp is % the amount of boost. % Equation: (1+ksharp*cos(2*pi*f/dscan)) %%%%%%%%%%%%%% % % Copyright (C) 2001 by Norman Koren, http://www.normankoren.com normkoren@earthlink.net % Every conceivable disclaimer applies; the author assumes no legal liability whatsoever. % You may distribute this program freely, but please maintain the original copyright. fboost = 0; flens = 0; lord = 2; dscan=0; ksharp=0; % Defaults. if (nargin==0 | nargin>6) error(['Entered ' num2str(nargin) ' arguments. Must enter 1 - 6.']); end if (nargin>=1) f50 = abs(str2num(varargin{1})); % Density (line pairs per mm) of 50% MTF. descript = ['f50 = ' num2str(f50,4) '/mm']; end prtarg = ((str2num(varargin{1}))<0); % Create printer target if first argument <0. if (nargin>=2) % Boost frequency from cosine eql or peak center shift. fboost = str2num(varargin{2}); % Boost by shifting peak if (fboost>eps) descript = [descript '; fboost = ' num2str(fboost,4) '/mm']; end, end if (nargin>=3) % Lens f50. flens = str2num(varargin{3}); % Lens 50% density. descript = [descript '; flens = ' num2str(flens,4) '/mm']; end if (nargin>=4) % Lens rolloff order (default 2). lord = str2num(varargin{4}); % Lens 50% density. if (lord.45*f50) % Boost by shifting peak. error(['fboost must be < .45*f50 = ' num2str(.45*f50,4)]); end close all xmax = 0.5; % Maximum of x1 in mm. The physical length of the image. ymax = 200; % Maximum lp/mm (spatial frequency) the plots. ncyc = log10(100); % Cycles of frequency to display. Argument is frequency ratio. ymin = ymax/10^ncyc; ymid = ymax/10^(ncyc/2); % Minimum, middle spatial frequencies. lfft = 2048; % Total array length; ldisp = 1920; % Length to display; lgr = lfft-ldisp; % Length of gray needed to avoid cyclic problems. ld1 = ldisp-1; lf2 = lfft/2; x1 = ([0:ld1]/ld1)*xmax; % Distance in mm along axis to display. xinc = x1(2)-x1(1); % x1 (spatial) increment in mm. fdel = 1/(lfft*xinc); % Spatial frequency increment in 1/mm. fq1 = 10.^(ncyc*[0:ld1]/ld1); fq1 = ymax*fq1/max(fq1); % Spatial frequency 1/mm. figure; semilogy([0:ld1]*xinc,fq1); grid on; zoom on; ax = axis; ax(2) = ld1*xinc; ax(4) = ymax; axis(ax); title('Spatial frequency as a function of distance'); xlabel('Distance in mm.'); ylabel('Spatial frequency in lp/mm (1/mm)'); p1 = get(gcf,'Position'); p1(4) = .8*p1(4); p1(3) = .8*p1(3); set(gcf,'Position',p1); u1 = 2.*pi*cumsum(fq1)*xinc; % Equation for varying frequency is tricky! ybw = .5*(1+cos(u1)); % Original cosine pattern before rounding. temp = find(round(ybw(1:ldisp-1))~=round(ybw(2:ldisp))); % Anti-aliasing calculation. temp1 = (.5-ybw(temp))./(ybw(temp+1)-ybw(temp)); % Fraction for crossing. ybw = round(ybw); % {0,1} basic black/white pattern to analyze. Comment out for testing. ybw(temp+round(temp1)) = (1-abs(temp1-.5)).*ybw(temp+ round(temp1)) + ... (abs(temp1-.5)) .*ybw(temp+1-round(temp1)); clear temp,temp1; % figure; plot(x1,ybw); zoom on; % For test. half = .5*ones(1,lgr); ybw = [ybw half]; % Last lgr points have a value of 1/2. if (prtarg) % COUNTERPARTS FOR PRINTER xpmax = 10; % Max x1 mm for printer test image. lpfft = 16384; lpdsp = 15360; lpgr = lpfft-lpdsp; % Counterparts of lfft... for printer. lpd1 = lpdsp-1; lpf2 = lpfft/2; xp1 = ([0:lpd1]/lpd1)*xpmax; % Distance in mm along axis to display. xpinc = xpmax/lpd1; % Spatial increment in mm. fpel = 1/(lpfft*xpinc); fpq1 = 10.^(ncyc*[0:lpd1]/lpd1); fpq1 = ymax*fpq1/max(fpq1); % Spatial frequency 1/mm. u1 = 2.*pi*cumsum(fpq1)*xpinc; % Equation for varying frequency is tricky! ypw = .5*(1+cos(u1)); % Original cosine pattern before rounding. temp = find(round(ypw(1:lpdsp-1))~=round(ypw(2:lpdsp))); % Anti-aliasing calculation. temp1 = (.5-ypw(temp))./(ypw(temp+1)-ypw(temp)); % Fraction for crossing. ypw = round(ypw); % {0,1} basic black/white pattern to analyze. Comment out for testing. ypw(temp+round(temp1)) = (1-abs(temp1-.5)).*ypw(temp+ round(temp1)) + ... (abs(temp1-.5)) .*ypw(temp+1-round(temp1)); clear temp, temp1; % figure; plot(1:lpdsp,ypw,'bx-'); zoom on; %%%%%%% % ax = axis; ax(3) = -1; ax(4) = 2; axis(ax); %%%%%%% phalf = .5*ones(1,lpgr); ypw = [ypw phalf]; % Last lgr points have a value of 1/2. end % END PRINTER FOR NOW. cmap = zeros(256,3); % Color map: gray scale. Apparently gamma corrected. gamma = 1; cmap(1:256,1) = ([0:255]'/255).^(1/gamma); cmap(1:256,2) = cmap(1:256,1); cmap(1:256,3) = cmap(1:256,1); y4 = [0:.01:1]; y5 = y4'*ybw(1:ldisp) + (1-y4)'*.5*ones(size(ybw(1:ldisp))); % y4 is gradient. figure; image(255*y5); colormap(cmap); title('Original bands'); xlabel([num2str(ymin,4) ' line pairs per mm at left; ' num2str(ymid,4) ' in middle; ' ... num2str(ymax,4) ' at right']); ylabel('Maximum contrast at bottom [0,1]; none at top [all 0.5]'); lvon = 40; lv2 = ceil(.4*lvon); vones = ones([lvon 1]); % Start sequence display. yp2 = real(ybw(1:ldisp)); yp3 = vones*yp2; % Original bands. yp3 is a 2D matrix. lyp = length(yp2); yp4 = yp2(1:5:lyp); yx1 = round(.76*lyp); % Insert reduced box. yp3(6:25,yx1+1:yx1+length(yp4)+10) = vones(1:20)*[zeros(1,5) yp4 zeros(1,5)]; yp3(5:20:26,yx1+1:yx1+length(yp4)+10) = vones(1:2)*[zeros(1,length(yp4)+10)]; yp = yp3; f1 = fft(ybw); fq2 = [0:lf2, -lf2+1:-1]; % figure; plot(fq2,real(f1),fq2,imag(f1)); grid on; zoom on; tfrfn = 1./(1+(fq2*fdel/f50).^2); % MTF (without cos boost). if (prtarg) fp1 = fft(ypw); fp2 = [0:lpf2,-lpf2+1:-1]; % For printer. pfrfn = 1./(1+(fp2*fpel/f50).^2); end % MTF (without cos boost). % Add cosine boost, but not at high frequencies. Use tricks. if (fboost>eps) % MTF with shifted zero point. tfrfn = 1./(1+((abs(fq2*fdel)-fboost)/sqrt(f50^2-2*f50*fboost-fboost^2)).^2); tfrfn = tfrfn/tfrfn(1); % normalize tfrfn to lowest frequency. if (prtarg) pfrfn = 1./(1+((abs(fp2*fpel)-fboost)/sqrt(f50^2-2*f50*fboost-fboost^2)).^2); % Printer pfrfn = pfrfn/pfrfn(1); end % normalize tfrfn to lowest frequency. end if (flens>eps) tlens = 1./(1+abs(fq2*fdel/flens).^lord); % Lens MTF. Order (lord) defaults to 2. if (prtarg) plens = 1./(1+abs(fp2*fpel/flens).^lord); end % Lens MTF for printer. f2 = ifft(tlens.*f1); % Lens only. yp2 = real(f2(1:ldisp)); yp3 = vones*yp2; % Lens only. yp3 is a 2D matrix. lyp = length(yp2); yp4 = yp2(1:5:lyp); yx1 = round(.76*lyp); % Insert reduced box. yp3(6:25,yx1+1:yx1+length(yp4)+10) = vones(1:20)*[zeros(1,5) yp4 zeros(1,5)]; yp3(5:20:26,yx1+1:yx1+length(yp4)+10) = vones(1:2)*[zeros(1,length(yp4)+10)]; yp = [yp; yp3]; f2 = ifft(tfrfn.*f1); yp2 = real(f2(1:ldisp)); yp3 = vones*yp2; % Lens only. yp3 is a 2D matrix. lyp = length(yp2); yp4 = yp2(1:5:lyp); yx1 = round(.76*lyp); % Insert reduced box. yp3(6:25,yx1+1:yx1+length(yp4)+10) = vones(1:20)*[zeros(1,5) yp4 zeros(1,5)]; yp3(5:20:26,yx1+1:yx1+length(yp4)+10) = vones(1:2)*[zeros(1,length(yp4)+10)]; yp = [yp; yp3]; tfrfn = tfrfn.*tlens; if (prtarg)pfrfn = pfrfn.*plens; end end % Multiply tfrfn by lens MTF. [t50,i50] = min((tfrfn(1:lf2)-.5).^2); fq50 = fq2(i50)*fdel; % Find 50% point. if (tfrfn(i50)>.5) i51 = i50+1; else i51 = i50-1; end f51 = abs((tfrfn(i50)-.5)/(tfrfn(i50)-tfrfn(i51))); % Fraction i51 for interpolation. fq50 = fdel*((1-f51)*fq2(i50)+f51*fq2(i51)); % 50% point by interpolation. [t10,i10] = min((tfrfn(1:lf2)-.1).^2); fq10 = fq2(i10)*fdel; % Find 10% point. if (tfrfn(i10)>.1) i11 = i10+1; else i11 = i10-1; end f11 = abs((tfrfn(i10)-.1)/(tfrfn(i10)-tfrfn(i11))); % Fraction i11 for interpolation. fq10 = fdel*((1-f11)*fq2(i10)+f11*fq2(i11)); % 10% point by interpolation. figure; loglog(fq2(2:lf2)*fdel,100*tfrfn(2:lf2)); grid on; zoom on; ax = axis; ax(1) = ymin; ax(2) = ymax; ax(3) = 1; if (max(tfrfn)<2 & ax(4)>200) ax(4) = 200; end; axis(ax); xlblm = ['Line pairs per mm; MTF = 50%,10% @ ' num2str(fq50,3) ', ' num2str(fq10,3) '/mm']; if (fboost>eps) xlblm = [xlblm '; Max MTF = ',num2str(max(tfrfn),3)]; end xlabel(xlblm); title(descript); f2 = ifft(tfrfn.*f1); yp2 = real(f2(1:ldisp)); yp3 = vones*yp2; % yp3 is a 2D matrix. Lens+film lyp = length(yp2); yp4 = yp2(1:5:lyp); yx1 = round(.76*lyp); % Insert reduced box. yp3(6:25,yx1+1:yx1+length(yp4)+10) = vones(1:20)*[zeros(1,5) yp4 zeros(1,5)]; yp3(5:20:26,yx1+1:yx1+length(yp4)+10) = vones(1:2)*[zeros(1,length(yp4)+10)]; yp = [yp; yp3]; figure; plot([0:ld1]*xinc,real(f2(1:ldisp)),[0:ld1]*xinc,imag(f2(1:ldisp))); title(descript); ylabel('Band amplitude'); xlabel(['Distance in mm; ' num2str(ymin,4) ' line pairs per mm at left; ' num2str(ymid,4) ... ' in middle; ' num2str(ymax,4) ' at right']); ax = axis; ax(2) = ld1*xinc; axis(ax); grid on; zoom on; if (prtarg)fp2 = ifft(pfrfn.*fp1); %%%%%%%%%%%%%%% FOR PRINTER %%%%%%%%%%%% figure; plot([0:lpd1]*xpinc,real(fp2(1:lpdsp)),[0:lpd1]*xpinc,imag(fp2(1:lpdsp))); %%%%%% ax = axis; ax(2) = lpd1*xpinc; axis(ax); %%%%%%% title('For printer'); grid on; zoom on; %%%%%%% end y6 = real(f2); y7 = y4'*y6(1:ldisp) + (1-y4)'*.5*ones(size(y6(1:ldisp))); y7 = min(y7,1); y7 = max(y7,0); % Limit the array to a range of {0,1}. % 1/5 size box. sy7 = size(y7); y8 = y7(1:5:101, 1:5:sy7(2)); sy8 = size(y8); y9 = zeros([sy8(1)+2 sy8(2)+20]); y9(2:sy8(1)+1,11:sy8(2)+10) = y8; sy9 = size(y9); yy1 = 11; yx1 = round(.76*sy7(2)); y7(yy1+1:yy1+sy9(1),yx1+1:yx1+sy9(2)) = y9; % COMBINED PLOT figure; p1 = get(gcf,'Position'); p1(2) = p1(2)-60; p1(4) = p1(4)+60; p1(3) = p1(3)-80; set(gcf,'Position',p1); p2 = get(gcf,'PaperPosition'); p2(2) = p2(2)-1; p2(4) = p2(4)+2; set(gcf,'PaperPosition',p2); subplot('Position',[.10 .4 .88 .55]); % subplot(2,1,1); image(255*y7); colormap(cmap); % get(gcf) gets properties. title(descript); ylabel('Max contrast at bottom [0,1]; none at top [0.5] '); if (f50>10*ymax) htxt = text(ldisp/2,7,['Original image']); elseif (flenseps) loglog(fq2(2:lf2)*fdel,100*tlens(2:lf2),'b:'); end loglog(fq1,10.^(2*y6(1:ldisp)),'r-'); plot([2 200],[10 10],'r--'); plot([2 200],[50 50],'b:'); hold off; ax = axis; ax(1) = ymin; ax(2) = ymax; % ax(3) = 1; % old ax(3) = min(1, min(10.^(2*y6(1:ldisp)))); ax(4) = max(100,max(10.^(2*y6(1:ldisp)))); axis(ax); xlabel(xlblm); ylabel('MTF %, amplitude'); if (dscan>2) % Results for a scanner with dscan pixels/mm (dscan*25.4 pixels/inch) tfrtot = tfrfn; % Total transfer function with scanner. sscan = 1/(dscan*xinc); % Scan steps in array points (non-integer). sstep = [1:sscan:lfft]; % Scan boundaries for averaging. if (sstep(length(sstep))1 & ix1>1) % fscan MUST be antialiased to avoid the jaggies! fx0 = ix1-ix0; % Weighting fraction. fscan(i2,ix1-1) = fx0*fstep(i1)+(1-fx0)*fstep(i1-1); end end if (ksharp>eps) % Sharpen the image using a cosine boost. fshp = fstep; fshp(2:lfs-1) = (fstep(2:lfs-1)-ksharp*(fstep(1:lfs-2)+fstep(3:lfs)))/(1-2*ksharp); for (i1=1:lfs) ix0 = max(sstep(i1)-sshft,1); % Non-integer left boundary for anti-aliasing. ix1 = ceil(ix0); ix2 = max(floor(sstep(i1+1)-sshft),1); fsharp(i2,ix1:ix2) = fshp(i1); if (i1>1 & ix1>1) % fscan MUST be antialiased to avoid the jaggies! fx0 = ix1-ix0; % Weighting fraction. fsharp(i2,ix1-1) = fx0*fshp(i1)+(1-fx0)*fshp(i1-1); end end end % End cosine boost for sharpening end if (prtarg) % PRINTER TARGET pscan =1/(dscan*xpinc); % Scan steps in points in array fp2 (non-integer). pstep = [1:pscan:lpdsp]; % Scan boundaries in array fp2 for averaging. lps = length(pstep); % Number of points (more than target length, typ. 10mm) if (pstep(length(pstep))eps) % Sharpen here using a cosine boost. fpstep(2:lps-1) = (fpstep(2:lps-1)-ksharp*(fpstep(1:lps-2)+ ... fpstep(3:lps)))/(1-2*ksharp); end % End cosine boost for sharpening htarg = round(lpst/16); ht1 = round(lpst/72); ht2 = round(lpst/160); fptarg = ones(6*htarg,lpst-1); m1 = min(fpstep); m2 = max(fpstep); for (i1=1:2*htarg-1) fptarg(i1,:) = ... ((fpstep-m1)/(m2-m1)*(i1-1)+.5*(2*htarg-i1))/(2*htarg-1); end for (i1=2*htarg+7:3*htarg-1) fptarg(i1,:) = (fpstep-m1)/(m2-m1); end for (i1=3*htarg+7:4*htarg) fptarg(i1,:) = fpstep; end grayw = max(round(htarg/8),6); % Width of gray line. for (i1=2*htarg:2*htarg+grayw) fptarg(i1,:) = .5*ones(1,lpst-1); end for (i1=3*htarg:3*htarg+grayw) fptarg(i1,:) = .5*ones(1,lpst-1); end % Create tics for 2 to 200 lp/mm logarithmic scale. xt1 = log10(2); xt3 = (lpst-2)/(log10(200)-xt1); tic1 = [2 3 4 5 6 7 8 9 10 20 30 40 50 60 70 80 90 100 200]; tic2 = [2.5 3.5 15 25 35 150]; ltic1 = round(1+xt3*(log10(tic1)-xt1)); ltic2 = round(1+xt3*(log10(tic2)-xt1)); for (i1=4*htarg+1:4*htarg+1+ht1) fptarg(i1,ltic1) = 0; end for (i1=4*htarg+1:4*htarg+1+ht2) fptarg(i1,ltic2) = 0; end for (i1=5*htarg-ht1:5*htarg) fptarg(i1,ltic1) = 0; end for (i1=5*htarg-ht2:5*htarg) fptarg(i1,ltic2) = 0; end for (i1=5*htarg+1:6*htarg) fptarg(i1,:) = fporig; end imwrite(fptarg,'printarget.tif','tif'); % Write the target. fprintf(' Printer target has been written to printarget.tif.\n'); figure; subplot('Position',[.10 .44 .88 .52]); image(255*fptarg); colormap(cmap); subplot('Position',[.10 .10 .88 .34]); plot(fpstep); zoom on; grid on; ax = axis; ax(2) = length(fpstep); axis(ax); % Create a super target for photographing, etc. if (0) clear fptarg % Get some memory. This target is very long. fptarg = zeros(200,lpdsp); for (i1=1:200) fptarg(i1,:) = y6(1:lpdsp); end imwrite(fptarg,'idealtgt.tif','tif'); % Write the target. end end % END PRINTER TARGET yp3 = fscan(:,1:ldisp); syp3 = size(yp3); yp4 = yp3(1:2:syp3(1),1:5:syp3(2)); % Needs anti-aliasing. syp4 = size(yp4); yx1 = round(.76*syp3(2)); % Reduced box. yp4(:,2:syp4(2)-1) = (yp3(1:2:syp3(1),6:5:syp3(2)-5)+yp3(1:2:syp3(1),5:5:syp3(2)-6) ... +yp3(1:2:syp3(1),4:5:syp3(2)-7)+yp3(1:2:syp3(1),7:5:syp3(2)-4) ... +yp3(1:2:syp3(1),8:5:syp3(2)-3))/5; % Anti aliasing! yp3(6:5+syp4(1),yx1+1:yx1+syp4(2)) = yp4; yp3(6:35,yx1+1:yx1+5) = vones(1:30)*[zeros(1,5)]; % Box outline left yp3(6:35,yx1+1+syp4(2)+5:yx1+10+syp4(2)) = vones(1:30)*[zeros(1,5)]; yp3(5:30:36,yx1+1:yx1+syp4(2)+10) = vones(1:2)*[zeros(1,syp4(2)+10)]; % Box outline top,bot yp = [yp; yp3]; if (ksharp>eps) % Sharpen the image using a cosine boost. yp3 = fsharp(:,1:ldisp); yp4 = yp3(1:2:syp3(1),1:5:syp3(2)); % Needs anti-aliasing. % syp4 = size(yp4); yx1 = round(.76*syp3(2)); % Reduced box. yp4(:,2:syp4(2)-1) = (yp3(1:2:syp3(1),6:5:syp3(2)-5)+yp3(1:2:syp3(1),5:5:syp3(2)-6) ... +yp3(1:2:syp3(1),4:5:syp3(2)-7)+yp3(1:2:syp3(1),7:5:syp3(2)-4) ... +yp3(1:2:syp3(1),8:5:syp3(2)-3))/5; % Anti aliasing! yp3(6:5+syp4(1),yx1+1:yx1+syp4(2)) = yp4; yp3(6:35,yx1+1:yx1+5) = vones(1:30)*[zeros(1,5)]; % Box outline left yp3(6:35,yx1+1+syp4(2)+5:yx1+10+syp4(2)) = vones(1:30)*[zeros(1,5)]; yp3(5:30:36,yx1+1:yx1+syp4(2)+10) = vones(1:2)*[zeros(1,syp4(2)+10)]; % Box outline top,bot yp = [yp; yp3]; if (0) % Illustration of sharpening. if (1) turn on; if (0) turn off. temp1 = real(ifft(tfrtot)); temp1 = temp1./max(temp1); lt = length(temp1); sr = round(sscan); lt2 = lt-2*sr; lt3 = lt2/2-8*sr; lt4 = lt2/2+8*sr; xtemp = [lt3-lt2/2:lt4-lt2/2]/sscan; temp1 = [temp1(lt/2+1:lt) temp1(1:lt/2)]; % Put peak in center. temp2 = -ksharp*temp1(1+2*sr:lt); temp3 = -ksharp*temp1(1:lt-2*sr); temp4 = (temp1(1+sr:lt-sr)+temp2+temp3)/(1-2*ksharp); % Slimmed pulse. figure; subplot('Position',[.2 .2 .6 .6]); plot(xtemp,temp1(lt3+sr:lt4+sr),'k',... xtemp,temp4(lt3:lt4)/max(temp4),'r',xtemp,temp2(lt3:lt4),'b',... xtemp,temp3(lt3:lt4),'b'); % xtemp,temp4(lt3:lt4),'r:', xlabel('Spacing in pixels'); ylabel('Density (D)'); axis tight; zoom on; end % End sharpening illustration. tsharp= (1-2*ksharp*cos(2*pi*fdel*fq2/dscan))./(1-2*ksharp); % Sharpening MTF. tfrtot= tfrtot.*tsharp; lfs = length(fstep); fscan = fsharp; end % End cosine boost for sharpening y6 = real(fscan(1,:)); y7 = y4'*y6(1:ldisp) + (1-y4)'*.5*ones(size(y6(1:ldisp))); % Gradient; for combined plot. y7 = [y7; fscan(:,1:ldisp)]; % Combine tilted and gradient plots. y7 = min(y7,1); y7 = max(y7,0); % Limit the array to a range of {0,1}. sy7 = size(y7); y8 = y7(1:5:161, 1:5:sy7(2)); sy8 = size(y8); % Small box. y8(:,2:sy8(2)-1) = (y7(1:5:sy7(1),6:5:sy7(2)-5)+y7(1:5:sy7(1),5:5:sy7(2)-6) ... +y7(1:5:sy7(1),4:5:sy7(2)-7)+y7(1:5:sy7(1),7:5:sy7(2)-4) ... +y7(1:5:sy7(1),8:5:sy7(2)-3))/5; % Anti aliasing! y9 = zeros([sy8(1)+2 sy8(2)+20]); y9(2:sy8(1)+1,11:sy8(2)+10) = y8; sy9 = size(y9); yy1 = 16; yx1 = round(.76*sy7(2)); y7(yy1+1:yy1+sy9(1),yx1+1:yx1+sy9(2)) = y9; ffscan = fft(fscan(1,:)); tfrscan = abs(ffscan./f1); % MTF of scan. tfrsc = abs(sinc(fdel*fq2/dscan)).^sincpwr; % Scanner transfer function (sin(x)/x)^2. tfrscp = max(abs(tfrsc),.0001); % |Scanner tfr fn| to plot. tfrtot = tfrtot.*tfrscp; % Total transfer function to plot. [t50,i50] = min((tfrtot(1:lf2)-.5).^2); fq50 = fq2(i50)*fdel; % Find 50% point. if (tfrtot(i50)>.5) i51 = i50+1; else i51 = i50-1; end f51 = abs((tfrtot(i50)-.5)/(tfrtot(i50)-tfrtot(i51))); % Fraction i51 for interpolation. fq50 = fdel*((1-f51)*fq2(i50)+f51*fq2(i51)); % 50% point by interpolation. [t10,i10] = min((tfrtot(1:lf2)-.1).^2); fq10 = fq2(i10)*fdel; % Find 10% point. if (tfrtot(i10)>.1) i11 = i10+1; else i11 = i10-1; end f11 = abs((tfrtot(i10)-.1)/(tfrtot(i10)-tfrtot(i11))); % Fraction i11 for interpolation. fq10 = fdel*((1-f11)*fq2(i10)+f11*fq2(i11)); % 10% point by interpolation. figure; loglog(fq2(2:lf2)*fdel,100*tfrscan(2:lf2)); grid on; zoom on; hold on; loglog(fq2(2:lf2)*fdel,100*abs(tfrsc(2:lf2)),'r-'); hold off; ax(1) = ymin; ax(2) = ymax; ax(3) = .1; ax(4) = 1000.; if (max(tfrscan)<2 & ax(4)>200) ax(4) = 200; end; axis(ax); xlblm = ['Line pairs per mm; MTF = 50%,10% @ ' num2str(fq50,3) ', ' num2str(fq10,3) '/mm']; if (fboost>eps) xlblm = [xlblm '; Max MTF = ',num2str(max(tfrfn),3)]; end xlabel(xlblm); title(descript); % COMBINED PLOT figure; p1 = get(gcf,'Position'); p1(2) = p1(2)-60; p1(4) = p1(4)+60; p1(3) = p1(3)-80; set(gcf,'Position',p1); p2 = get(gcf,'PaperPosition'); p2(2) = p2(2)-1; p2(4) = p2(4)+2; set(gcf,'PaperPosition',p2); subplot('Position',[.10 .4 .88 .55]); image(255*y7); colormap(cmap); % get(gcf) gets properties. htxt = text(ldisp/2,9,['Scanner resolution = ' num2str(dscan,4) ' pixels/mm = ' ... num2str(dscan*25.4,4) '/in; sinc^' num2str(sincpwr,1)]); set(htxt,'Color',[1 1 .8],'Fontsize',10,'HorizontalAlignment','center','FontWeight','bold') title(descript); ylabel('Max contrast at bottom [0,1]; none at top [0.5] '); subplot('Position',[.10 .09 .88 .31]); loglog(fq2(2:lf2)*fdel,100*tfrfn(2:lf2),'b-',fq2(2:lf2)*fdel,100*tfrtot(2:lf2),'k-'); grid on; zoom on; hold on; if (ksharp>eps) tsn = max(tfrtot(2:lf2))/max(tfrscp(2:lf2).*tsharp(2:lf2)); % Normalize. loglog(fq2(2:lf2)*fdel,100*tfrscp(2:lf2).*tsharp(2:lf2)*tsn,'b:'); else loglog(fq2(2:lf2)*fdel,100*tfrscp(2:lf2),'b:'); end % Note f2 and fscan nominally vary between 0 and 1. fmax = max([.5 max(abs(f2(1:ldisp)-.5)) max(abs(fscan(1,1:ldisp)-.5))]); % Norml. to 0.5. loglog(fq1,10.^((real(f2(1:ldisp)) -.5)/fmax+1),'m-'); loglog(fq1,10.^((real(fscan(1,1:ldisp))-.5)/fmax+1),'r-'); % if (ksharp>eps) loglog(fq2(2:lf2)*fdel,100*tsharp(2:lf2),'g-'); end % Test only. plot([2 200],[10 10],'r--'); plot([2 200],[50 50],'b:'); hold off; ax = axis; ax(1) = ymin; ax(2) = ymax; % ax(3) = 1; % old ax(3) = 1; ax(4) = max([100 max(100*tfrfn) max(100*tfrtot)]); axis(ax); xlabel(xlblm); ylabel('MTF %, amplitude'); end % Sequence plot yp = yp./(max(max(yp))-min(min(yp))); yp = yp-min(min(yp)); % Normalize to overshoot. figure; subplot('Position',[.01 .115 .98 .87]); image(255*yp); axis off; colormap(cmap); if (nargin>=6) htxt = text(ldisp/30,4*lvon+lton+lv2,['Sharpen: ' num2str(ksharp,3)]); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold'); end if (nargin>=5) htxt = text(ldisp/30,4*lvon+lv2,['Scan: ' num2str(dscan,4) ' dpmm = ' ... num2str(dscan*25.4,4) ' dpi; sinc^' num2str(sincpwr,1)]); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold'); end if (nargin>=3) htxt = text(ldisp/30,lvon+lv2,['Lens only: flens = ' num2str(flens,3) ... '; lord = ' num2str(lord,2)]); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold'); htxt = text(ldisp/30,2*lvon+lv2,['Film only: f50 = ' num2str(f50,3) ... '; fboost = ' num2str(fboost,3)]); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold'); htxt = text(ldisp/30,3*lvon+lv2,'Film + lens'); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold'); else % Film only; no lens. htxt = text(ldisp/30,lvon+lv2,['Film: f50 = ' num2str(f50,3) ... '; fboost = ' num2str(fboost,3)]); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold'); end htxt = text(ldisp/30,lv2,['Original bands']); set(htxt,'Color',[1 .2 0],'Fontsize',10,'FontWeight','bold') subplot('Position',[.01 .11 .98 .005]); loglog(fq1,ones(size(fq1)),'k-'); ax = axis; ax(1) = ymin; ax(2) = ymax; axis(ax); xlabel(xlblm); % WRITE TIF FILE! fprintf('%s',7); % Ring bell. function y=sinc(x) %SINC Sin(pi*x)/(pi*x) function. y=ones(size(x)); i=find(x); y(i)=sin(pi*x(i))./(pi*x(i));