├── .gitignore ├── README.md ├── subplottight.m ├── phase_shift.m ├── str2matrix.m ├── duplicate_image.m ├── gs_fast.m ├── tight_subplot.m ├── gs_init.m ├── trap_obj.m └── slm_gui.m /.gitignore: -------------------------------------------------------------------------------- 1 | *.asv -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # slm-gerchberg-saxton 2 | Optical phase retrieval for optical tweezers with spatial light modulation, and a rough GUI 3 | -------------------------------------------------------------------------------- /subplottight.m: -------------------------------------------------------------------------------- 1 | function h = subplottight(n,m,i) 2 | % Makes borderless plots; use just like subplot 3 | [c,r] = ind2sub([m n], i); 4 | h = subplot('Position', [(c-1)/m, 1-(r)/n, 1/m, 1/n]); 5 | end 6 | 7 | -------------------------------------------------------------------------------- /phase_shift.m: -------------------------------------------------------------------------------- 1 | % change A from -pi:pi to 0:2*pi 2 | function B=phase_shift(A) 3 | 4 | [m,n]=size(A); 5 | AA=A(:); 6 | k_pos=find(AA>0); 7 | k_neg=find(AA<0); 8 | AA(k_pos)=0; 9 | AA(k_neg)=1; 10 | B=A+reshape(AA,m,n)*2*pi; 11 | 12 | end 13 | -------------------------------------------------------------------------------- /str2matrix.m: -------------------------------------------------------------------------------- 1 | function out = str2matrix(key, constant) 2 | switch key 3 | case 'w' 4 | out = [0 -constant]; 5 | case 'a' 6 | out = [-constant 0]; 7 | case 's' 8 | out = [0 constant]; 9 | case 'd' 10 | out = [constant 0]; 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /duplicate_image.m: -------------------------------------------------------------------------------- 1 | % Takes the input image, x_grid in x direction, y_grid in y direction 2 | function duplication_image = duplicate_image (A,x_grid,y_grid) 3 | 4 | [m,n] = size(A); % n in x direction, m in y direction 5 | 6 | X = floor(x_grid/n); % duplicate time in x 7 | x = x_grid - X*n; % columns left for duplication 8 | Y = floor(y_grid/m); % duplicate time in y 9 | y = y_grid - Y*m; % rows left for duplicatoin 10 | 11 | % duplicate columns 12 | B = A; 13 | for i = 1:X-1 14 | B = [B, A]; 15 | end 16 | B=[B, A(:, 1:x)]; 17 | 18 | % duplicate rows 19 | C = B; 20 | for i = 1:Y-1 21 | C = [C; B]; 22 | end 23 | C = [C; B(1:y, :)]; 24 | 25 | duplication_image = C; 26 | end 27 | -------------------------------------------------------------------------------- /gs_fast.m: -------------------------------------------------------------------------------- 1 | % No convergence plot, no tolerance specification, uses GPU for most calculations 2 | 3 | function gs_fast() 4 | % Algorithm for calculating the phase and target approximation. 5 | % Called from the trap_obj callback to update the phase and target 6 | % approximation plots. 7 | global glb; 8 | 9 | % ---- GS algorithm ---- % 10 | 11 | % Initialize phase estimate 12 | hologram_phase = gpuArray.rand(glb.x_src,glb.y_src); 13 | 14 | % Iterate between fourier and image domains 15 | for k = 1:glb.num 16 | hologram = glb.src_intensity .* exp(1i * hologram_phase); % get the current approximation in phasor form 17 | targ_approx = fftshift(fft2 (hologram, glb.x_targ, glb.y_targ)); % optical fourier transform 18 | targ_approx_intensity = abs(targ_approx); % current intensity at the target 19 | targ_approx_angle = angle(targ_approx); % current angle at the target 20 | targ_approx = glb.TARGET .* exp(1i * targ_approx_angle); % update the approx with the desired target intensity 21 | hologram_approx = ifftshift(ifft2 (fftshift(targ_approx), glb.x_src, glb.y_src)); % get the corresponding input to the approximation 22 | hologram_phase = angle(hologram_approx); % new phase at the input plane 23 | end 24 | hologram_phase = phase_shift(hologram_phase); 25 | 26 | % ---- Update Plots ---- % 27 | 28 | % hologram phase 29 | p = duplicate_image(hologram_phase, 792, 600); %792x600: size of the SLM 30 | phase2 = gather(p/max(max(p))); 31 | set(glb.holo, 'cdata', phase2); 32 | 33 | % target intensity 34 | set(glb.ti, 'cdata', glb.TARGET); 35 | 36 | % target approximation intensity 37 | maxVal = max(max(targ_approx_intensity)); 38 | targ_approx_intensity = targ_approx_intensity / maxVal; 39 | set(glb.tai, 'cdata', gather(targ_approx_intensity)); 40 | 41 | end 42 | -------------------------------------------------------------------------------- /tight_subplot.m: -------------------------------------------------------------------------------- 1 | function ha = tight_subplot(Nh, Nw, gap, marg_h, marg_w) 2 | 3 | % tight_subplot creates "subplot" axes with adjustable gaps and margins 4 | % 5 | % ha = tight_subplot(Nh, Nw, gap, marg_h, marg_w) 6 | % 7 | % in: Nh number of axes in hight (vertical direction) 8 | % Nw number of axes in width (horizontaldirection) 9 | % gap gaps between the axes in normalized units (0...1) 10 | % or [gap_h gap_w] for different gaps in height and width 11 | % marg_h margins in height in normalized units (0...1) 12 | % or [lower upper] for different lower and upper margins 13 | % marg_w margins in width in normalized units (0...1) 14 | % or [left right] for different left and right margins 15 | % 16 | % out: ha array of handles of the axes objects 17 | % starting from upper left corner, going row-wise as in 18 | % going row-wise as in 19 | % 20 | % Example: ha = tight_subplot(3,2,[.01 .03],[.1 .01],[.01 .01]) 21 | % for ii = 1:6; axes(ha(ii)); plot(randn(10,ii)); end 22 | % set(ha(1:4),'XTickLabel',''); set(ha,'YTickLabel','') 23 | 24 | % Pekka Kumpulainen 20.6.2010 @tut.fi 25 | % Tampere University of Technology / Automation Science and Engineering 26 | 27 | 28 | if nargin<3; gap = .02; end 29 | if nargin<4 || isempty(marg_h); marg_h = .05; end 30 | if nargin<5; marg_w = .05; end 31 | 32 | if numel(gap)==1; 33 | gap = [gap gap]; 34 | end 35 | if numel(marg_w)==1; 36 | marg_w = [marg_w marg_w]; 37 | end 38 | if numel(marg_h)==1; 39 | marg_h = [marg_h marg_h]; 40 | end 41 | 42 | axh = (1-sum(marg_h)-(Nh-1)*gap(1))/Nh; 43 | axw = (1-sum(marg_w)-(Nw-1)*gap(2))/Nw; 44 | 45 | py = 1-marg_h(2)-axh; 46 | 47 | ha = zeros(Nh*Nw,1); 48 | ii = 0; 49 | for ih = 1:Nh 50 | px = marg_w(1); 51 | 52 | for ix = 1:Nw 53 | ii = ii+1; 54 | ha(ii) = axes('Units','normalized', ... 55 | 'Position',[px py axw axh], ... 56 | 'XTickLabel','', ... 57 | 'YTickLabel',''); 58 | px = px+axw+gap(2); 59 | end 60 | py = py-axh-gap(1); 61 | end 62 | 63 | -------------------------------------------------------------------------------- /gs_init.m: -------------------------------------------------------------------------------- 1 | function gs_init(m,n,d) 2 | % First iteration of the complete GS algorithm, initializes trap objects 3 | % and other parameters 4 | global glb; 5 | 6 | %************************************************************************** 7 | % target 8 | %************************************************************************** 9 | 10 | % initialize trap objects into grid, and target image 11 | N = 200; 12 | image = zeros(N); 13 | SQSZ = glb.sz; % trap size in pixels 14 | 15 | figure(3); 16 | trapIndex = 0; 17 | for i = 1:n 18 | for j = 1:m 19 | trapIndex = trapIndex + 1; 20 | glb.trap(trapIndex) = trap_obj(trapIndex, SQSZ, gca, N/2-(m+1)/2*d+i*d,N/2-(n+1)/2*d+j*d); 21 | 22 | ycords = round(N/2-(m+1)/2*d+i*d-SQSZ/2):round(N/2-(m+1)/2*d+i*d+SQSZ/2); 23 | xcords = round(N/2-(n+1)/2*d+j*d-SQSZ/2):round(N/2-(n+1)/2*d+j*d+SQSZ/2); 24 | image(xcords, ycords) = 1; 25 | end 26 | end 27 | glb.flag = true; % records that a set of trap objects has been initialized 28 | 29 | glb.TARGET = double(image); % target image 30 | [glb.x_targ,glb.y_targ] = size(glb.TARGET); 31 | glb.x_src=glb.x_targ; 32 | glb.y_src=glb.y_targ; 33 | 34 | %************************************************************************** 35 | % computation source 36 | %************************************************************************** 37 | I0 = 1; 38 | w0 = 100; % minimum width, in grid 39 | w = 200; % refer to position, in grid 40 | 41 | x_src = glb.x_targ; % x dimension 42 | y_src = glb.y_targ; % y dimension 43 | centroid = [round(x_src/2),round(y_src/2)]; 44 | glb.src_intensity = zeros(x_src,y_src); 45 | for i = 1:x_src 46 | for j = 1:y_src 47 | r = ((i-centroid(1))^2+(j-centroid(2))^2)^0.5; 48 | glb.src_intensity(i, j) = I0*(w0/w)^2*exp(-2*r^2/w^2); 49 | end 50 | end 51 | 52 | % update source intensity 53 | imshow(glb.src_intensity, 'parent', glb.sub(2)); 54 | title('source intensity'); 55 | 56 | %************************************************************************** 57 | % Gerchberg and Saxton algorithm 58 | %************************************************************************** 59 | gs_fast(); % call the GS algorithm to update the approximation intensity 60 | 61 | end 62 | -------------------------------------------------------------------------------- /trap_obj.m: -------------------------------------------------------------------------------- 1 | % Usage: trap_obj(trap index, trap size, axis, x_coord, y_coord,) 2 | 3 | % impoint methods (for reference) 4 | %{ 5 | addNewPositionCallback le 6 | addlistener lt 7 | createMask ne 8 | delete notify 9 | eq removeNewPositionCallback 10 | findprop resume 11 | ge setColor 12 | getColor setConstrainedPosition 13 | getPosition setPosition 14 | getPositionConstraintFcn setPositionConstraintFcn 15 | gt setString 16 | impoint wait 17 | isvalid 18 | %} 19 | 20 | classdef trap_obj < impoint 21 | % object for a single optical trap 22 | properties 23 | trap_sz % size of the trap in pixels 24 | prevPos % previous position of the trap 25 | index % index in the array containing all trap objects 26 | end 27 | 28 | methods 29 | function obj = trap_obj(index, sz, varargin) % varargin = axis, x, y 30 | obj = obj@impoint(varargin{:}); % call impoint constructor 31 | 32 | obj.trap_sz = sz; 33 | obj.prevPos = obj.getPosition; 34 | obj.index = index; 35 | 36 | % Make new callback for drawing trap positions 37 | addNewPositionCallback(obj, @(pos) obj.drawPoint(pos)); 38 | 39 | % Boundary constraint function (prevents from dragging the 40 | % trap object outside of the figure window) 41 | bdn = makeConstrainToRectFcn('impoint',... 42 | get(gca,'XLim')+obj.trap_sz*[2 -2],... 43 | get(gca,'YLim')+obj.trap_sz*[2 -2]); 44 | 45 | % Enforce boundary constraint 46 | setPositionConstraintFcn(obj,bdn); 47 | 48 | end 49 | 50 | % draws the trap object on dependent plots 51 | % Updates position data and most recently clicked 52 | % Calls GS algorithm to update the hologram 53 | function drawPoint(obj, pos) 54 | global glb; 55 | 56 | SqSz = glb.sz; 57 | 58 | prevX = round(obj.prevPos(2)-SqSz/2):round(obj.prevPos(2)+SqSz/2); % previous x coordinates 59 | prevY = round(obj.prevPos(1)-SqSz/2):round(obj.prevPos(1)+SqSz/2); % previous y coordinates 60 | glb.TARGET(prevX,prevY) = 0; 61 | 62 | currX = round(pos(2)-SqSz/2):round(pos(2)+SqSz/2); % current x coordinates 63 | currY = round(pos(1)-SqSz/2):round(pos(1)+SqSz/2); % current y coordinates 64 | glb.TARGET(currX, currY) = 1; 65 | obj.prevPos = pos; % update the previous position 66 | glb.recent = obj.index; % mark this trap object as the most recently clicked 67 | gs_fast(); 68 | end 69 | 70 | % Deletes the trap object from the control figure, and updates 71 | % dependent fields and figures 72 | function deletePoint(obj) 73 | global glb; 74 | if obj.isvalid 75 | pos = obj.getPosition; 76 | SqSz = glb.sz; 77 | 78 | currX = round(pos(2)-SqSz/2):round(pos(2)+SqSz/2); 79 | currY = round(pos(1)-SqSz/2):round(pos(1)+SqSz/2); 80 | glb.TARGET(currX, currY) = 0; 81 | glb.recent = -1; 82 | obj.delete; 83 | gs_fast(); 84 | end 85 | end 86 | 87 | end 88 | end 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /slm_gui.m: -------------------------------------------------------------------------------- 1 | function slm_gui() 2 | % Enter parameters into Figure 1, separated by commas or spaces, for instance: 3 | % 4 | % 1, 20, 4, 4, 10 5 | % or 6 | % 1 20 4 4 10 7 | % 8 | % The parameters have the following interpretation: 9 | % 10 | % 'trap size' : the width and height of the trap, in pixels 11 | % 12 | % 'iterations' : the number of iterations used in the 13 | % approximation algorithm (Gerchberg-Saxton) 14 | % 15 | % 'rows' and 'columns' : the number of rows and columns in the initial 16 | % arrangement of traps 17 | % 18 | % 'distance' : the distance in pixels between traps in the initial 19 | % arrangement 20 | % 21 | % Key presses do the following: 22 | % 23 | % 'e' : Enables a cursor, that adds a new trap upon left-click. Press 24 | % 'enter' to escape without adding a trap. 25 | % 26 | % 'w' 'a' 's' 'd' : Moves the most recently clicked trap by 1 pixel 27 | % 28 | % 'backspace' : Deletes the most recently clicked trap 29 | % 30 | 31 | % initializes GUI figures, along with figure positions & properties 32 | global glb; % struct to store data across functions 33 | close all; 34 | 35 | glb.flag = false; 36 | 37 | % make figure handles 38 | glb.f(1) = figure('visible', 'off'); 39 | glb.f(2) = figure('visible', 'off'); 40 | glb.f(3) = figure('visible', 'off'); 41 | glb.f(4) = figure('visible', 'off'); 42 | 43 | % make subplot handles (for updating plots in other functns) 44 | figure(2); 45 | % glb.sub(1) = subplottight(2,2,1); <- unused subplot 46 | glb.sub(2) = subplottight(2,2,2); % source intensity 47 | glb.sub(3) = subplottight(2,2,3); % target intensity 48 | glb.ti = imshow(zeros(200,200)); 49 | glb.sub(4) = subplottight(2,2,4); % target approx. intensity 50 | glb.tai = imshow(zeros(200,200)); % 51 | 52 | % get screen parameters 53 | scnsize = get(0,'ScreenSize'); 54 | left = 0.65*scnsize(3); 55 | bottom = 0.3*scnsize(4); 56 | width = scnsize(3) - left - 10; 57 | height = width; 58 | 59 | % set figure 1 (parameter control) 60 | set(glb.f(1),'Position', [left-width/2, bottom+380,250,60]); 61 | 62 | hparams = uicontrol('Style','edit','Position',[15,15,200,20],... 63 | 'Callback',{@params_Callback},... 64 | 'parent', glb.f(1)); 65 | htext1 = uicontrol('Style','text','String',... 66 | 'trap size,iterations,rows,columns,distance',... 67 | 'Position',[9,40,225,15],... 68 | 'parent', glb.f(1)); 69 | 70 | % set figure 2 (target, targ. approx, and source) 71 | set(glb.f(2),'position',... 72 | [left bottom width height],... 73 | 'Renderer', 'zbuffer'); 74 | 75 | % set figure 3 (control figure) 76 | figure(3); 77 | imshow(zeros(200,200), 'border', 'tight'); 78 | set(glb.f(3), 'position', [left bottom+height/2 width/2 height/2],... 79 | 'KeyPressFcn', @keyPress_Callback); 80 | % 'WindowButtonDownFcn', @click_Callback); 81 | 82 | % set figure 4 (hologram phase) 83 | figure(4); 84 | lcSz = [600 792]; 85 | glb.holo = imshow(zeros(lcSz), 'border', 'tight'); % stores hologram data 86 | set(glb.f(4), 'position', [scnsize(3)-lcSz(2) bottom-50 lcSz(2) lcSz(1)],... 87 | 'Renderer', 'zbuffer'); 88 | 89 | % set rendering properties for image objects 90 | set(glb.holo, 'EraseMode', 'none'); 91 | set(glb.ti, 'EraseMode', 'none'); 92 | set(glb.tai, 'EraseMode', 'none'); 93 | 94 | % show all figures; 95 | figure(4); 96 | figure(2); 97 | figure(3); 98 | figure(1); 99 | 100 | % callback function for parameter control (on fig 1) 101 | function params_Callback(source, eventdata) 102 | t = num2cell(str2num(get(source, 'String'))); 103 | [glb.sz, glb.num, m, n, d] = deal(t{:}); % 'sz' : the trap size, in pixels 104 | % 'num' : number of iterations for the GS algorithm 105 | % 'm' : # of rows 106 | % 'n' : # of columns 107 | % 'd' : distance between traps, in pixels 108 | % clears old trap objects 109 | if glb.flag == true 110 | for i = 1:length(glb.trap) 111 | glb.trap(i).deletePoint; 112 | end 113 | end 114 | 115 | gs_init(m, n, d); 116 | end 117 | 118 | % callback function for key presses (on fig 3) 119 | function keyPress_Callback(source, eventdata) 120 | glb.stepSz = 1; % set constant for testing 121 | key = eventdata.Key; 122 | mostRecent = glb.recent; 123 | 124 | switch key 125 | % moves the most recently clicked trap by the stepSz, in pixels 126 | case {'w', 'a', 's', 'd'} 127 | if mostRecent >= 0 128 | currentPos = glb.trap(mostRecent).getPosition; 129 | step = str2matrix(key, glb.stepSz); 130 | glb.trap(mostRecent).setPosition(currentPos+step); 131 | end 132 | % deletes the most recently clicked trap 133 | case 'backspace' 134 | if mostRecent >= 0 135 | glb.trap(mostRecent).deletePoint; 136 | end 137 | % enables a cursor and adds a trap upon clicking. 'enter' to escape 138 | case 'e' 139 | [x,y] = ginput(1); % gets cursor position upon clicking 140 | trapIndex = length(glb.trap) + 1; 141 | glb.trap(trapIndex) = trap_obj(trapIndex, glb.sz, gca, x, y); 142 | glb.trap(trapIndex).drawPoint([x,y]); 143 | end 144 | end 145 | 146 | end 147 | 148 | --------------------------------------------------------------------------------