├── CircularPath.m ├── ColorLena256.png ├── PathMat2OrderMat.m ├── README.md ├── ZigzagFourier.m ├── existVectorInMat.m ├── getBayerFringe.m ├── getFSPIReconstruction.m ├── getFourierPattern.m ├── getHalfSpecMask.m ├── getInitPhaseArr.m ├── getOrderMat.m ├── getOrderMatFilename.m ├── getRealFourierCoeftList.m ├── main.m ├── rmse.m └── specshow.m /CircularPath.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/CircularPath.m -------------------------------------------------------------------------------- /ColorLena256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/ColorLena256.png -------------------------------------------------------------------------------- /PathMat2OrderMat.m: -------------------------------------------------------------------------------- 1 | 2 | function OrderMat = PathMat2OrderMat(PathMat, mRow, nCol, PathStr) 3 | [ OrderMatFilename ] = getOrderMatFilename( mRow, nCol, PathStr ); 4 | 5 | if exist(OrderMatFilename, 'file') 6 | load(OrderMatFilename); 7 | disp('The path matrix exists.'); 8 | else 9 | disp('Generating the path matrix...'); 10 | 11 | Mask = getHalfSpecMask(mRow, nCol); 12 | 13 | PathMatMasked = PathMat .* Mask; 14 | PathMatMaskedArr = reshape(PathMatMasked, [length(PathMatMasked(:)) 1]); 15 | [Val, Ind] = sort(PathMatMaskedArr, 'ascend'); 16 | 17 | FirstNonZeroElement = find(Val~=0); 18 | FirstNonZeroElementInd = FirstNonZeroElement(1); 19 | Ind = Ind(FirstNonZeroElementInd:end); 20 | 21 | nPoint = length(Ind); 22 | OrderMat = zeros(nPoint, 2); 23 | 24 | for iPoint = 1:nPoint 25 | OrderMat(iPoint,2) = floor(Ind(iPoint) / mRow) + 1; 26 | OrderMat(iPoint,1) = mod(Ind(iPoint)- 1, mRow) + 1; 27 | end 28 | 29 | disp('Generating the path matrix...Done!'); 30 | 31 | save(OrderMatFilename, 'OrderMat'); 32 | end 33 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Simultaneous spatial, spectral, and 3D compressive imaging via efficient Fourier single-pixel measurements (Color Fourier single-pixel imaging) 2 | https://www.osapublishing.org/optica/fulltext.cfm?uri=optica-5-3-315&id=383834 3 | -------------------------------------------------------------------------------- /ZigzagFourier.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/ZigzagFourier.m -------------------------------------------------------------------------------- /existVectorInMat.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/existVectorInMat.m -------------------------------------------------------------------------------- /getBayerFringe.m: -------------------------------------------------------------------------------- 1 | function ColorFringe = getBayerFringe(fringe) 2 | [mRow, nCol] = size(fringe); 3 | 4 | %% Color imaging 5 | RedMaskSingleton = [0 1; 0 0]; 6 | GreenMaskSingleton = [1 0; 0 1]; 7 | BlueMaskSingleton = [0 0; 1 0]; 8 | 9 | %% chromatic masks 10 | RedMask = repmat(RedMaskSingleton, [mRow/2, nCol/2]); 11 | GreenMask = repmat(GreenMaskSingleton, [mRow/2, nCol/2]); 12 | BlueMask = repmat(BlueMaskSingleton, [mRow/2, nCol/2]); 13 | 14 | ColorFringe(:,:,1) = RedMask .* fringe; 15 | ColorFringe(:,:,2) = GreenMask .* fringe; 16 | ColorFringe(:,:,3) = BlueMask .* fringe; 17 | end -------------------------------------------------------------------------------- /getFSPIReconstruction.m: -------------------------------------------------------------------------------- 1 | function [img, spec] = getFSPIReconstruction( I, nStepPS, Phaseshift ) 2 | % Phase-shifting formulae 3 | if and(nStepPS == 4, Phaseshift == 90) 4 | spec = (I(:,:,1)-I(:,:,2))+1i*(I(:,:,3)-I(:,:,4)); 5 | end 6 | 7 | if and(nStepPS == 3, Phaseshift == 120) 8 | spec = (2*(I(:,:,1))-I(:,:,2)-I(:,:,3)) + sqrt(3)*1i*(I(:,:,2)-I(:,:,3)); 9 | spec = spec / (3/2); 10 | end 11 | 12 | if and(nStepPS == 3, Phaseshift == 90) 13 | spec = ((I(:,:,3))-I(:,:,2)) + 1i*(2*I(:,:,1)-I(:,:,2)-I(:,:,3)); 14 | end 15 | 16 | spec = completeSpec(spec); 17 | img = real(ifft2(ifftshift(spec))); 18 | end 19 | 20 | function fullSpec = completeSpec(halfSpec) 21 | % Complete a Fourier spectrum according to central symmetry (conjugation) 22 | [mRow, nCol] = size(halfSpec); 23 | fullSpec = zeros(size(halfSpec)); 24 | 25 | if mod(mRow,2) == 1 && mod(nCol,2) == 1 % nPixel is odd 26 | halfSpec = halfSpec + rot90(conj(halfSpec), 2); 27 | halfSpec(ceil(mRow/2),ceil(nCol/2)) = (halfSpec(ceil(mRow/2),ceil(nCol/2)))/2; 28 | fullSpec = halfSpec; 29 | else 30 | if mod(mRow, 2) == 0 && mod(nCol, 2) == 0 % Even * Even 31 | RightBottomHalfSpec = halfSpec(2:end, 2:end); 32 | RightBottomFullSpec = completeSpec(RightBottomHalfSpec); 33 | 34 | fullSpec(2:end, 2:end) = RightBottomFullSpec; 35 | 36 | TopLine = halfSpec(1, 2:end); 37 | TopLine = completeSpec(TopLine); 38 | fullSpec(1, 2:end) = TopLine; 39 | 40 | LeftColumn = halfSpec(2:end, 1); 41 | LeftColumn = completeSpec(LeftColumn); 42 | fullSpec(2:end, 1) = LeftColumn; 43 | 44 | fullSpec(1,1) = halfSpec(1,1); 45 | else 46 | if mod(mRow, 2) == 1 && mod(nCol, 2) == 0 % ODD * EVEN 47 | LeftColumn = halfSpec(1:end, 1); 48 | LeftColumn = completeSpec(LeftColumn); 49 | 50 | RightHalfSpec = halfSpec(:, 2:end); 51 | RightFullSpec = completeSpec(RightHalfSpec); 52 | 53 | fullSpec(1:end, 1) = LeftColumn; 54 | fullSpec(:, 2:end) = RightFullSpec; 55 | else % EVEN * ODD 56 | halfSpec = halfSpec'; 57 | fullSpec = completeSpec(halfSpec); 58 | fullSpec = fullSpec'; 59 | end 60 | 61 | end 62 | end 63 | end 64 | 65 | -------------------------------------------------------------------------------- /getFourierPattern.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/getFourierPattern.m -------------------------------------------------------------------------------- /getHalfSpecMask.m: -------------------------------------------------------------------------------- 1 | function Mask = getHalfSpecMask (mRow, nCol) 2 | Mask = zeros(mRow, nCol); 3 | 4 | if mod(mRow, 2) == 0 && mod(nCol, 2) == 0 % EVEN x EVEN 5 | smallerMask = getHalfSpecMask (mRow - 1, nCol - 1); 6 | Mask(2:end, 2:end) = smallerMask; 7 | 8 | Mask(2:mRow/2+1, 1) = 1; 9 | Mask(1,1) = 1; 10 | Mask(1, 2:nCol/2+1) = 1; 11 | else % ODD x ODD 12 | if mod(mRow, 2) == 1 && mod(nCol, 2) == 1 13 | Mask(1:ceil(mRow/2), :) = 1; 14 | Mask(ceil(mRow/2), ceil(nCol/2)+1:end) = 0; 15 | else % ODD x EVEN 16 | if mod(mRow,2) == 1 && mod(nCol,2) == 0 17 | Mask(1:ceil(mRow/2), 2:end) = 1; 18 | Mask(ceil(mRow/2), ceil(nCol/2)+2:end) = 0; 19 | Mask(1:ceil(mRow/2), 1) = 1; 20 | else % EVEN x ODD 21 | Mask(1:mRow/2+1, :) = 1; 22 | Mask(mRow/2+1, ceil(nCol/2)+1:end) = 0; 23 | Mask(1, ceil(nCol/2)+1:end) = 0; 24 | end 25 | end 26 | end 27 | end -------------------------------------------------------------------------------- /getInitPhaseArr.m: -------------------------------------------------------------------------------- 1 | function InitPhaseArr = getInitPhaseArr(nStep, phaseshift) 2 | if and(nStep == 4, phaseshift == 90) 3 | InitPhaseArr = [0, pi, pi/2, 3*pi/2]; 4 | return; 5 | end 6 | 7 | if and(nStep == 3, phaseshift == 120) 8 | InitPhaseArr = [0, 2*pi/3, 4*pi/3]; 9 | return; 10 | end 11 | 12 | if and(nStep == 3, phaseshift == 90) 13 | InitPhaseArr = [0, pi/2, -pi/2]; 14 | return; 15 | end 16 | 17 | error('No corresponding initial phase array found!'); 18 | end -------------------------------------------------------------------------------- /getOrderMat.m: -------------------------------------------------------------------------------- 1 | %% GENERATE THE PATH MATRIX -> orderMat 2 | % Rev 1: 2016/06/23 by Charles Cheung 3 | % nPixel can be odd or even. 4 | function OrderMat = getOrderMat(mRow, nCol, PathStr) 5 | if mRow > nCol 6 | nPixel = mRow; 7 | else 8 | nPixel = nCol; 9 | end 10 | 11 | switch PathStr 12 | case 'spiral' 13 | PathMat = rot90(spiral(nPixel), 2); 14 | case 'circular' 15 | PathMat = CircularPath( nPixel, nPixel, floor(nPixel/2)+1, floor(nPixel/2)+1, ceil(ceil(nPixel/2)*sqrt(2)) ); 16 | case 'diamond' 17 | PathMat = ZigzagFourier(nPixel); 18 | end 19 | 20 | 21 | LeftMargin = floor((nPixel - nCol)/2); 22 | RightMargin = floor((nPixel - nCol)/2); 23 | TopMargin = floor((nPixel - mRow)/2); 24 | BottomMargin = floor((nPixel - mRow)/2); 25 | 26 | PathMat = PathMat(TopMargin + 1 : TopMargin + mRow, ... 27 | LeftMargin + 1 : LeftMargin + nCol); 28 | 29 | OrderMat = PathMat2OrderMat(PathMat, mRow, nCol, PathStr); 30 | -------------------------------------------------------------------------------- /getOrderMatFilename.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/getOrderMatFilename.m -------------------------------------------------------------------------------- /getRealFourierCoeftList.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zibangzhang/Color-Fourier-Single-pixel-Imaging/d0cb330641b2a8288b9d11282a0feebd6222b4b0/getRealFourierCoeftList.m -------------------------------------------------------------------------------- /main.m: -------------------------------------------------------------------------------- 1 | % Simulation for color Fourier single-pixel imaging 2 | % 05/31/2021 3 | 4 | close all 5 | clear all 6 | clc 7 | TimeStamp = datestr(now, 'YYmmDD_HHMMSS'); 8 | 9 | %% SWITCH 10 | SW_NOISE = 0; % 0: noiseless, 1: noisy 11 | 12 | %% Parameters 13 | nStepPS = 3; % n-step phase-shifting 14 | Phaseshift = 120; % phase shift 15 | Amplitude = 1; % amplitude of sin. pattern 16 | SamplingRatio = 1; % e.g. sampling ratio 17 | SamplingPath = 'circular'; % sprial, diamond, circular 18 | 19 | % Get input image 20 | [imgFile pathname] = uigetfile({'*.bmp;*.jpg;*.tif;*.png;*.gif'','... 21 | 'All Image Files';'*.*','All Files'}); 22 | InputImg = im2double(imread([pathname imgFile])); 23 | figure,imshow(InputImg);title('Input image'); axis image; 24 | 25 | [mRow, nCol, nBand] = size(InputImg); 26 | if nBand ~= 3 27 | error('Input image is not a color image.'); 28 | end 29 | 30 | [fxMat, fyMat] = meshgrid([0:1:nCol-1]/nCol, [0:1:mRow-1]/mRow); % generate coordinates in Fourier domain (not neccessary) 31 | fxMat = fftshift(fxMat); 32 | fyMat = fftshift(fyMat); 33 | 34 | OrderMat = getOrderMat(mRow, nCol, SamplingPath); % generate sampling path in Fourier domain 35 | [nCoeft,tmp] = size(OrderMat); 36 | nCoeft = round(nCoeft * SamplingRatio); 37 | 38 | InitPhaseArr = getInitPhaseArr(nStepPS, Phaseshift); 39 | IntensityMat = zeros(mRow, nCol, nStepPS); 40 | 41 | RealFourierCoeftList = getRealFourierCoeftList(mRow, nCol); 42 | 43 | if SW_NOISE 44 | ReponseNoise = rand(nCoeft * nStepPS) * 2; % add noise 45 | end 46 | 47 | %% Main loop for simulating time-varying patterns illumiantion and single-pixel detection 48 | tic; 49 | 50 | for iCoeft = 1:nCoeft 51 | iRow = OrderMat(iCoeft,1); 52 | jCol = OrderMat(iCoeft,2); 53 | 54 | fx = fxMat(iRow,jCol); 55 | fy = fyMat(iRow,jCol); 56 | 57 | IsRealCoeft = existVectorInMat( [iRow jCol], RealFourierCoeftList ); 58 | 59 | for iStep = 1:nStepPS 60 | if IsRealCoeft == 1 && iStep > 2 61 | if nStepPS == 3 62 | IntensityMat(iRow,jCol,iStep) = IntensityMat(iRow,jCol,2); 63 | end 64 | if nStepPS == 4 65 | IntensityMat(iRow,jCol,iStep) = 0; 66 | end 67 | continue; 68 | end 69 | 70 | [ Pattern ] = getFourierPattern( Amplitude, mRow, nCol, fx, fy, InitPhaseArr(iStep) ); 71 | PatternColor = getBayerFringe(Pattern); 72 | 73 | IntensityMat(iRow, jCol, iStep) = sum(sum(sum(InputImg .* PatternColor))); 74 | end 75 | end 76 | 77 | toc; 78 | 79 | %% Show and save results 80 | [img, spec] = getFSPIReconstruction( IntensityMat, nStepPS, Phaseshift ); 81 | 82 | figure, imshow(img); caxis([0 1]); axis image; colormap gray; title('Reconstructed Img'); 83 | figure, specshow(spec); % show Fourier spectrum in a log scale 84 | 85 | % Color reconstruction 86 | imgColor = im2double(demosaic(uint8(img * 255), 'grbg')); 87 | figure, imshow(imgColor); 88 | 89 | PSNR = psnr(imgColor, InputImg); 90 | SSIM = ssim(imgColor, InputImg); 91 | RMSE = rmse(InputImg, imgColor); 92 | 93 | fprintf('PNSR = %f\nSSIM = %f\nRMSE = %f\n', PSNR, SSIM, RMSE); -------------------------------------------------------------------------------- /rmse.m: -------------------------------------------------------------------------------- 1 | function r=rmse(data,estimate) 2 | % Function to calculate root mean square error from a data vector or matrix 3 | % and the corresponding estimates. 4 | % Usage: r=rmse(data,estimate) 5 | % Note: data and estimates have to be of same size 6 | % Example: r=rmse(randn(100,100),randn(100,100)); 7 | 8 | % delete records with NaNs in both datasets first 9 | I = ~isnan(data) & ~isnan(estimate); 10 | data = data(I); estimate = estimate(I); 11 | 12 | r=sqrt(sum((data(:)-estimate(:)).^2)/numel(data)); -------------------------------------------------------------------------------- /specshow.m: -------------------------------------------------------------------------------- 1 | function specshow(spec) 2 | imagesc(mat2gray((log(abs(spec)+1).^(1/3)))); axis image; colormap jet; 3 | end --------------------------------------------------------------------------------