├── .idea ├── .gitignore ├── CODA.iml ├── inspectionProfiles │ └── profiles_settings.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── CODA Visium integration scripts 05-07-2024 ├── HE_cell_count_visium.m ├── README.txt └── register_CODA_segmentation_to_visium_slide.m ├── original upload version ├── HE_cell_count.m ├── base │ ├── annotation_bounding_boxes.m │ ├── bpassW.m │ ├── calculate_elastic_registrationC.m │ ├── calculate_elastic_registrationS.m │ ├── calculate_global_reg.m │ ├── calculate_tissue_space_082.m │ ├── calculate_transform.m │ ├── colordeconv2pw4_log10.m │ ├── combine_tiles_big.m │ ├── deeplab_classification.m │ ├── detect_scales.m │ ├── edit_individual_classes_082.m │ ├── fill_annotations_file.m │ ├── find_tissue_area.m │ ├── gcnt.m │ ├── getImLocalWindowInd_rf.m │ ├── get_ims.m │ ├── image_preprocessing_global.m │ ├── invert_D.m │ ├── load_xml_file.m │ ├── make_final_grids.m │ ├── make_rgb_kmeans_90.m │ ├── mskcircle2_rect.m │ ├── pad_im.m │ ├── pad_im_both.m │ ├── pad_im_both2.m │ ├── pkfndW.p │ ├── plot_settings.m │ ├── preprocessing.m │ ├── random_augmentation.m │ ├── reg_ims_EL.m │ ├── reg_ims_ELS.m │ ├── reg_ims_com.m │ ├── register_cell_coordinates_1type.m │ ├── register_cell_coordinates_list.m │ ├── register_cell_coordinates_new.m │ ├── register_global_im.m │ ├── register_images_2E.m │ ├── rescale_ims.m │ ├── save_images_global.m │ ├── save_inverted_Ds.m │ ├── train_deeplab.m │ ├── xcorrf2.m │ └── xml2struct2.m ├── make_training_deeplab.m ├── normalize_HE.m ├── plot_3D_tiss.m ├── register_cell_coordinates.m ├── register_images.m ├── save_cell_coordinates_registered.m └── save_images_elastic2.m └── update 12-13-2023 ├── README.txt ├── apply_image_registration.m ├── build_cell_volume.m ├── build_tissue_volume.m ├── calculate_image_registration.m ├── calculate_tissue_ws.m ├── cell_detection.m ├── cell_detection_base_functions ├── calculate_optimal_vals.m ├── cell_cell_dist.m ├── colordeconv_log10.m ├── im2mat.p └── pkfndW.p ├── combine_z_projections.m ├── create_downsampled_tif_images.m ├── deconvolve_histological_images.m ├── get_mpp_of_image.m ├── get_nuclear_detection_parameters.m ├── image registration base functions ├── bpassW.m ├── calculate_elastic_registration.m ├── calculate_global_reg.m ├── calculate_global_reg_IHC.m ├── calculate_transform.m ├── find_tissue_area.m ├── gcnt.m ├── getImLocalWindowInd_rf.m ├── get_ims.m ├── group_of_reg.m ├── im2mat.p ├── invert_D.m ├── make_final_grids.m ├── mskcircle2_rect.m ├── pad_im_both2.m ├── preprocessing.m ├── reg_ims_ELS.m ├── reg_ims_com.m ├── register_global_im.m └── xcorrf2.m ├── make_cell_detection_mosaic.m ├── manual_cell_count.m ├── register_cell_coordinates.m ├── segmentation base functions ├── build_model_tiles.m ├── calculate_tissue_space.m ├── combine_tiles_density_shuffle.m ├── deeplab_classification.m ├── fill_annotations_file.m ├── load_xml_file.m ├── load_xml_loop.m ├── make_check_annotation_classified_image.m ├── make_cmap_legend.m ├── make_confusion_matrix.m ├── random_augmentation.m ├── save_annotation_bounding_boxes.m ├── test_model_performance.m ├── train_deeplab.m └── xml2struct2.m ├── train_image_segmentation.m └── train_image_segmentation_lung.m /.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | # Datasource local storage ignored files 5 | /dataSources/ 6 | /dataSources.local.xml 7 | # Editor-based HTTP Client requests 8 | /httpRequests/ 9 | -------------------------------------------------------------------------------- /.idea/CODA.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /CODA Visium integration scripts 05-07-2024/HE_cell_count_visium.m: -------------------------------------------------------------------------------- 1 | function HE_cell_count_visium(pth) 2 | path(path,'\\motherserverdw\ashleysync\PanIN Modelling Package\image registration'); % for getting DAB stain from IHC images 3 | outpth=[pth,'cell_coords\']; 4 | mkdir(outpth); 5 | imlist=dir([pth,'*tif']); 6 | 7 | tic; 8 | for kk=1:length(imlist) 9 | imnm=imlist(kk).name; 10 | if exist([outpth,imnm(1:end-3),'mat'],'file');continue;end 11 | 12 | % count cells 13 | imH=imread([pth,imnm]); 14 | imH=double(imH); 15 | tmp=imH(:,:,3)-imH(:,:,1); 16 | tmp=imgaussfilt(tmp,1); 17 | xy=pkfndW(double(tmp),70,7); % minimum brightness, size of object 18 | % figure(2),imshow(uint8(imH));axis equal;hold on;plot(xy(:,1),xy(:,2),'ro'); 19 | 20 | disp(round([kk length(imlist)])) 21 | save([outpth,imnm(1:end-3),'mat'],'xy'); 22 | 23 | imout=zeros(size(tmp)); 24 | ii=sub2ind(size(imout),xy(:,2),xy(:,1)); 25 | imout(ii)=1; 26 | imwrite(uint8(imout),[outpth,imnm]); 27 | end 28 | 29 | end 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /CODA Visium integration scripts 05-07-2024/README.txt: -------------------------------------------------------------------------------- 1 | This document contains instructions for using CODA (Kiemen, 2 | et al Nature Methods, 2022, code version 'update 12-13-2023) 3 | to segment tissue structures in H&E, generate coordinates of 4 | cell nuclei, and deconvolve Visium spots. 5 | 6 | First, follow the protocol and scripts to segment the major 7 | tissue structures and deconvolve the H&E image, as described 8 | in https://zenodo.org/records/11130691 9 | 10 | Given the results of the classification use the following 11 | to determine nuclear coordinates, register the high-resolution 12 | H&E image to the Visium H&E, and determine the number and 13 | identity of cells located within each Visium spot. 14 | 15 | 1. call "HE_cell_count_visium(pth)" to generate mask images 16 | containing logical indices for each cellular nucleas detected 17 | in the H&E image. -------------------------------------------------------------------------------- /original upload version/HE_cell_count.m: -------------------------------------------------------------------------------- 1 | function HE_cell_count(pth) 2 | path(path,'\\motherserverdw\ashleysync\PanIN Modelling Package\image registration'); % for getting DAB stain from IHC images 3 | 4 | 5 | outpth=[pth,'cell_coords\']; 6 | mkdir(outpth); 7 | 8 | imlist=dir([pth,'*tif']); 9 | if isempty(imlist);imlist=dir([pth,'*jp2']);end 10 | 11 | tic; 12 | xyc=zeros([1 length(imlist)]); 13 | tot_area=zeros([1 length(imlist)]); 14 | tic; 15 | for kk=1:length(imlist) 16 | imnm=imlist(kk).name; 17 | if exist([outpth,imnm(1:end-3),'mat'],'file');continue;end 18 | %if kk>1 && contains(imnm(end-5),'0');disp(imnm);continue;end 19 | 20 | % count cells 21 | imH=imread([pth,imnm]); 22 | imH=imH(:,:,1); 23 | ii=imH;ii=ii(ii~=0);imH(imH==0)=mode(ii); 24 | imH=255-imH; 25 | 26 | %imB=bpassW(imH,1,3); % size of noise, size of object 27 | imB=imgaussfilt(imH,1); 28 | xy=pkfndW(double(imB),110,7); % minimum brightness, size of object 29 | %whos xy0 xy 30 | if kk==1;figure(2),imshow(255-imH);axis equal;hold on;plot(xy(:,1),xy(:,2),'ro');end 31 | disp(round([kk length(imlist) xyc(kk)])) 32 | 33 | % ii=sub2ind(size(imB),xy(:,2),xy(:,1)); 34 | % ind=imH(ii); 35 | % ii=ii(ind>170); 36 | % [y,x]=ind2sub(size(imB),ii); 37 | % xy=[x y]; 38 | 39 | %figure(2),imshow(255-imH);axis equal;hold on;plot(xy(:,1),xy(:,2),'ro'); 40 | %figure,imshow(255-imH);axis equal;hold on;plot(xy(:,1),xy(:,2),'ro'); 41 | %whos xy 42 | %xy{kk}=count_cells(imH); 43 | xyc(kk)=size(xy,1); 44 | 45 | 46 | %t=imB>3; 47 | %t=imclose(t,strel('disk',3)); 48 | %t=bwareaopen(t,1000); 49 | %tot_area(kk)=sum(t(:))*2*2/1000000; % area in square mm 50 | 51 | % save([outpth,imnm(1:end-3),'mat'],'xy','tot_area'); 52 | save([outpth,imnm(1:end-3),'mat'],'xy'); 53 | end 54 | % save([pth,'cell_locations.mat'],'xy','xyc','tot_area','-v7.3'); 55 | % figure,plot([1:length(imlist)]*12/1000,xyc),title('cell count'); 56 | % xlabel('mm along z axis') 57 | % ylabel('cell count per tissue section') 58 | % ylim([0 2500000]) 59 | 60 | % figure,plot([1:length(imlist)]*12/1000,xyc./tot_area),title('density'); 61 | % xlabel('mm along z axis') 62 | % ylabel('cell density per tissue section (cells / mm^2)') 63 | % ylim([0 10000]) 64 | end 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /original upload version/base/annotation_bounding_boxes.m: -------------------------------------------------------------------------------- 1 | function [numann,ctlist0]=annotation_bounding_boxes(im,outpth,nm,numclass,fn,imlabel) 2 | disp(' Creating bounding box tiles of all annotations') 3 | 4 | im=double(im); 5 | if ~exist('imlabel','var') 6 | try 7 | imlabel=imread([outpth,'view_annotations.tif']); 8 | catch 9 | imlabel=imread([outpth,'view_annotations_raw.tif']); 10 | end 11 | end 12 | imlabel=double(imlabel); 13 | 14 | pthbb=[outpth,nm,'_boundbox\']; 15 | pthim=[pthbb,'im\']; 16 | pthlabel=[pthbb,'label\']; 17 | if isfolder('pthim');rmdir(pthim);rmdir(pthlabel);end 18 | mkdir(pthim);mkdir(pthlabel); 19 | numann=[]; 20 | count=1; 21 | 22 | 23 | tmp=imclose(imlabel>0,strel('disk',10)); 24 | tmp=imfill(tmp,'holes'); 25 | tmp=bwareaopen(tmp,500); 26 | L=bwlabel(tmp); 27 | for pk=1:max(L(:)) 28 | tmp=double(L==pk); 29 | a=sum(tmp,1);b=sum(tmp,2); 30 | rect=[find(a,1,'first') find(a,1,'last') find(b,1,'first') find(b,1,'last')]; 31 | tmp=tmp(rect(3):rect(4),rect(1):rect(2)); 32 | 33 | % make label bounding box 34 | tmplabel=imlabel(rect(3):rect(4),rect(1):rect(2)).*tmp; 35 | 36 | % make H&E bounding box 37 | tmp=tmplabel>0; 38 | %tmpim=im(rect(3):rect(4),rect(1):rect(2),:).*tmp; 39 | tmpim=im(rect(3):rect(4),rect(1):rect(2),:); 40 | 41 | nm=num2str(count,'%05.f'); 42 | imwrite(uint8(tmpim),[pthim,nm,'.tif']); 43 | imwrite(uint8(tmplabel),[pthlabel,nm,'.tif']); 44 | 45 | for anns=1:numclass 46 | numann(count,anns)=sum(tmplabel(:)==anns); 47 | end 48 | count=count+1; 49 | end 50 | ctlist0=dir([pthim,'*tif']); 51 | 52 | bb=1; % indicate that xml file is fully analyzed 53 | if exist('fn','var') 54 | if exist([outpth,fn],'file') 55 | save([outpth,fn],'numann','ctlist0','bb','-append'); 56 | else 57 | save([outpth,fn],'numann','ctlist0','bb'); 58 | end 59 | else 60 | if exist([outpth,'annotations.mat'],'file') 61 | save([outpth,'annotations.mat'],'numann','ctlist0','bb','-append'); 62 | else 63 | save([outpth,'annotations.mat'],'numann','ctlist0','bb'); 64 | end 65 | end -------------------------------------------------------------------------------- /original upload version/base/bpassW.m: -------------------------------------------------------------------------------- 1 | function res = bpassW(arr,lnoise,lobject) 2 | % 3 | % bandpass filter. 4 | % 5 | % Written by Pei-Hsun Wu, 6 | % Post-doc associate, @ JHU, IMBT. 7 | % 8 | 9 | 10 | b = double(lnoise); 11 | w = round(lobject); 12 | N=2*w+1; 13 | hg=fspecial('gaussian',N, b*sqrt(2)); 14 | arrg = imfilter(arr,hg,'symmetric','conv') ; 15 | ha = fspecial('average',N); 16 | arra = imfilter(arr,ha,'symmetric','conv'); 17 | rest = max(arrg-arra,0); 18 | % res = zeros(size(rest)); % for fixe the edge 19 | % res ( w+1 : end-w, w+1 : end-w) = rest ( w+1 : end-w, w+1 : end-w) ; 20 | % 21 | res=rest; 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /original upload version/base/calculate_elastic_registrationC.m: -------------------------------------------------------------------------------- 1 | function [D,xgg,ygg,xx,yy]=calculate_elastic_registrationC(imrfR,immvR,TArf,TAmv,sz,bf,di,cutoff) 2 | if ~exist('cutoff','var');cutoff=0.15;end 3 | cc=10;cc2=cc+1; 4 | szim=size(immvR); 5 | m=(sz-1)/2+1; 6 | 7 | immvR=double(padarray(immvR,[bf bf],mode(immvR(:))));immvR=imgaussfilt(immvR,3); 8 | imrfR=double(padarray(imrfR,[bf bf],mode(imrfR(:))));imrfR=imgaussfilt(imrfR,3); 9 | TAmv=padarray(TAmv,[bf bf],0); 10 | TArf=padarray(TArf,[bf bf],0); 11 | 12 | % make grid for registration points 13 | n1=randi(round(di/2),1)+bf+m; 14 | n2=randi(round(di/2),1)+bf+m; 15 | [x,y]=meshgrid(n1:di:size(immvR,2)-m-bf,n2:di:size(immvR,1)-m-bf); 16 | x=x(:);y=y(:); 17 | 18 | % get percentage of tissue in each registration ROI 19 | checkS=zeros(size(x)); 20 | numb=200; 21 | for b=1:numb:size(x) 22 | b2=min([b+numb-1 length(x)]); 23 | ii=getImLocalWindowInd_rf([x(b:b2) y(b:b2)],size(TAmv),m-1,1); 24 | 25 | imcheck=reshape(permute(TAmv(ii),[2 1]),[sz sz size(ii,1)]); 26 | imcheck2=zeros(size(imcheck)); 27 | imcheck2(cc2:end-cc,cc2:end-cc,:)=imcheck(cc2:end-cc,cc2:end-cc,:); 28 | mvS=squeeze(sum(sum(imcheck2,1),2)); % indices of image tiles with tissue in them 29 | 30 | imcheck=reshape(permute(TArf(ii),[2 1]),[sz sz size(ii,1)]); 31 | rfS=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 32 | 33 | checkS(b:b2)=min([mvS rfS],[],2); 34 | end 35 | clearvars ii imcheck imcheck2 36 | checkS=checkS/(sz^2); 37 | 38 | yg=(y-min(y))/di+1; 39 | xg=(x-min(x))/di+1; 40 | xgg0=ones([length(unique(y)) length(unique(x))])*-5000; 41 | ygg0=ones([length(unique(y)) length(unique(x))])*-5000; 42 | 43 | for kk=find(checkS>cutoff)' 44 | % setup small tiles 45 | ii=getImLocalWindowInd_rf([x(kk) y(kk)],size(TAmv),m-1,1);ii(ii==-1)=1; 46 | immvS=immvR(ii);imrfS=imrfR(ii); 47 | immvS=reshape(permute(immvS,[2 1]),[sz sz]); 48 | imrfS=reshape(permute(imrfS,[2 1]),[sz sz]); 49 | 50 | % calculate registration for tiles kk 51 | immvS2=zeros(size(immvS)); 52 | immvS2(cc2:end-cc,cc2:end-cc,:)=immvS(cc2:end-cc,cc2:end-cc,:); 53 | [X,Y,imoutS,RS]=reg_ims_ELS(immvS,imrfS,2,1);a=immvS2(immvS2>0 & imrfS>0); 54 | b=imrfS(immvS2>0 & imrfS>0);R0=corr2(a,b); 55 | %[XS,YS,imoutS,RS]=reg_ims_ELS(immvS,imrfS,2);a=immvS(immvS>0 & imrfS>0);b=imrfS(immvS>0 & imrfS>0);R0=corr2(a,b); 56 | RR=[R0 RS]; 57 | %if max(RR)==RS;X=XS;Y=YS;else;X=-5000;Y=-5000;end 58 | % figure(38), 59 | % subplot(1,2,1),imshowpair(imrfS,immvS),title(R0/checkS(kk)) 60 | % subplot(1,2,2),imshowpair(imrfS,imoutS),title(RS/checkS(kk)) 61 | xgg0(yg(kk),xg(kk))=X; 62 | ygg0(yg(kk),xg(kk))=Y; 63 | end 64 | % smooth registration grid and make interpolated displacement map 65 | [D,xgg,ygg,xx,yy]=make_final_grids(xgg0,ygg0,bf,x,y,szim); 66 | im=imwarp(immvR,D);%figure,imshowpair(im,imrfR) 67 | % figure(17); 68 | % subplot(1,2,1),imshowpair(imrfR,immvR) 69 | % subplot(1,2,2),imshowpair(imrfR,im) 70 | % ha=get(gcf,'children');linkaxes(ha); 71 | end 72 | 73 | 74 | % if max([abs(X) abs(Y)])>40 75 | % imcheck=imtranslate(tmpmv,[-X -Y]); 76 | % a=tmpmv(tmpmv>0 & tmprf>0);b=tmprf(tmpmv>0 & tmprf>0);R0=corr2(a,b); 77 | % a=imcheck(imcheck>0 & tmprf>0);b=tmprf(imcheck>0 & tmprf>0);RR=corr2(a,b); 78 | % if R0>RR 79 | % X=-5000;Y=-5000;disp([R0 RR]); 80 | % end 81 | % end 82 | 83 | 84 | % figure(92), 85 | % subplot(1,2,1),imshowpair(tmprf,tmpmv),title(num2str(C0(yg(kk),xg(kk)))) 86 | % subplot(1,2,2),imshowpair(tmprf,mv20),title(num2str(CR(yg(kk),xg(kk)))) 87 | 88 | 89 | 90 | 91 | % P1=sz+bf+bf; 92 | % checkS=zeros(size(x)); 93 | % checkL=zeros(size(x)); 94 | % numb=200; 95 | % for b=1:numb:size(x) 96 | % b2=min([b+numb-1 length(x)]); 97 | % ii=getImLocalWindowInd_rf([x(b:b2) y(b:b2)],size(TAmv),m-1+bf,1); 98 | % 99 | % imcheck=permute(TAmv(ii),[2 1]); 100 | % imcheck=reshape(imcheck,[P1 P1 size(imcheck,2)]); 101 | % mvS=squeeze(sum(sum(imcheck(bf+1:bf+sz,bf+1:bf+sz,:),1),2)); % indices of image tiles with tissue in them 102 | % mvL=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 103 | % 104 | % imcheck=permute(TArf(ii),[2 1]); 105 | % imcheck=reshape(imcheck,[P1 P1 size(imcheck,2)]); 106 | % rfS=squeeze(sum(sum(imcheck(bf+1:bf+sz,bf+1:bf+sz,:),1),2)); % indices of image tiles with tissue in them 107 | % rfL=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 108 | % 109 | % checkS(b:b2)=min([mvS rfS],[],2); 110 | % checkL(b:b2)=min([mvL rfL],[],2); 111 | % end 112 | % clearvars ii imcheck 113 | % checkS=checkS/(sz^2); 114 | % checkL=checkL/(P1^2); 115 | 116 | -------------------------------------------------------------------------------- /original upload version/base/calculate_elastic_registrationS.m: -------------------------------------------------------------------------------- 1 | function [D,xgg,ygg,xx,yy]=calculate_elastic_registrationS(imrfR,immvR,TArf,TAmv,sz,bf,di) 2 | szim=size(immvR); 3 | m=(sz-1)/2+1; 4 | P1=sz+bf+bf; 5 | immvR=double(padarray(immvR,[bf bf],mode(immvR(:)))); 6 | imrfR=double(padarray(imrfR,[bf bf],mode(imrfR(:)))); 7 | TAmv=padarray(TAmv,[bf bf],0); 8 | TArf=padarray(TArf,[bf bf],0); 9 | 10 | % make grid for registration points 11 | n1=randi(round(di/2),1)+bf+m; 12 | n2=randi(round(di/2),1)+bf+m; 13 | [x,y]=meshgrid(n1:di:size(immvR,2)-m-bf,n2:di:size(immvR,1)-m-bf); 14 | x=x(:);y=y(:); 15 | 16 | % get percentage of tissue in each registration ROI 17 | checkS=zeros(size(x)); 18 | numb=200; 19 | for b=1:numb:size(x) 20 | b2=min([b+numb-1 length(x)]); 21 | ii=getImLocalWindowInd_rf([x(b:b2) y(b:b2)],size(TAmv),m-1,1); 22 | 23 | imcheck=reshape(permute(TAmv(ii),[2 1]),[sz sz size(ii,1)]); 24 | %imcheck2=zeros(size(imcheck));imcheck2(36:end-35,36:end-35,:)=imcheck(36:end-35,36:end-35,:);mvS2=squeeze(sum(sum(imcheck2,1),2)); 25 | mvS=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 26 | 27 | imcheck=reshape(permute(TArf(ii),[2 1]),[sz sz size(ii,1)]); 28 | rfS=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 29 | 30 | checkS(b:b2)=min([mvS rfS],[],2); 31 | end 32 | clearvars ii imcheck imcheck2 33 | checkS=checkS/(sz^2); 34 | 35 | yg=(y-min(y))/di+1; 36 | xg=(x-min(x))/di+1; 37 | xgg0=ones([length(unique(y)) length(unique(x))])*-5000; 38 | ygg0=ones([length(unique(y)) length(unique(x))])*-5000; 39 | cutoff=0.4; % register if image tiles are >=15% tissue 40 | for kk=find(checkS>cutoff)' 41 | % setup small tiles 42 | ii=getImLocalWindowInd_rf([x(kk) y(kk)],size(TAmv),m-1,1);ii(ii==-1)=1; 43 | immvS=immvR(ii);imrfS=imrfR(ii); 44 | immvS=reshape(permute(immvS,[2 1]),[sz sz size(immvS,1)]); 45 | imrfS=reshape(permute(imrfS,[2 1]),[sz sz size(imrfS,1)]); 46 | 47 | % calculate registration for tiles kk 48 | %immvS2=zeros(size(immvS));immvS2(36:end-35,36:end-35,:)=immvS(36:end-35,36:end-35,:);[XS,YS,imoutS,RS]=reg_ims_ELS(immvS2,imrfS,2);a=immvS2(immvS2>0 & imrfS>0);b=imrfS(immvS2>0 & imrfS>0);R0=corr2(a,b); 49 | [XS,YS,imoutS,RS]=reg_ims_ELS(immvS,imrfS,2); 50 | a=immvS(immvS>0 & imrfS>0);b=imrfS(immvS>0 & imrfS>0);R0=corr2(a,b); 51 | RR=[R0 RS]; 52 | if max(RR)==RS;X=XS;Y=YS;else;X=-5000;Y=-5000;end 53 | % figure(38), 54 | % subplot(1,2,1),imshowpair(imrfS,immvS),title(R0/checkS(kk)) 55 | % subplot(1,2,2),imshowpair(imrfS,imoutS),title(RS/checkS(kk)) 56 | xgg0(yg(kk),xg(kk))=X; 57 | ygg0(yg(kk),xg(kk))=Y; 58 | end 59 | % smooth registration grid and make interpolated displacement map 60 | [D,xgg,ygg,xx,yy]=make_final_grids(xgg0,ygg0,bf,x,y,szim); 61 | im=imwarp(immvR,D);%figure,imshowpair(im,imrfR) 62 | figure(17); 63 | subplot(1,2,1),imshowpair(imrfR,immvR) 64 | subplot(1,2,2),imshowpair(imrfR,im) 65 | ha=get(gcf,'children');linkaxes(ha); 66 | end 67 | 68 | 69 | % if max([abs(X) abs(Y)])>40 70 | % imcheck=imtranslate(tmpmv,[-X -Y]); 71 | % a=tmpmv(tmpmv>0 & tmprf>0);b=tmprf(tmpmv>0 & tmprf>0);R0=corr2(a,b); 72 | % a=imcheck(imcheck>0 & tmprf>0);b=tmprf(imcheck>0 & tmprf>0);RR=corr2(a,b); 73 | % if R0>RR 74 | % X=-5000;Y=-5000;disp([R0 RR]); 75 | % end 76 | % end 77 | 78 | 79 | % figure(92), 80 | % subplot(1,2,1),imshowpair(tmprf,tmpmv),title(num2str(C0(yg(kk),xg(kk)))) 81 | % subplot(1,2,2),imshowpair(tmprf,mv20),title(num2str(CR(yg(kk),xg(kk)))) 82 | 83 | 84 | 85 | 86 | % P1=sz+bf+bf; 87 | % checkS=zeros(size(x)); 88 | % checkL=zeros(size(x)); 89 | % numb=200; 90 | % for b=1:numb:size(x) 91 | % b2=min([b+numb-1 length(x)]); 92 | % ii=getImLocalWindowInd_rf([x(b:b2) y(b:b2)],size(TAmv),m-1+bf,1); 93 | % 94 | % imcheck=permute(TAmv(ii),[2 1]); 95 | % imcheck=reshape(imcheck,[P1 P1 size(imcheck,2)]); 96 | % mvS=squeeze(sum(sum(imcheck(bf+1:bf+sz,bf+1:bf+sz,:),1),2)); % indices of image tiles with tissue in them 97 | % mvL=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 98 | % 99 | % imcheck=permute(TArf(ii),[2 1]); 100 | % imcheck=reshape(imcheck,[P1 P1 size(imcheck,2)]); 101 | % rfS=squeeze(sum(sum(imcheck(bf+1:bf+sz,bf+1:bf+sz,:),1),2)); % indices of image tiles with tissue in them 102 | % rfL=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 103 | % 104 | % checkS(b:b2)=min([mvS rfS],[],2); 105 | % checkL(b:b2)=min([mvL rfL],[],2); 106 | % end 107 | % clearvars ii imcheck 108 | % checkS=checkS/(sz^2); 109 | % checkL=checkL/(P1^2); 110 | 111 | -------------------------------------------------------------------------------- /original upload version/base/calculate_global_reg.m: -------------------------------------------------------------------------------- 1 | function [imout,tform,cent,f,Rout]=calculate_global_reg(imrf,immv,rf,iternum,IHC,bb) 2 | if ~exist('bb','var');bb=0.9;end 3 | % imrf == reference image 4 | % immv == moving image 5 | % szz == max size of images in stack 6 | % rf == reduce images by _ times 7 | % count == number of iterations of registration code 8 | % pre-registration image processing 9 | amv=imresize(immv,1/rf);amv=imgaussfilt(amv,2); 10 | arf=imresize(imrf,1/rf);arf=imgaussfilt(arf,2); 11 | sz=[0 0];cent=[0 0]; 12 | if IHC>0;amv=imadjust(amv);arf=imadjust(arf);end 13 | 14 | % calculate registration, flipping image if necessary 15 | iternum0=2; 16 | [R,rs,xy,amv1out]=group_of_reg(amv,arf,iternum0,sz,rf,bb); 17 | %figure(9),imshowpair(amv1out,arf) 18 | f=0; 19 | % figure(12),imshowpair(arf,amv1out),title(num2str(round(R*100))) 20 | if R<0.8 21 | disp('try flipping image') 22 | amv2=amv(end:-1:1,:,:); 23 | [R2,rs2,xy2,amv2out]=group_of_reg(amv2,arf,iternum0,sz,rf,bb); 24 | if R2>R;rs=rs2;xy=xy2;f=1;amv=amv2;end 25 | % figure(13),imshowpair(arf,amv2out),title(num2str(round(R2*100))) 26 | end 27 | [tform,amvout,~,~,Rout]=reg_ims_com(amv,arf,iternum-iternum0,sz,rf,rs,xy,0); 28 | aa=double(arf>0)+double(amvout>0); 29 | Rout=sum(aa(:)==2)/sum(aa(:)>0); 30 | %figure(9),imshowpair(amvout,arf) 31 | 32 | % create output image 33 | Rin=imref2d(size(immv)); 34 | if sum(abs(cent))==0 35 | mx=mean(Rin.XWorldLimits); 36 | my=mean(Rin.YWorldLimits); 37 | cent=[mx my]; 38 | end 39 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 40 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 41 | 42 | if f==1 43 | immv=immv(end:-1:1,:,:); 44 | end 45 | 46 | % register 47 | imout=imwarp(immv,Rin,tform,'nearest','Outputview',Rin,'Fillvalues',0); 48 | %figure,imshowpair(arf,amvout) 49 | end 50 | 51 | function [R,rs,xy,amv]=group_of_reg(amv0,arf,iternum0,sz,rf,bb) 52 | if ~exist('bb','var');bb=0.9;end 53 | T=[-2 177 87 268 -1 88 269 178 -7:2:7 179:183 89:93 270:272]; 54 | R=0.2; 55 | rs=0; 56 | xy=0; 57 | aa=arf==0;ab=amv0==0; 58 | arf=double(arf);amv0=double(amv0); 59 | arf=(arf-mean(arf(:)))/std(amv0(:)); 60 | amv0=(amv0-mean(amv0(:)))/std(amv0(:)); 61 | arf=arf-min(arf(:));arf(aa==1)=0; 62 | amv0=amv0-min(amv0(:));amv(ab==1)=0; 63 | amv=amv0; 64 | RR0=sum(amv(:)>0) + sum(arf(:)>0); 65 | for kp=1:length(T) 66 | try 67 | [~,amv1,rs1,xy1,RR]=reg_ims_com(amv0,arf,iternum0,sz,rf,T(kp),[0; 0],1); 68 | if RR~=0;aa=double(arf>0)+double(amv1>0);RR=sum(aa(:)==2)/sum(aa(:)>0);end 69 | catch 70 | disp('catch') 71 | RR=0; 72 | end 73 | 74 | %figure(8),imshowpair(arf,amv1),title(num2str(RR)) 75 | if RR>R;R=RR;rs=rs1;xy=xy1;amv=amv1;end 76 | if R>bb && kp>16;break;end 77 | %disp([RR R bb]) 78 | end 79 | end -------------------------------------------------------------------------------- /original upload version/base/calculate_tissue_space_082.m: -------------------------------------------------------------------------------- 1 | function [im0,TA,outpth]=calculate_tissue_space_082(pth,imnm) 2 | % creates logical image with tissue area 3 | 4 | outpth=[pth,'TA\']; 5 | if ~isfolder(outpth);mkdir(outpth);end 6 | 7 | try im0=imread([pth,imnm,'.tif']); 8 | catch 9 | try im0=imread([pth,imnm,'.jp2']); 10 | catch 11 | im0=imread([pth,imnm,'.jpg']); 12 | end 13 | end 14 | if exist([outpth,imnm,'.tif'],'file') 15 | TA=imread([outpth,imnm,'.tif']); 16 | return; 17 | end 18 | 19 | im=double(imgaussfilt(im0,1)); 20 | TA=std(im,[],3); 21 | TA=TA>4; 22 | TA=bwareaopen(TA,9); 23 | figure(12),imshowpair(im0,TA);axis equal;axis off 24 | imwrite(uint8(TA),[outpth,imnm,'.tif']); 25 | 26 | -------------------------------------------------------------------------------- /original upload version/base/calculate_transform.m: -------------------------------------------------------------------------------- 1 | function res=calculate_transform(im,imnxt,xy,p) 2 | % estimate the dislocation of images between frames. 3 | % using PIV. 4 | % 5 | % input: im : current image (required) 6 | % imnxt: image at next frame,(required) 7 | % p : parameters settings 8 | % 9 | % developed by Pei-Hsun Wu, Ph.D 10 | % @ johns Hopkins University, INBT, 0/20/2014; 11 | [imly,imlx]=size(im); 12 | [imly2,imlx2]=size(imnxt); 13 | imly=min(imly,imly2); 14 | imlx=min(imlx,imlx2); 15 | if nargin<3 || isempty(xy) 16 | xy=floor([(imly-1)/2, (imlx-1)/2])+1; % using center point of a image 17 | end 18 | 19 | if nargin<4 || isempty(p) 20 | p.rm=floor([imly-1,imlx-1]/2); 21 | p.rm=round(p.rm*0.95); % for excluding edge effect % added 6/2/2016 22 | p.rs=floor(p.rm); 23 | p.tm=3; 24 | p.rg=max([imly,imlx]); % search range 25 | end 26 | 27 | % [imly,imlx]=size(im); 28 | % for kk= p.imoi(2:end) 29 | 30 | x0=xy(2); % location of image pattern 31 | y0=xy(1); 32 | 33 | imptn=im((y0-p.rm(1):y0+p.rm(1)),(x0-p.rm(2):x0+p.rm(2))) ; 34 | imgrid=imnxt((y0-p.rs(1):y0+p.rs(1)),(x0-p.rs(2):x0+p.rs(2))) ; 35 | 36 | % intenitsy normalization 37 | % this might help to take off scale effect; 38 | % this help a lot. especially for fft based transformation 39 | imptn=(imptn-mean(imptn))/std(imptn(:)); 40 | imgrid=(imgrid-mean(imgrid))/std(imgrid(:)); 41 | 42 | % decide pattern recognization method 43 | % based on tm, =1 cross-correlation, 2:LSM 44 | switch(p.tm) 45 | case(1); y1=xcorr2(imgrid,imptn); 46 | case(2); y1=patrecog(imgrid,imptn); 47 | case(3); y1=xcorrf2(imptn,imgrid); 48 | case(4); y1=xcor2d_nmrd(imgrid,imptn); 49 | case(5); y1=normxcorr2(imptn,imgrid); y1=y1(end:-1:1,end:-1:1)*100; % rescale to larger htan 1 50 | end 51 | 52 | % estimate maximimum liklihood of the object location at next frame.` 53 | 54 | 55 | 56 | % offx= x0 - p.rs- p.rm -1 ; % this is a integer 57 | % offy= y0 - p.rs- p.rm -1 ; 58 | % 59 | msk=mskcircle2_rect(size(y1),p.rg); 60 | % y1g=bpassW(y1,7,21); 61 | % y1g=y1g.*msk; 62 | % [myg,mxg]=find(y1g==max(y1g(:))); 63 | % msk=zeros(size(y1g)); 64 | % msk(myg-20:myg+20,mxg-20:mxg+20)=1; 65 | y1m=y1.*msk; 66 | % y1m=y1; 67 | [my,mx]=find(y1m==max(y1m(:))); % pixel resolution; 68 | % [cnt]=cntrd(y1,[mx,my],5); 69 | [cnt]=gcnt(y1,[mx(1),my(1)],3,0,0); 70 | yx=cnt([2 1]); 71 | res=yx-p.rs-p.rm-1; 72 | res=res([2 1]); % xy 73 | 74 | % xhd=cnt(1)+offx; 75 | % yhd=cnt(2)+offy; 76 | % 77 | % % update 78 | % xnew=(xhd) ; 79 | % ynew=(yhd) ; 80 | % 81 | % dx=xnew-x0; 82 | % dy=ynew-y0; 83 | % res=[dx dy]; 84 | 85 | 86 | % cof=xcorrf2(pij1,pij2); 87 | % [ll,ww]=size(imptn); 88 | % xcc=round((ww+1)/2); 89 | % ycc=round((ll+1)/2); 90 | % % 91 | % dxx=ww-xcc +1 ; 92 | % dyy=ll-ycc +1 ; 93 | % % 94 | % cof=y1(dyy:end-dyy,dxx:end-dxx); 95 | % % 96 | % pkc=pkfndW(cof,max(cof(:))*0.999,21); 97 | % % [col,row]=find(cof==max(cof(:))); 98 | % % pkc=[row,col]; 99 | % cntcc=gcnt(cof,pkc,5,0,0); % this should give the shit inbetween frame 100 | % % 101 | % res=[cntcc(1:2)-[xcc,ycc]]; 102 | 103 | 104 | % estimate the local signal strength in imptn and update pattern 105 | % location; 106 | 107 | 108 | 109 | % result register 110 | 111 | 112 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /original upload version/base/combine_tiles_big.m: -------------------------------------------------------------------------------- 1 | function numann=combine_tiles_big(numann0,numann,imlist,nblack,pthDL,outpth,sxy,EE,stile,nbg) 2 | if ~exist('EE','var');EE=0;end 3 | if ~exist('nbg','var');nbg=0;end 4 | if ~exist('stile','var');stile=10000;end 5 | cc=300; 6 | stile=stile+cc*2; 7 | 8 | % define folder locations 9 | outpthim=[pthDL,outpth,'im\']; 10 | outpthlabel=[pthDL,outpth,'label\']; 11 | outpthbg=[pthDL,outpth,'big_tiles\']; 12 | mkdir(outpthim);mkdir(outpthlabel);mkdir(outpthbg); 13 | imlistck=dir([outpthim,'*tif']); 14 | nm0=length(imlistck)+1; 15 | 16 | % create very large blank images 17 | imH=ones([stile stile 3])*nbg; 18 | imT=zeros([stile stile]); 19 | imT2=imT(cc+1:end-cc,cc+1:end-cc,:); 20 | nL=numel(imT2);clearvars imT2 21 | ct=zeros([1 size(numann,2)]); 22 | sf=sum(ct)/nL; 23 | 24 | count=1; 25 | cutoff=0.6; 26 | tcount=1; 27 | tps=find(sum(numann0)==0); 28 | minn=50;maxx=500000; 29 | while sfminn & numann(:,type)minn & numann(:,type)cutoff 85 | ct=histcounts(imT(:),0:size(numann,2)+1); 86 | ct=ct(2:end);ct(tps)=Inf; 87 | disp([round(sf*100) ct/min(ct)]) 88 | else 89 | ct2=histcounts(tmpT(:),0:size(numann,2)+1);ct=ct+ct2(2:end); 90 | end 91 | ct2=ct;ct2(ct2==Inf)=0;sf=sum(ct2)/nL; 92 | 93 | count=count+1; 94 | end 95 | 96 | % cut edges off tile 97 | imH=uint8(imH(cc+1:end-cc,cc+1:end-cc,:)); 98 | imT=uint8(imT(cc+1:end-cc,cc+1:end-cc,:)); 99 | imT(imT==0)=nblack; 100 | 101 | % save cutouts to outpth 102 | sz=size(imH); 103 | for s1=1:sxy:sz(1) 104 | for s2=1:sxy:sz(2) 105 | try 106 | imHtmp=imH(s1:s1+sxy-1,s2:s2+sxy-1,:); 107 | imTtmp=imT(s1:s1+sxy-1,s2:s2+sxy-1,:); 108 | catch 109 | continue 110 | end 111 | imwrite(imHtmp,[outpthim,num2str(nm0),'.tif']); 112 | imwrite(imTtmp,[outpthlabel,num2str(nm0),'.tif']); 113 | 114 | nm0=nm0+1; 115 | end 116 | end 117 | 118 | % save large tiles 119 | nm1=dir([outpthbg,'*tif']);nm1=length(nm1)/2+1; 120 | imwrite(imH,[outpthbg,'HE_tile_',num2str(nm1),'.tif']); 121 | imwrite(imT,[outpthbg,'label_tile_',num2str(nm1),'.tif']); 122 | 123 | end 124 | 125 | function [im,TA,kpout]=edit_annotation_tiles(im,TA,doaug,type,ct,sT,kpall) 126 | % makes sure annotation distribution doesn't vary by more than 1% 127 | if doaug 128 | [im,TA]=random_augmentation(im,TA,1,1,1,1,0); 129 | else 130 | [im,TA]=random_augmentation(im,TA,1,1,0,0,0); 131 | end 132 | 133 | if kpall==0 134 | maxn=ct(type); 135 | kp=ct<=maxn*1.05; 136 | else 137 | kp=ct>0; 138 | end 139 | %if type==4;kp(6)=1;end 140 | kp=[0 kp]; 141 | tmp=kp(TA+1); 142 | 143 | dil=randi(15)+15; 144 | tmp=imdilate(tmp,strel('disk',dil)); 145 | TA=TA.*double(tmp); 146 | im=im.*double(tmp); 147 | kpout=unique(TA);kpout=kpout(2:end); 148 | 149 | p1=min([sT size(TA,1)]); 150 | p2=min([sT size(TA,2)]); 151 | im=im(1:p1,1:p2,:);TA=TA(1:p1,1:p2); 152 | im=uint8(im); 153 | TA=uint8(TA); 154 | end 155 | -------------------------------------------------------------------------------- /original upload version/base/deeplab_classification.m: -------------------------------------------------------------------------------- 1 | function deeplab_classification(pth5x,pthDL,sxy,nm,cmap,nblack,nwhite,col,outpth) 2 | if ~exist('col','var');col=0;end 3 | 4 | 5 | load([pthDL,'net.mat'],'net'); 6 | if exist('outpth','var') 7 | outpth=[outpth,'classification_',nm,'\']; 8 | else 9 | outpth=[pth5x,'classification_',nm,'\']; 10 | end 11 | 12 | mkdir(outpth); 13 | 14 | b=100; 15 | imlist=dir([pth5x,'*tif']); 16 | if isempty(imlist);imlist=dir([pth5x,'*jp2']);end 17 | if isempty(imlist);imlist=dir([pth5x,'*jpg']);end 18 | 19 | x=tic; 20 | 21 | for kk=1:length(imlist) 22 | tic; 23 | if exist([outpth,imlist(kk).name(1:end-3),'tif'],'file');continue;end 24 | disp(imlist(kk).name) 25 | 26 | im=imread([pth5x,imlist(kk).name]); 27 | try 28 | TA=imread([pth5x,'TA\',imlist(kk).name(1:end-3),'tif']); 29 | catch 30 | TA=rgb2gray(im)<220; 31 | imfill(TA,'holes'); 32 | end 33 | 34 | % pad image so we classify all the way to the edge 35 | im=padarray(im,[sxy+b sxy+b],0,'both'); 36 | TA=padarray(TA,[sxy+b sxy+b],1,'both'); 37 | 38 | imclassify=zeros(size(TA)); 39 | sz=size(im); 40 | for s1=1:sxy-b*2:sz(1)-sxy 41 | for s2=1:sxy-b*2:sz(2)-sxy 42 | tileHE=im(s1:s1+sxy-1,s2:s2+sxy-1,:); 43 | tileTA=TA(s1:s1+sxy-1,s2:s2+sxy-1,:); 44 | if sum(tileTA(:))<100 45 | tileclassify=zeros(size(tileTA)); 46 | else 47 | tileclassify=semanticseg(tileHE,net); 48 | end 49 | tileclassify=tileclassify(b+1:end-b,b+1:end-b,:); 50 | imclassify(s1+b:s1+sxy-b-1,s2+b:s2+sxy-b-1)=tileclassify; 51 | 52 | % tileclassify=double(tileclassify); 53 | % tileclassify(tileclassify==nblack | tileclassify==0)=nwhite; 54 | % imcolor=uint8(cat(3,am(tileclassify),bm(tileclassify),cm(tileclassify))); 55 | % tileHE=tileHE(b+1:end-b,b+1:end-b,:); 56 | % figure(18), 57 | % subplot(1,2,1),imshow(tileHE) 58 | % subplot(1,2,2),imshow(imcolor) 59 | end 60 | end 61 | 62 | % remove padding 63 | im=im(sxy+b+1:end-sxy-b,sxy+b+1:end-sxy-b,:); 64 | imclassify=imclassify(sxy+b+1:end-sxy-b,sxy+b+1:end-sxy-b,:); 65 | 66 | disp([kk length(imlist) round(toc)]) 67 | imclassify(imclassify==nblack | imclassify==0)=nwhite; % make black class and zeros class whitespace 68 | imwrite(uint8(imclassify),[outpth,imlist(kk).name(1:end-3),'tif']); 69 | 70 | if col==1 71 | outpthcolor=[outpth,'color\']; 72 | if ~isfolder(outpthcolor);mkdir(outpthcolor);end 73 | am=cmap(:,1);bm=cmap(:,2);cm=cmap(:,3); 74 | imcolor=uint8(cat(3,am(imclassify),bm(imclassify),cm(imclassify))); 75 | imwrite(imcolor,[outpthcolor,imlist(kk).name(1:end-3),'tif']); 76 | end 77 | % figure,imshow(imcolor); 78 | % figure(18), 79 | % subplot(1,2,1),imshow(uint8(im)) 80 | % subplot(1,2,2),imshow(imcolor) 81 | 82 | end 83 | disp(['total time: ',num2str(round(toc(x)/60)),' minutes']) -------------------------------------------------------------------------------- /original upload version/base/detect_scales.m: -------------------------------------------------------------------------------- 1 | % pth='G:\Alicia Braxton\TC_90 PDAC\1x\'; 2 | pth='G:\Alicia Braxton\TC_90 PDAC\1x\registered_bad_scale\'; 3 | pthTA='G:\Alicia Braxton\TC_90 PDAC\1x\registered_bad_scale\TA\'; 4 | imlist=dir([pth,'*jpg']); 5 | imlistTA=dir([pthTA,'*tif']); 6 | li=length(imlist); 7 | 8 | ta=zeros([1 li]); 9 | for kk=1:li 10 | im=imread([pth,imlist(kk).name]); 11 | TA=find_tissue_area(im); 12 | ta(kk)=sum(TA(:)); 13 | if kk==1;ta0=ta(kk);end 14 | fprintf('image %0.0f of %0.0f. TA=%0.0f. change=%0.2f\n',... 15 | kk,li,ta(kk),ta(kk)/ta0); 16 | ta0=ta(kk); 17 | 18 | imwrite(TA,[pth,'TA\',imlist(kk).name]); 19 | 20 | 21 | end 22 | -------------------------------------------------------------------------------- /original upload version/base/edit_individual_classes_082.m: -------------------------------------------------------------------------------- 1 | function im=edit_individual_classes_082(im0,imnm) 2 | im=im0; 3 | 4 | % close fat area 5 | tmp0=im==4; 6 | im(im==4)=0; 7 | tmpk=im~=4 & im~=0; 8 | tmp=imclose(tmp0,strel('disk',3)); 9 | tmp2=tmp-tmp0; 10 | tmp=imfill(tmp,'holes'); 11 | tmp=imdilate(tmp,strel('disk',1)); 12 | im(tmp==1)=4; 13 | im(tmp2==1)=6; 14 | im(tmpk==1)=im0(tmpk==1); 15 | clearvars -except im0 im imnm 16 | 17 | % close blood vessel area 18 | % tmp=im==3; 19 | % im(im==3)=0; 20 | % tmp=imclose(tmp,strel('disk',1)); 21 | % tmp=bwareaopen(tmp,50); % remove small 22 | % im(tmp==1)=3; 23 | % clearvars -except im0 im imnm 24 | % 25 | % close duct area 26 | % tmp0=im==2; 27 | % im(im==2)=0; 28 | % tmp=imfill(tmp0,'holes')-tmp0; % find holes 29 | % tmp=tmp-bwareaopen(tmp,30); % keep only small holes 30 | % tmp=bwareaopen(tmp | tmp0,75); % remove small 31 | % im(tmp==1)=2; 32 | % clearvars -except im0 im imnm 33 | 34 | % close PanIN area 35 | % tmp0=im==8; 36 | % im(im==8)=0; 37 | % tmp=imfill(tmp0,'holes')-tmp0; % find holes 38 | % tmp=tmp-bwareaopen(tmp,30); % keep only small holes 39 | % tmp=bwareaopen(tmp | tmp0,75); % remove small 40 | % im(tmp==1)=8; 41 | % clearvars -except im0 im imnm 42 | 43 | % remove small white 44 | tmp=im==7; 45 | im(tmp==1)=0; 46 | tmp=bwareaopen(tmp,300); 47 | im(tmp==1)=7; 48 | clearvars -except im0 im imnm 49 | 50 | imwrite(uint8(im),imnm); 51 | end 52 | -------------------------------------------------------------------------------- /original upload version/base/fill_annotations_file.m: -------------------------------------------------------------------------------- 1 | function J=fill_annotations_file(I,outpth,WS,umpix,TA,kpb) 2 | if ~exist('kpb','var');kpb=0;end 3 | if umpix==100;umpix=1;elseif umpix==200;umpix=2;end 4 | disp(' 2. of 3. interpolating and filling annotated regions') 5 | % indices=[layer# annotation# x y] 6 | num=length(WS{1}); 7 | 8 | load([outpth,'annotations.mat'],'xyout'); 9 | if ~isempty(xyout) 10 | xyout(:,3:4)=round(xyout(:,3:4)/umpix); % indices are already at desired resolution 11 | 12 | % find areas of image containing tissue 13 | if ~isempty(TA) 14 | TA=TA>0; % remove small background 15 | TA=bwareaopen(TA,300); 16 | TA=imclose(TA,strel('disk',1)); 17 | TA=~TA; 18 | else 19 | I=double(I); 20 | % low standard deviation 21 | TA1=std(I(:,:,[1 2]),[],3); 22 | TA2=std(I(:,:,[1 3]),[],3); 23 | TA=max(cat(3,TA1,TA2),[],3); 24 | TAa=TA<10; 25 | % high green 26 | TAb=I(:,:,2)>210; 27 | TA=TAa & TAb; 28 | TA=bwareaopen(TA,5); 29 | 30 | end 31 | Ig=find(TA); 32 | szz=size(TA); 33 | J=cell([1 num]); 34 | % interpolate annotation points to make closed objects 35 | for k=unique(xyout(:,1))' % for each annotation type k 36 | Jtmp=zeros(szz); 37 | bwtypek=Jtmp; 38 | cc=xyout(:,1)==k; 39 | xyz=xyout(cc,:); 40 | for pp=unique(xyz(:,2))' % for each individual annotation 41 | if pp==0 42 | continue 43 | end 44 | cc=find(xyz(:,2)==pp); 45 | 46 | xyv=[xyz(cc,3:4); xyz(cc(1),3:4)]; 47 | dxyv=sqrt(sum((xyv(2:end,:)-xyv(1:end-1,:)).^2,2)); 48 | 49 | xyv(dxyv==0,:)=[]; % remove the repeating points 50 | dxyv(dxyv==0)=[]; 51 | dxyv=[0;dxyv]; 52 | 53 | ssd=cumsum(dxyv); 54 | ss0=1:0.49:ceil(max(ssd)); % increase by <0.5 to avoid rounding gaps 55 | xnew=interp1(ssd,xyv(:,1),ss0); 56 | ynew=interp1(ssd,xyv(:,2),ss0); 57 | xnew=round(xnew); 58 | ynew=round(ynew); 59 | try 60 | indnew=sub2ind(szz,ynew,xnew); 61 | catch 62 | disp('annotation out of bounds'); 63 | continue 64 | end 65 | indnew(isnan(indnew))=[]; 66 | bwtypek(indnew)=1; 67 | end 68 | bwtypek=imfill(bwtypek>0,'holes'); 69 | Jtmp(bwtypek==1)=k; 70 | if ~kpb;Jtmp(1:401,:)=0;Jtmp(:,1:401)=0;Jtmp(end-401:end,:)=0;Jtmp(:,end-401:end)=0;end 71 | J{k}=find(Jtmp==k); 72 | end 73 | clearvars bwtypek Jtmp xyout xyz 74 | 75 | % format annotations to keep or remove whitespace 76 | J=format_white(J,Ig,WS,szz); 77 | figure(93),imshowpair(J,I); 78 | imwrite(uint8(J),[outpth,'view_annotations_raw.tif']); 79 | else 80 | J=zeros(size(I(:,:,1))); 81 | end 82 | 83 | 84 | end 85 | 86 | function [J,ind]=format_white(J0,Ig,WS,szz) 87 | p=1; % image number I think 88 | ws=WS{1}; % defines keep or delete whitespace 89 | wsa=WS{2}; % defines non-tissue label 90 | wsnew=WS{3}; % redefines CNN label names 91 | wsorder=WS{4}; % gives order of annotations 92 | wsdelete=WS{5}; % lists annotations to delete 93 | 94 | Jws=zeros(szz); 95 | ind=[]; 96 | % remove white pixels from annotations areas 97 | for k=wsorder 98 | if intersect(wsdelete,k)>0;continue;end % delete unwanted annotation layers 99 | try ii=J0{k};catch;continue;end 100 | iiNW=setdiff(ii,Ig); % indices that are not white 101 | iiW=intersect(ii,Ig); % indices that are white 102 | if ws(k)==0 % remove whitespace and add to wsa 103 | Jws(iiNW)=k; 104 | Jws(iiW)=wsa; 105 | elseif ws(k)==1 % keep only whitespace 106 | Jws(iiW)=k; 107 | elseif ws(k)==2 % keep both whitespace and non whitespace 108 | Jws(iiNW)=k; 109 | Jws(iiW)=k; 110 | end 111 | end 112 | 113 | % remove small objects and redefine labels (combine labels if desired) 114 | J=zeros(szz); 115 | for k=1:max(Jws(:)) 116 | if wsnew(k)==wsa;dll=5;else;dll=5;end 117 | tmp=bwareaopen(Jws==k,dll); 118 | ii=find(tmp==1); 119 | J(tmp)=wsnew(k); 120 | P=[ones([length(ii) 2]).*[p wsnew(k)] ii]; 121 | ind=cat(1,ind,P); 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /original upload version/base/find_tissue_area.m: -------------------------------------------------------------------------------- 1 | function [TA,fillval]=find_tissue_area(im0,nm) 2 | if ~exist('nm','var');nm='';end 3 | %fillval=squeeze(mode(mode(im0,2),1))'; 4 | %im=double(im0); 5 | %if max(fillval)==255 6 | im=double(im0); 7 | img=rgb2gray(im0); 8 | img=img==255 | img==0; 9 | im(cat(3,img,img,img))=NaN; 10 | fillval=squeeze(mode(mode(im,2),1))'; 11 | ima=im(:,:,1);imb=im(:,:,2);imc=im(:,:,3); 12 | ima(img)=fillval(1);imb(img)=fillval(2);imc(img)=fillval(3); 13 | im=cat(3,ima,imb,imc); 14 | %end 15 | if contains(nm,'CD45') 16 | TA=im-permute(fillval,[1 3 2]); 17 | TA=mean(abs(TA),3)>2; 18 | elseif contains(nm,'CD3') 19 | TA=mean(im,3)<210;%figure(10),imagesc(TA) 20 | else 21 | % remove objects with very small standard deviations 22 | TA=im-permute(fillval,[1 3 2]); 23 | TA=mean(abs(TA),3)>10; 24 | if size(im0,3)==3 25 | black_line=imclose(std(im,[],3)<5 & rgb2gray(im0)<160,strel('disk',2)); 26 | TA=TA & ~black_line; 27 | end 28 | %TA=im(:,:,1)<210; 29 | end 30 | %TA=imdilate(TA,strel('disk',4)); 31 | TA=imclose(TA,strel('disk',2)); 32 | TA=bwlabel(TA); 33 | 34 | % remove objects that are less than 1/10 largest object size 35 | N=histcounts(TA(:),max(TA(:))+1); 36 | N(1)=0; 37 | N(N<(max(N)/20))=0; 38 | N(N>0)=1; 39 | TA=N(TA+1); 40 | 41 | %TA=imfill(TA,'holes'); 42 | D=(sum(TA(:))/numel(TA)); 43 | if D<0.05 44 | TA=fix_TA(im,fillval); 45 | end 46 | %figure(8),imagesc(TA) 47 | %TA=rgb2gray(im0)<255; 48 | end 49 | 50 | function TA=fix_TA(im,fillval) 51 | TA=im-permute(fillval,[1 3 2]); 52 | TA=mean(TA,3)<-20; 53 | TA=imdilate(TA,strel('disk',4)); 54 | TA=imclose(TA,strel('disk',6)); 55 | TA=bwlabel(TA); 56 | 57 | % remove objects that are less than 1/10 largest object size 58 | N=histcounts(TA(:),max(TA(:))+1); 59 | N(1)=0; 60 | N(N<(max(N)/10))=0; 61 | N(N>0)=1; 62 | TA=N(TA+1); 63 | 64 | %TA=imfill(TA,'holes'); 65 | end -------------------------------------------------------------------------------- /original upload version/base/gcnt.m: -------------------------------------------------------------------------------- 1 | function [out,outid]=gcnt(im,mx,sz,th,rsq) 2 | %cntrd: calculates the centroid of bright spots to sub-pixel accuracy. 3 | % Inspired by Grier & Crocker's feature for IDL, but greatly simplified and optimized 4 | % for matlab 5 | % INPUTS: 6 | % im: image to process, particle should be bright spots on dark background with little noise 7 | % ofen an bandpass filtered brightfield image or a nice fluorescent image 8 | % 9 | % mx: locations of local maxima to pixel-level accuracy from pkfnd.m 10 | % 11 | % sz: diamter of the window over which to average to calculate the centroid. 12 | % should be big enough 13 | % to capture the whole particle but not so big that it captures others. 14 | % if initial guess of center (from pkfnd) is far from the centroid, the 15 | % window will need to be larger than the particle size. RECCOMMENDED 16 | % size is the long lengthscale used in bpass plus 2. 17 | % 18 | % 19 | % 20 | %OUTPUT: a N x 3 array containing, x, y and brightness for each feature 21 | % out(:,1) is the x-coordinates 22 | % out(:,2) is the y-coordinates 23 | % out(:,3) is the Peak intensity 24 | % out(:,4) is the intensity radius 25 | % update (12/16/2010) : eliminate the complex output... 26 | 27 | if nargin<4; 28 | th=0; 29 | rsq=0; 30 | elseif nargin <5; 31 | rsq=0; 32 | end 33 | 34 | [nr,nc]=size(im); 35 | %remove all potential locations within distance sz from edges of image 36 | if 1 % remove edge object 37 | ind=find(mx(:,2) > 1.5*sz/2 & mx(:,2) < nr-1.5*sz/2); 38 | mx=mx(ind,:); 39 | ind=find(mx(:,1) > 1.5*sz/2 & mx(:,1) < nc-1.5*sz/2); 40 | mx=mx(ind,:); 41 | end 42 | [nmx,crap] = size(mx); %nmx is number of particles 43 | 44 | 45 | %inside of the window, assign an x and y coordinate for each pixel 46 | 47 | dimm=sz; 48 | x1=zeros(dimm,dimm); 49 | for i=1:dimm 50 | x1(i,:)=1:dimm; 51 | end 52 | x1=x1-(dimm+1)/2; 53 | y1=x1'; 54 | kk=(dimm-1)/2; 55 | pts=[]; 56 | %%%%%%%% generate the diamend mask matrix ( only valid below sz<=5 ) 57 | % msk=mskdiamond(sz); 58 | % msk=mskcircle(sz); 59 | msk=ones(sz); 60 | %%%%%%%% 61 | 62 | %msk=ones(dimm,dimm); 63 | %msk(1,1)=0;msk(dimm,1)=0; msk(dimm,dimm)=0; msk(1,dimm)=0; 64 | AAt=[x1(:).^2+y1(:).^2,x1(:),y1(:),ones(size(x1(:)))]; 65 | %loop through all of the candidate positions 66 | for i=1 :nmx 67 | 68 | %create a small working array around each candidate location, and apply the window function 69 | tmp = msk.*im((mx(i,2)-kk:mx(i,2)+kk),(mx(i,1)-kk:mx(i,1)+kk)) ;% intensity 70 | tmp=tmp - th; 71 | tmp=max(tmp,1); 72 | % AA=AAt; 73 | % intensity=[]; 74 | % xx=[];x0=[];inthat=[]; 75 | eint=0;rint=0;rsquare=0; 76 | 77 | intensity=tmp(:); 78 | cci=intensity>1; 79 | intensity=log(intensity(cci)); 80 | AA=AAt(cci,:); 81 | % for ii =1 : dimm*dimm 82 | % 83 | % if tmp(ii)== 1 84 | % % tmp(ii)= tmp(ii)+1; 85 | % else 86 | % AA=[AA; [x1(ii)^2+y1(ii)^2 ,x1(ii), y1(ii) , 1]]; 87 | % intensity=[intensity;log(tmp(ii))] ; 88 | % 89 | % %xx=[xx; [x1(ii),y1(ii),tmp(ii)]]; 90 | % %AA([1 5 21 25],:)=[]; 91 | % %intensity(1 5 21 25)=[]; 92 | % end 93 | % end 94 | 95 | AT=AA' ; 96 | maa=AT*AA ; 97 | bb=AT*intensity ; 98 | c=maa\bb ; 99 | inthat=AA*inv(maa)*AT*intensity ;% estimated y value from regression model 100 | mint=mean(intensity); 101 | eint=sum((inthat-mint).^2); 102 | rint=sum((intensity-mint).^2); 103 | rsquare=eint/rint; 104 | % use the rsquare to filter bad beads 105 | if rsquare >rsq && isreal(c)== 1 && isreal(rsquare)==1 && c(1)<0 106 | % c(1) (-1/sigma^2) 107 | xavg=-c(2)/c(1)/2 ; 108 | yavg=-c(3)/c(1)/2 ; 109 | peak=exp(c(4)-(xavg^2+yavg^2)*c(1)); 110 | % the initial guess for optimiation 111 | %x0(4)=(-1/c(1)) 112 | x0(4)=sqrt((-1/c(1))/2); % chage to fit the standard deviation for gaussian fucntion 113 | x0(2)=xavg; 114 | x0(3)=yavg; 115 | x0(1)=peak; 116 | %x0=x0' 117 | %x0=ones(4,1); 118 | %global p1 d1 119 | %simplex method :: amoeba, 120 | %steepest cresent method :: mincg. 121 | 122 | %xa=mincg('f801','f801pd','ftau2cg',x0,5e-6); 123 | % [p,y]=amoeba(x0,'f801',1e-5); xa=p(1,:); 124 | xa=x0; 125 | 126 | % call out the function to optimized the solution start from the at 127 | % least estimator. 128 | % 129 | 130 | %concatenate it up 131 | pts=[pts,[mx(i,1)+xa(2),mx(i,2)+xa(3),xa(1),xa(4),rsquare, i]']; 132 | end 133 | end 134 | 135 | pts=pts'; 136 | outid=pts(:,end); 137 | out=pts(:,1:end-1); -------------------------------------------------------------------------------- /original upload version/base/getImLocalWindowInd_rf.m: -------------------------------------------------------------------------------- 1 | function ind=getImLocalWindowInd_rf(xy,imsz,wndra,skipstep) 2 | % convert the locations in images to indice matrice of regional windows. 3 | % Input : xy; n by 2 vectors of centroid location 4 | % imsz: 1 by 2 vector represent image size (i.e. =size(image)) 5 | % wndra: local window size (in radius) 6 | % output: n by wndra*2+1 indices matrices ; each row represent the local 7 | % windwos 8 | % 9 | % to use: linearize pixel intensity of given locations and window size can be obtinaed by 10 | % image(ind) 11 | % 12 | % 05/17/2018 developed by P-H Wu. at JHU. 13 | % 14 | 15 | % identify the location that are within the range of image 16 | 17 | ccx= xy(:,1) wndra+1; 18 | ccy= xy(:,2) wndra+1; 19 | cc=ccx & ccy; 20 | 21 | xmin=xy(:,1)-wndra; 22 | ymin=xy(:,2)-wndra; 23 | % account for negative indices 24 | % ymin(ymin<1)=-ymin(ymin<1)+1; 25 | % xmin(xmin<1)=-xmin(xmin<1)+1; 26 | % ymin(ymin<1)=1; 27 | % xmin(xmin<1)=1; 28 | 29 | % ymin(ymin>imsz(1))=2*imsz(1)-ymin(ymin>imsz(1)); 30 | % xmin(xmin>imsz(2))=2*imsz(2)-xmin(xmin>imsz(2)); 31 | 32 | indmin=sub2ind(imsz,ymin,xmin); 33 | indmax=imsz(1)*imsz(2); 34 | 35 | if length(skipstep)==1 % if its a value 36 | [gx,gy]=meshgrid([0:skipstep:2*wndra]*imsz(1),0:skipstep:2*wndra); 37 | 38 | else % when skipstep is a vector 39 | gx0=[0:1:2*wndra]*imsz(1); 40 | gy0=[0:1:2*wndra]; 41 | gx0=gx0(skipstep); 42 | gy0=gy0(skipstep); 43 | [gx,gy]=meshgrid(gx0,gy0); 44 | end 45 | gxy=gx+gy; 46 | gxy=gxy(:)'; 47 | ind=indmin+gxy(:)'; 48 | % ind(ind>indmax)=1; 49 | % ind(ind<1)=1; 50 | %ind(~cc,:)=0; 51 | end 52 | -------------------------------------------------------------------------------- /original upload version/base/get_ims.m: -------------------------------------------------------------------------------- 1 | function [im,TA]=get_ims(pth,nm,tp,rdo) 2 | if ~exist([pth,'TA\'],'dir');mkdir([pth,'TA\']);end 3 | if ~exist('rdo','var');rdo=0;end 4 | 5 | im=imread([pth,nm,tp]); 6 | if size(im,3)==1;im=cat(3,im,im,im);end 7 | pthTA=[pth,'TA\']; 8 | if exist([pthTA,nm,'tif'],'file') && ~rdo 9 | TA=imread([pthTA,nm,'tif']); 10 | else 11 | TA=find_tissue_area(im,nm); 12 | imwrite(TA,[pthTA,nm,'tif']); 13 | end 14 | 15 | 16 | % figure,subplot(1,2,1),imshow(im);subplot(1,2,2),imshow(TA) 17 | % TA=im(:,:,1); 18 | % TA=TA>30; 19 | % TA=imclose(TA,strel('disk',10)); 20 | % TA=bwareaopen(TA,5000); 21 | % imshow(TA) -------------------------------------------------------------------------------- /original upload version/base/image_preprocessing_global.m: -------------------------------------------------------------------------------- 1 | function [arf,amv,sz,cent]=image_preprocessing_global(immv,imrf,rf,cropim) 2 | if ~isa(imrf,'uint8') 3 | imrf=im2uint8(imrf); 4 | immv=im2uint8(immv); 5 | end 6 | 7 | % complement images 8 | if size(imrf,3)==3 9 | mvn=im2double(immv); 10 | rfn=im2double(imrf); 11 | mvn=std(mvn,[],3)>0.03; 12 | rfn=std(rfn,[],3)>0.03; 13 | 14 | arf=imcomplement(rgb2gray(imrf));arf(rfn==0)=mode(arf(:)); 15 | amv=imcomplement(rgb2gray(immv));amv(mvn==0)=mode(amv(:)); 16 | else 17 | arf=imcomplement(imrf); 18 | amv=imcomplement(immv); 19 | end 20 | 21 | if cropim && size(imrf,3)~=3 22 | % crop tissue from slide 23 | amvsz=imgradient(im2double(amv)); 24 | amvsz=imfilter(amvsz,fspecial('gauss',15,11),'replicate'); 25 | amvsz=imbinarize(amvsz); 26 | amvsz=imopen(amvsz,strel('disk',5)); 27 | amvsz=imclose(amvsz,strel('disk',10)); 28 | amvsz=bwareafilt(amvsz,1,'largest'); 29 | statsmv = regionprops(amvsz,'BoundingBox'); 30 | 31 | arfsz=imgradient(im2double(arf)); 32 | arfsz=imfilter(arfsz,fspecial('gauss',15,11),'replicate'); 33 | arfsz=imbinarize(arfsz); 34 | arfsz=imopen(arfsz,strel('disk',5)); 35 | arfsz=imclose(arfsz,strel('disk',10)); 36 | arfsz=bwareafilt(arfsz,1,'largest'); 37 | statsrf = regionprops(arfsz,'BoundingBox'); 38 | 39 | % full sized crop 40 | Bmv=round(statsmv.BoundingBox*rf); 41 | Brf=round(statsrf.BoundingBox*rf); 42 | Bmv(3:4)=[min([Bmv(3) Brf(3)]) min([Bmv(4) Brf(4)])]; 43 | Brf(3:4)=[min([Bmv(3) Brf(3)]) min([Bmv(4) Brf(4)])]; 44 | cent=round([Bmv(1)+Bmv(3)/2 Bmv(2)+Bmv(4)/2]); 45 | sz=Brf(1:2)-Bmv(1:2); 46 | 47 | % smaller crop 48 | Bmv=round(Bmv/rf); 49 | Brf=round(Brf/rf); 50 | amv=imfilter(amv,fspecial('gauss',21,11),'replicate'); 51 | arf=imfilter(arf,fspecial('gauss',21,11),'replicate'); 52 | arf=imcrop(arf,Brf); 53 | amv=imcrop(amv,Bmv); 54 | 55 | % resize images 56 | amv=imresize(amv,1/rf,'nearest'); 57 | arf=imresize(arf,1/rf,'nearest'); 58 | 59 | % figure(3) 60 | % subplot(1,2,1),imagesc(amv),axis equal, axis off 61 | % subplot(1,2,2),imagesc(arf),axis equal, axis off 62 | else 63 | % remove noise 64 | modeimr=mode(arf(:)); 65 | modeimv=mode(amv(:)); 66 | % resize images 67 | amv=imresize(amv,1/rf,'nearest'); 68 | arf=imresize(arf,1/rf,'nearest'); 69 | if size(immv,3)==3 70 | im2=amv>(modeimv+20); % delete small objects in image 71 | im2=imdilate(im2,strel('disk',1)); 72 | im2=bwlabel(im2); 73 | N=histcounts(im2(:),max(im2(:))+1); 74 | N(1)=0; 75 | N(N<(max(N)/10))=0; 76 | N(N>0)=1; 77 | im2=N(im2+1); 78 | amv(~im2)=modeimv; 79 | 80 | im2=arf>(modeimr+20); 81 | im2=imdilate(im2,strel('disk',1)); 82 | im2=bwlabel(im2); 83 | N=histcounts(im2(:),max(im2(:))+1); 84 | N(1)=0; 85 | N(N<(max(N)/10))=0; 86 | N(N>0)=1; 87 | im2=N(im2+1); 88 | arf(~im2)=modeimr; 89 | end 90 | sz=[0 0]; 91 | cent=[0 0]; 92 | end 93 | 94 | amv=imgaussfilt(amv,2); 95 | arf=imgaussfilt(arf,2); 96 | end -------------------------------------------------------------------------------- /original upload version/base/invert_D.m: -------------------------------------------------------------------------------- 1 | function Dnew=invert_D(D,skk2) 2 | if ~exist('skk','var');skk=10;end 3 | if ~exist('skk2','var');skk2=3;end 4 | 5 | % calculate coordinates 6 | [xx,yy]=meshgrid(1:size(D,2),1:size(D,1)); 7 | xnew=xx+D(:,:,1); % new position of x 8 | ynew=yy+D(:,:,2); % new position of y 9 | 10 | % interpolate D at original position 11 | D1=D(:,:,1);D2=D(:,:,2); 12 | D1=D1(:);D2=D2(:);xnew2=xnew(:);ynew2=ynew(:); 13 | F1=scatteredInterpolant(xnew2(1:skk:end),ynew2(1:skk:end),D1(1:skk:end)); 14 | F2=scatteredInterpolant(xnew2(1:skk:end),ynew2(1:skk:end),D2(1:skk:end)); 15 | 16 | [xx,yy]=meshgrid(1:skk2:size(D,2),1:skk2:size(D,1)); 17 | D1=-F1(xx,yy); 18 | D2=-F2(xx,yy); 19 | Dnew=zeros(size(D)); 20 | Dnew(:,:,1)=imresize(D1,size(D(:,:,1))); 21 | Dnew(:,:,2)=imresize(D2,size(D(:,:,1))); 22 | Dnew(isnan(Dnew))=0; 23 | end -------------------------------------------------------------------------------- /original upload version/base/make_final_grids.m: -------------------------------------------------------------------------------- 1 | function [D,xgg,ygg,x,y]=make_final_grids(xgg0,ygg0,bf,x,y,szim) 2 | % xgg / ygg = 20x17 or whatever grid of displacements for image 3 | % x / y = points on 1x image that xgg and ygg are centered at 4 | % C0 = corr2 of immv and imrf tiles before elastic registration 5 | % CR = corr2 of immv and imrf tiles after elastic registration (same size as xgg / ygg) 6 | % bf = buffer I applied around 1x image (bf = 0 if you have no buffer) 7 | % sz = size of each tile that finetune registration is calculated on (mine are 250) 8 | % szim = size of 1x image 9 | % xgg / ygg are -5000 at locations that don't have tissue space 10 | 11 | xgg=xgg0; 12 | ygg=ygg0; 13 | if 1 14 | mxy=75;%50 % allow no translation larger than this cutoff 15 | cmxy=xgg>mxy | ygg>mxy; % non continuous values 16 | xgg(cmxy)=-5000; 17 | 18 | % find points where registration was calculated 19 | cempty=xgg==-5000; 20 | xgg(cempty)=0;ygg(cempty)=0; 21 | 22 | %xgg0=xgg;ygg0=ygg; 23 | % replace non continuous values with mean of neighbors 24 | [~,~,dxgg,dygg,~,sxgg,sygg]=fill_vals(xgg,ygg,cempty,1); 25 | m1=abs((xgg-dxgg)./dxgg);m1(m1==Inf)=0; % percent difference between x and mean of surrounding 26 | m2=abs((ygg-dygg)./dygg);m2(m2==Inf)=0; 27 | dds=sxgg>50 | sygg>50;ddm=m1>5 | m2>5;ddp=abs(xgg)>80 | abs(ygg)>80; 28 | dd=(dds | ddm | ddp) & ~cempty; 29 | xgg(dd)=dxgg(dd);ygg(dd)=dygg(dd); 30 | 31 | % fill in values outside tissue region with mean of neighbors 32 | cc=cempty;count=1; 33 | while sum(cc(:))>0 && count<500 34 | [~,~,dxgg,dygg,denom]=fill_vals(xgg,ygg,cc); 35 | cfill=denom>2 & cc; % touching 3+ numbers and needs to be filled 36 | xgg(cfill)=dxgg(cfill); 37 | ygg(cfill)=dygg(cfill); 38 | cc=cc & ~cfill; % needs to be filled and has not been filled 39 | count=count+1; 40 | end 41 | disp([count 500]) 42 | xgg=imgaussfilt(xgg,1); 43 | ygg=imgaussfilt(ygg,1); 44 | else 45 | cempty=xgg==-5000; 46 | xgg(cempty)=0;ygg(cempty)=0; 47 | 48 | ct=30; 49 | xgg(abs(xgg)>ct)=0; 50 | ygg(abs(ygg)>ct)=0; 51 | 52 | xgg=imgaussfilt(xgg,2); 53 | ygg=imgaussfilt(ygg,2); 54 | end 55 | 56 | % add buffer to outline of displacement map to avoid discontinuity 57 | xgg=cat(1,xgg(1,:),xgg,xgg(end,:)); 58 | xgg=cat(2,xgg(:,1),xgg,xgg(:,end)); 59 | ygg=cat(1,ygg(1,:),ygg,ygg(end,:)); 60 | ygg=cat(2,ygg(:,1),ygg,ygg(:,end)); 61 | 62 | x=[1; unique(x)-bf; szim(2)]; 63 | y=[1; unique(y)-bf; szim(1)]; 64 | 65 | % get D 66 | [xq,yq] = meshgrid(1:szim(2),1:szim(1)); 67 | xgq=interp2(x,y,xgg,xq,yq,'spline'); 68 | ygq=interp2(x,y,ygg,xq,yq,'spline'); 69 | D=cat(3,xgq,ygq); 70 | 71 | end 72 | 73 | 74 | function [xgg,ygg,dxgg,dygg,denom,sxgg,sygg]=fill_vals(xgg,ygg,cc,xystd) 75 | if ~exist('xystd','var');xystd=0;sxgg=[];sygg=[];end 76 | % xgg ygg are matrices to be smoothed 77 | % cc is logical matrix containing locations to be smoothed 78 | denom=imfilter(double(~cc),[1 1 1;1 0 1;1 1 1]); 79 | 80 | if xystd % get standard deviation of nearest neighbors 81 | gridX=get_nn_grids(xgg); 82 | gridY=get_nn_grids(ygg); 83 | gridD=get_nn_grids(~cc); 84 | gridX=(gridX-xgg).^2.*gridD; 85 | gridY=(gridY-ygg).^2.*gridD; 86 | 87 | sxgg=(sum(gridX,3)./sum(denom,3)).^0.5;sxgg(cc)=0; 88 | sygg=(sum(gridY,3)./sum(denom,3)).^0.5;sygg(cc)=0; 89 | end 90 | 91 | denom(denom==0)=1; 92 | dxgg=imfilter(xgg,[1 1 1;1 0 1;1 1 1])./denom; 93 | dygg=imfilter(ygg,[1 1 1;1 0 1;1 1 1])./denom; 94 | xgg(cc)=dxgg(cc); 95 | ygg(cc)=dygg(cc); 96 | end 97 | 98 | function gridX=get_nn_grids(xgg) 99 | d1=imfilter(xgg,[1 0 0;0 0 0;0 0 0]); 100 | d2=imfilter(xgg,[0 1 0;0 0 0;0 0 0]); 101 | d3=imfilter(xgg,[0 0 1;0 0 0;0 0 0]); 102 | d4=imfilter(xgg,[0 0 0;1 0 0;0 0 0]); 103 | d5=imfilter(xgg,[0 0 0;0 0 1;0 0 0]); 104 | d6=imfilter(xgg,[0 0 0;0 0 0;1 0 0]); 105 | d7=imfilter(xgg,[0 0 0;0 0 0;0 1 0]); 106 | d8=imfilter(xgg,[0 0 0;0 0 0;0 0 1]); 107 | gridX=cat(3,d1,d2,d3,d4,d5,d6,d7,d8); 108 | end 109 | 110 | % CP=(CR-C0)./C0; 111 | % cc=CP<0; % cc=(CR-C0)./C0<0 | abs(xgg)>sz/4 | abs(ygg)>sz/4; 112 | % [xgg,ygg]=fill_vals(xgg,ygg,cc); -------------------------------------------------------------------------------- /original upload version/base/make_rgb_kmeans_90.m: -------------------------------------------------------------------------------- 1 | function [CVS,tiss_area]=make_rgb_kmeans_nw(im,knum,v) 2 | % im: image (type uint8) 3 | % knum: number of pixels to use for kmeans 4 | % v: if true, visualize colors chosen for deconvolution 5 | 6 | % uses kmeans to detect bluest and pinkest groups in H&E image 7 | 8 | if nargin==1 9 | knum=200000; 10 | v=0; 11 | elseif nargin==2 12 | v=0; 13 | end 14 | 15 | im=im2single(im); 16 | imr0=im(:,:,1);imr=imgaussfilt(imr0,1); 17 | img0=im(:,:,2);img=imgaussfilt(img0,1); 18 | imb0=im(:,:,3);imb=imgaussfilt(imb0,1); 19 | Cw=[mode(imr0(:)) mode(img0(:)) mode(imb0(:))]; 20 | %meshlist=cat(2,imr(:),img(:),imb(:)); 21 | 22 | 23 | % make list of nonwhite pixels in tissue image 24 | tiss_area=std(cat(3,imr0,img0,imb0),[],3); 25 | tiss_area=tiss_area>0.03 & tiss_area<0.35; % remove white and random bright pixels 26 | meshlist=cat(2,imr(tiss_area),img(tiss_area),imb(tiss_area)); 27 | 28 | % classify subset of tissue area into 255 groups 29 | tic; 30 | if knum>size(meshlist,1) 31 | knum=size(meshlist,1); 32 | end 33 | kk=randperm(size(meshlist,1),knum); 34 | meshlist=meshlist(kk,:); 35 | [idx,C]=kmeans(meshlist,1000); 36 | clearvars meshlist 37 | 38 | % delete 50 classes with fewest pixels 39 | N=histcounts(idx,1000); 40 | [~,s]=sort(N); 41 | C=C(s,:); 42 | C=C(26:end,:); 43 | 44 | % delete classes that are too close to greyscale 45 | [~,s]=sort(sum(C,2)); 46 | C=C(s,:); 47 | Cs=std(C,[],2); 48 | 49 | % define background class 50 | bg=-log((Cw*255+1)./256); 51 | bg=bg/norm(bg); 52 | C=C(Cs>0.1,:); 53 | 54 | % find bluest class (average of 100 darkest classes): 55 | [~,s]=sort(sum(C(:,3),2)); 56 | Cb=C(s,:); 57 | ii=Cb(:,3)>Cb(:,1); 58 | Cb=Cb(ii,:); 59 | num=min([size(Cb,1)/5 75]); 60 | bluest0=mean(Cb(1:num,:)); % 75 61 | bluest=-log((bluest0*255+1)./256); 62 | bluest=bluest/norm(bluest); 63 | 64 | % find brownest class (average of 100 lightest classes): 65 | [~,s]=sort(sum(C(:,1),2)); 66 | Cr=C(s,:); 67 | ii=Cr(:,1)>Cr(:,3); 68 | if isempty(ii) || sum(ii)<10 69 | b=Cr(:,1)./Cr(:,3); 70 | [~,s]=sort(sum(b,2)); 71 | Cr=Cr(s,:); 72 | else 73 | Cr=Cr(ii,:); 74 | end 75 | 76 | num=min([round(size(Cr,1)/5) 75]); 77 | pinkest0=mean(Cr(end-num:end,:)); 78 | pinkest=-log((pinkest0*255+1)./256); 79 | pinkest=pinkest/norm(pinkest); 80 | 81 | CVS=[bluest;pinkest;[1 1 1]-mean([bluest;pinkest])]; 82 | %CVS=[bluest;pinkest;bg]; 83 | if v % visualize chosen colors and colormap 84 | cmap=cat(3,C(:,1),C(:,2),C(:,3)); 85 | cmap=repmat(cmap,[1 100]); 86 | figure(1),imshow(cmap),set(gcf,'color','k'),title('color map') 87 | 88 | tmp1=permute(bluest0,[1 3 2]); 89 | tmp2=permute(pinkest0,[1 3 2]); 90 | tmp3=permute(Cw,[1 3 2]); 91 | figure(2), 92 | subplot(1,3,1),imshow(repmat(tmp1,[50 50 1])),set(gcf,'color','k') 93 | subplot(1,3,2),imshow(repmat(tmp2,[50 50 1])),set(gcf,'color','k') 94 | subplot(1,3,3),imshow(repmat(tmp3,[50 50 1])),set(gcf,'color','k') 95 | % pause 96 | % close all 97 | disp(CVS) 98 | end 99 | end 100 | 101 | 102 | 103 | %Cp=C(sum(C,2)>2,:); 104 | %Cp=C(C(:,1)>C(:,3),:); % red channel greater than blue channel 105 | % Cp=C; 106 | % Cpink=(Cp(:,1)+Cp(:,3)/2)./Cp(:,2); 107 | % ipink=find(Cpink==max(Cpink)); 108 | % pinkest0=Cp(ipink,:); 109 | % Cp=C(end-19:end,:); 110 | % [~,s]=sort(Cp(:,1)); 111 | % pinkest0=Cp(s,:); 112 | % pinkest0=mean(pinkest0(end-9:end,:)); % take 10 of the lightest 20 classes with most red -------------------------------------------------------------------------------- /original upload version/base/mskcircle2_rect.m: -------------------------------------------------------------------------------- 1 | function msk=mskcircle2_rect(sz,da) 2 | % generate the circle mask based on size of input matrix 3 | % sz : matrix size 4 | % da : diameter of circle; 5 | if nargin==1; 6 | da=min(sz); 7 | end 8 | msk=zeros(sz); 9 | m = sz; 10 | cent = (m+1)/2; 11 | 12 | x = 1:m(2) ; 13 | y = 1:m(1) ; 14 | [gx,gy]=meshgrid(x,y); 15 | gx=gx-cent(2); 16 | gy=gy-cent(1); 17 | 18 | % calculate distance; 19 | D=sqrt(gx.^2 + gy.^2); 20 | rr=(da-1)/2; 21 | ind= D <= rr; 22 | msk(ind)=1; 23 | 24 | 25 | end -------------------------------------------------------------------------------- /original upload version/base/pad_im.m: -------------------------------------------------------------------------------- 1 | function im=pad_im_both(im,sz,ext) 2 | 3 | szim=[sz(1)-size(im,1) sz(2)-size(im,2)]; 4 | 5 | if size(im,3)==3 6 | 7 | a0=im(:,:,1);a=mode(a0(:)); 8 | b0=im(:,:,2);b=mode(b0(:)); 9 | c0=im(:,:,3);c=mode(c0(:)); 10 | ima=padarray(a0,szim,a,'post'); 11 | imb=padarray(b0,szim,b,'post'); 12 | imc=padarray(c0,szim,c,'post'); 13 | 14 | ima=padarray(ima,ext,a,'both'); 15 | imb=padarray(imb,ext,b,'both'); 16 | imc=padarray(imc,ext,c,'both'); 17 | 18 | im=cat(3,ima,imb,imc); 19 | else 20 | a=mode(im(:)); 21 | im=padarray(im,szim,a,'post'); 22 | im=padarray(im,ext,a,'both'); 23 | end 24 | end -------------------------------------------------------------------------------- /original upload version/base/pad_im_both.m: -------------------------------------------------------------------------------- 1 | function im=pad_im_both(im,sz,ext,fillval) 2 | if nargin<4;fillval=squeeze(mode(mode(im,2),1))';end 3 | if nargin<3;ext=0;end 4 | 5 | 6 | if size(im,1)>sz(1);im=im(1:sz(1),:,:);end 7 | if size(im,2)>sz(2);im=im(:,1:sz(2),:);end 8 | 9 | szim=[sz(1)-size(im,1) sz(2)-size(im,2)]; 10 | szim(szim<0)=0; 11 | if size(im,3)==3 12 | if length(fillval)==1;fillval=[fillval fillval fillval];end 13 | ima=padarray(im(:,:,1),szim,fillval(1),'post'); 14 | imb=padarray(im(:,:,2),szim,fillval(2),'post'); 15 | imc=padarray(im(:,:,3),szim,fillval(3),'post'); 16 | ima=padarray(ima,[ext ext],fillval(1),'both'); 17 | imb=padarray(imb,[ext ext],fillval(2),'both'); 18 | imc=padarray(imc,[ext ext],fillval(3),'both'); 19 | im=cat(3,ima,imb,imc); 20 | else 21 | im=padarray(im,szim,fillval,'post'); 22 | im=padarray(im,[ext ext],fillval,'both'); 23 | end 24 | 25 | % szim=[sz(1)-size(im,1) sz(2)-size(im,2)]; 26 | % szA=floor(szim/2); 27 | % szB=szim-szA+ext; 28 | % szA=szA+ext; 29 | % if size(im,3)==3 30 | % if length(fillval)==1;fillval=[fillval fillval fillval];end 31 | % ima=padarray(im(:,:,1),szA,fillval(1),'pre'); 32 | % imb=padarray(im(:,:,2),szA,fillval(2),'pre'); 33 | % imc=padarray(im(:,:,3),szA,fillval(3),'pre'); 34 | % ima=padarray(ima,szB,fillval(1),'post'); 35 | % imb=padarray(imb,szB,fillval(2),'post'); 36 | % imc=padarray(imc,szB,fillval(3),'post'); 37 | % im=cat(3,ima,imb,imc); 38 | % else 39 | % im=padarray(im,szA,fillval,'pre'); 40 | % im=padarray(im,szB,fillval,'post'); 41 | % end 42 | 43 | end -------------------------------------------------------------------------------- /original upload version/base/pad_im_both2.m: -------------------------------------------------------------------------------- 1 | function im=pad_im_both2(im,sz,ext,fillval) 2 | if nargin<4;fillval=squeeze(mode(mode(im,2),1))';end 3 | if nargin<3;ext=0;end 4 | 5 | szim=[sz(1)-size(im,1) sz(2)-size(im,2)]; 6 | szA=floor(szim/2); 7 | szB=szim-szA+ext; 8 | szA=szA+ext; 9 | if size(im,3)==3 10 | if length(fillval)==1;fillval=[fillval fillval fillval];end 11 | ima=padarray(im(:,:,1),szA,fillval(1),'pre'); 12 | imb=padarray(im(:,:,2),szA,fillval(2),'pre'); 13 | imc=padarray(im(:,:,3),szA,fillval(3),'pre'); 14 | ima=padarray(ima,szB,fillval(1),'post'); 15 | imb=padarray(imb,szB,fillval(2),'post'); 16 | imc=padarray(imc,szB,fillval(3),'post'); 17 | im=cat(3,ima,imb,imc); 18 | else 19 | im=padarray(im,szA,fillval,'pre'); 20 | im=padarray(im,szB,fillval,'post'); 21 | end 22 | end -------------------------------------------------------------------------------- /original upload version/base/pkfndW.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashleylk/CODA/2984da5d1aeef770d6c8fd02cc46a5d3194aa449/original upload version/base/pkfndW.p -------------------------------------------------------------------------------- /original upload version/base/plot_settings.m: -------------------------------------------------------------------------------- 1 | function plot_settings(ll) 2 | if nargin==0;ll=0;end 3 | axis equal;axis on;box on; 4 | daspect([1 1 1]) 5 | camva(9.72) 6 | set(gcf,'color','w'); 7 | set(gca,'xtick',[],'ytick',[],'ztick',[]) 8 | xlabel('');zlabel('');ylabel(''); 9 | 10 | el=23; 11 | az=44; 12 | view(az,el) 13 | if ll 14 | delete(findall(gcf,'Type','light')) 15 | set(gca,'CameraViewAngleMode','Manual') 16 | camlight 17 | lighting gouraud 18 | lightangle(az,el) 19 | end 20 | end -------------------------------------------------------------------------------- /original upload version/base/preprocessing.m: -------------------------------------------------------------------------------- 1 | function [im,impg,TA,fillval]=preprocessing(im,TA,szz,padall,IHC) 2 | % pad image 3 | fillval=squeeze(mode(mode(im,2),1))'; 4 | if IHC==2;fillval=[241 241 241];end 5 | if IHC==5;fillval=[0 0 0];end 6 | if ~isempty(padall) 7 | im=pad_im_both2(im,szz,padall,fillval); 8 | if size(TA,1)~=size(im,1) || size(TA,2)~=size(im,2) 9 | TA=pad_im_both2(TA,szz,padall,0); 10 | end 11 | end 12 | 13 | % remove noise and complement images 14 | TA=TA>0; 15 | if ~isa(im,'uint8') 16 | im=im2uint8(im); 17 | end 18 | 19 | if IHC==2 20 | impg=rgb2gray(im); 21 | TA=ones(size(im(:,:,1))); 22 | elseif IHC==5 23 | impg=imcomplement(rgb2gray(im)); 24 | %TA=ones(size(im(:,:,1)));fillval=0; 25 | elseif size(im,3)==3 26 | ima=im(:,:,1);ima(~TA)=255; 27 | imb=im(:,:,2);imb(~TA)=255; 28 | imc=im(:,:,3);imc(~TA)=255; 29 | imp=cat(3,ima,imb,imc); 30 | impg=imcomplement(rgb2gray(imp)); 31 | else 32 | imp=im; 33 | imp(~TA)=255; 34 | impg=imcomplement(imp); 35 | end 36 | 37 | 38 | impg=imgaussfilt(impg,2); 39 | end -------------------------------------------------------------------------------- /original upload version/base/random_augmentation.m: -------------------------------------------------------------------------------- 1 | function [imh,imlabel]=random_augmentation(imh0,imlabel0,rot,sc,hue,bl,rsz) 2 | % randomly augments with rotation, scaling, and hue 3 | if nargin<7 4 | rot=1; 5 | sc=1; 6 | hue=1; 7 | rsz=0; 8 | bl=0; 9 | end 10 | if ~exist('imlabel0','var');imlabel0=imh0;end 11 | 12 | imh=double(imh0); 13 | imlabel=double(imlabel0); 14 | szz=size(imh0,1); 15 | 16 | % random rotation 17 | if rot 18 | angs=0:5:355; 19 | ii=randperm(3,1); 20 | imh=imrotate(imh,angs(ii)); 21 | imlabel=imrotate(imlabel,angs(ii)); 22 | end 23 | 24 | % random scaling 25 | if sc 26 | scales=[0.75:0.01:0.9 1.1:0.01:1.25]; 27 | ii=randperm(length(scales),1); 28 | imh=imresize(imh,scales(ii),'nearest'); 29 | imlabel=imresize(imlabel,scales(ii),'nearest'); 30 | end 31 | 32 | if hue 33 | % scale blue / red 34 | rd=[0.88:0.01:0.98 1.02:0.01:1.12]; 35 | bl=[0.88:0.01:0.98 1.02:0.01:1.12]; 36 | gr=[0.88:0.01:0.98 1.02:0.01:1.12]; 37 | ird=randperm(length(rd),1); 38 | ibl=randperm(length(bl),1); 39 | igr=randperm(length(gr),1); 40 | 41 | % scale red 42 | imr=255-imh(:,:,1); 43 | imh(:,:,1)=255-(imr*rd(ird)); 44 | % scale blue 45 | imb=255-imh(:,:,3); 46 | imh(:,:,3)=255-(imb*bl(ibl)); 47 | % scale green 48 | img=255-imh(:,:,2); 49 | imh(:,:,3)=255-(img*gr(igr)); 50 | end 51 | 52 | if bl 53 | bll=ones([1 50]); 54 | bll(1)=1.05; 55 | bll(2)=1.1; 56 | bll(3)=1.15; 57 | bll(4)=1.2; 58 | ibl=randperm(length(bll),1); 59 | bll=bll(ibl); 60 | 61 | if bll~=1 62 | imh=imgaussfilt(imh,bll); 63 | end 64 | end 65 | 66 | 67 | if rsz 68 | szh=size(imh,1); 69 | if szh>szz 70 | cent=round(size(imh,1)/2); 71 | sz1=floor((szz-1)/2); 72 | sz2=ceil((szz-1)/2); 73 | imh=imh(cent-sz1:cent+sz2,cent-sz1:cent+sz2,:); 74 | imlabel=imlabel(cent-sz1:cent+sz2,cent-sz1:cent+sz2,:); 75 | end 76 | 77 | if szh0 & arf0>0); 15 | b=arf0(imout>0 & arf0>0); 16 | RR=corr2(a,b); 17 | end 18 | end -------------------------------------------------------------------------------- /original upload version/base/reg_ims_com.m: -------------------------------------------------------------------------------- 1 | function [tform,amv,rsft,xyt,RR]=reg_ims_com(amv0,arf,count,sz,rf,deg0,xy0,r,th) 2 | tform=[];rsft=0;xyt=[0; 0];RR=0;amv=amv0; 3 | mm=mode(amv(:));mmr=mode(arf(:)); 4 | % arf=padarray(arf,[50 50],mmr,'both'); 5 | % amv=padarray(amv,[50 50],mm,'both'); 6 | 7 | if nargin<10 8 | theta=-90:0.5:90; 9 | thetaout=2; 10 | else 11 | theta=th{1}; 12 | thetaout=th{2}; 13 | end 14 | if nargin<8;r=0;end 15 | if nargin<7;xy0=[0; 0];end 16 | if nargin<6;deg0=0;end 17 | 18 | brf=single(arf); 19 | 20 | xyt=[0; 0]; 21 | 22 | rsft=zeros([1 count+1]); 23 | rsft(1)=deg0; 24 | amvr=imrotate(amv0,sum(rsft),'crop'); 25 | if sum(amvr(:))==0;rsft=sum(rsft);return;end 26 | 27 | % first translation dictated by center of mass 28 | if r 29 | % find center of mass of images 30 | amv2=bpassW(amvr,2,50);amv2=amv2>0; 31 | arf2=bpassW(arf,2,50);arf2=arf2>0; 32 | cx=sum(amv2);cy=sum(amv2,2); 33 | cx=cumsum(cx);cy=cumsum(cy); 34 | cmamv=[find(cx>cx(end)/2,1) find(cy>cy(end)/2,1)]; 35 | cx=sum(arf2);cy=sum(arf2,2); 36 | cx=cumsum(cx);cy=cumsum(cy); 37 | cmarf=[find(cx>cx(end)/2,1) find(cy>cy(end)/2,1)]; 38 | xy=cmarf-cmamv; 39 | xyt=[xy(1); xy(2)]*rf; 40 | end 41 | 42 | xyt=xyt+xy0; 43 | amv=imtranslate(amvr,xyt'/rf,'OutputView','same'); 44 | a=arf>0; 45 | RR0=corr2(amv(a),arf(a)); 46 | 47 | % iterate 'count' times to achieve sufficient global registration on resized, blurred image 48 | for kk=1:count 49 | bmv=single(amv); 50 | 51 | % use radon for rotational registration 52 | R0 = radon(arf, theta); 53 | Rn = radon(amv, theta); 54 | R0=bpassW(R0,1,3); 55 | Rn=bpassW(Rn,1,3); 56 | 57 | try rsf1=calculate_transform(R0,Rn);catch;rsf1=0;end 58 | rsf=rsf1(1)/thetaout; 59 | 60 | % rotate image then calculate translational registration 61 | bmvr=imrotate(bmv,rsf(1),'crop'); 62 | bmvr(bmvr==0)=mm; 63 | 64 | try xy1=calculate_transform(brf,bmvr);catch;xy1=[0 0];end 65 | try xy2=calculate_transform(bmvr,brf);catch;xy2=[0 0];end 66 | xy=mean([xy1;-xy2]); 67 | 68 | % keep old transform in case update is bad 69 | rsft0=rsft; 70 | xyt0=xyt; 71 | 72 | % update total rsf and xy 73 | rsft(kk+1)=rsf; 74 | if rsf>0 % update rotation 75 | xyt=[cosd(rsf) -sind(rsf);sind(rsf) cosd(rsf)]*xyt; % clockwise rotation 76 | else 77 | xyt=[cosd(rsf) sind(rsf);-sind(rsf) cosd(rsf)]*xyt; % counterclockwise rotation 78 | end 79 | xyt=xyt+([xy(1); xy(2)]*rf); % translation 80 | 81 | % update registration image 82 | amv=imrotate(amv0,sum(rsft),'crop'); 83 | amv=imtranslate(amv,xyt'/rf,'OutputView','same'); 84 | amv(amv==0)=mm; 85 | 86 | a=arf>0; 87 | RR=corr2(amv(a),arf(a)); 88 | if RR+0.022 % if iteration hasn't improved correlation of images, then stop 89 | rsft=rsft0; 90 | xyt=xyt0; 91 | amv=imrotate(amv0,sum(rsft),'crop'); 92 | amv=imtranslate(amv,xyt'/rf,'OutputView','same'); 93 | amv(amv==0)=mm; 94 | RR=corr2(amv(a),arf(a)); 95 | break; 96 | end 97 | 98 | x1=round(size(amv,2)/2); % maximum distance a point in the image moves 99 | y1=round(size(amv,1)/2); 100 | x2=x1*cosd(rsft(kk+1))-y1*sind(rsft(kk+1))+xy(2)-x1; 101 | y2=x1*sind(rsft(kk+1))+ y1*cosd(rsft(kk+1))+xy(1)-y1; 102 | rff=sqrt(x2^2+y2^2); 103 | if rff<0.75 || RR>0.9 104 | break 105 | end 106 | 107 | end 108 | % apply calculated registration to fullscale image 109 | % (account for translation 'sz' due to cropping tissue from full images) 110 | rsft=sum(rsft); 111 | tform=affine2d([cosd(rsft) -sind(rsft) 0;sind(rsft) cosd(rsft) 0;xyt(1)+sz(1) xyt(2)+sz(2) 1]); 112 | end -------------------------------------------------------------------------------- /original upload version/base/register_cell_coordinates_1type.m: -------------------------------------------------------------------------------- 1 | function register_cell_coordinates_1type(pth1x,scale,pthcoords,pad2,szvol) 2 | if ~exist('pad2','var');pad2=0;end 3 | datapth=[pth1x,'registered\elastic registration\save_warps\']; 4 | pthD=[pth1x,'registered\elastic registration\save_warps\D\']; 5 | if ~exist('pthcoords','var');pthcoords=[pth1x(1:end-3),'5x\cell_coordinates\'];end 6 | 7 | 8 | pthimG=[pth1x,'registered\']; 9 | pthimE=[pth1x,'registered\elastic registration\']; 10 | outpth=[pth1x,'registered\elastic registration\cell_coordinates_registered\']; 11 | if ~isfolder(outpth);mkdir(outpth);end 12 | matlist=dir([pthcoords,'*mat']); 13 | volcell=uint8([]); 14 | % scalevol=4/6; % scale from 1x (8um/pixl) to 6um/pixel 15 | scalevol=4.5/8; 16 | % scalevol=1; % scale from 1x (8um/pixl) to 12um/pixel 17 | if exist([pthimE,matlist(1).name(1:end-4),'.jpg'],'file');tp='.jpg';tp2='.tif';else;tp='.tif';tp2='.jpg';end 18 | if ~exist('szvol','var');im=imread([pthimE,matlist(1).name(1:end-4),tp]);szvol=round(size(im(:,:,1))*scalevol);end 19 | 20 | load([datapth,matlist(1).name],'padall','szz'); 21 | szz2=szz+padall;if pad2==1;szz2=szz+padall;end 22 | count=1; 23 | for kk=1:length(matlist) 24 | if ~exist([pthD,matlist(kk).name],'file');continue;end 25 | load([pthcoords,matlist(kk).name],'cell_unreg','xy');%xy=cell_unreg; 26 | if isempty(xy) || ~isfile([pth1x,matlist(kk).name(1:end-4),'.tif']);continue;end 27 | xy=xy/scale; 28 | 29 | if kk==1 30 | try im0=imread([pth1x,matlist(kk).name(1:end-4),tp]);catch im0=imread([pth1x,matlist(kk).name(1:end-4),tp2]);end 31 | figure(31),imshow(im0);hold on;scatter(xy(:,1),xy(:,2),'*y');hold off 32 | end 33 | 34 | xy=xy+padall; 35 | if pad2==1 36 | a=imfinfo([pth1x,matlist(kk).name(1:end-4),'.tif']);a=[a.Height a.Width]; 37 | szim=[szz(1)-a(1) szz(2)-a(2)];szA=floor(szim/2);szA=[szA(2) szA(1)]; 38 | xy=xy+szA; 39 | end 40 | % rough register points 41 | try 42 | load([datapth,matlist(kk).name],'tform','cent','szz'); 43 | xyr = transformPointsForward(tform,xy-cent); 44 | xyr=xyr+cent; 45 | cc=min(xyr,[],2)>1 & xyr(:,1)0;xyrE=xyrE(cc,:); 61 | ii=sub2ind(size(Dnew),xyrE(:,2),xyrE(:,1)); 62 | ixyA=[D1(ii) D2(ii)]; 63 | xye=xyrE+ixyA; 64 | xye=xye*rsim; 65 | %imE=imread([pthimE,nm,'jpg']);figure(3),imshow(imE),hold on,scatter(xye(:,1)/scale,xye(:,2)/scale,'*g'); 66 | 67 | % add coordinates to list 68 | disp([kk length(matlist) size(xye,1) round(toc/60)]) 69 | try xyout(ff,2:3)=xye; 70 | catch 71 | disp('cca') 72 | end 73 | clearvars xyim xy cell_unreg tform cent D Dnew f 74 | end 75 | 76 | 77 | end -------------------------------------------------------------------------------- /original upload version/base/register_cell_coordinates_new.m: -------------------------------------------------------------------------------- 1 | function register_cell_coordinates_new(pth1x,pthcoords,datapth,scale,rsim,outpth) 2 | if ~exist('rsim','var');rsim=12;end 3 | if ~exist('outpth','var');outpth=[pth1x,'cell_coordinates_registered\'];end 4 | mkdir(outpth); 5 | pthimG=[pth1x,'registered\']; 6 | pthimE=[pth1x,'registered\elastic registration\']; 7 | if ~exist('scim','var');scim=4/3;end 8 | pthD=[datapth,'D\']; 9 | matlist=dir([pthcoords,'*mat']); 10 | 11 | 12 | outpthim=[outpth,'ims\'];mkdir(outpthim); 13 | 14 | load([datapth,matlist(1).name],'padall','szz'); 15 | padall=padall*scale; 16 | 17 | 18 | 19 | szz2=imfinfo([pthimG,matlist(1).name(1:end-4),'.jpg']); 20 | szz2=round([szz2.Height szz2.Width]*scale); 21 | for kk=1:length(matlist) 22 | szz2=imfinfo([pthimG,matlist(kk).name(1:end-4),'.jpg']); 23 | szz2=round([szz2.Height szz2.Width]*scale); 24 | 25 | load([pthcoords,matlist(kk).name]); 26 | if exist('cell_unreg','var');xy0=cell_unreg; 27 | elseif exist('xyboth','var');xy0=xyboth; 28 | elseif exist('cell_locations','var');xy0=cell_locations; 29 | elseif exist('leuko','var');xy0=leuko; 30 | else;xy0=xy; 31 | end 32 | 33 | % pad cell coordinates to match registration 34 | xy=xy0+padall; 35 | % rough register 36 | try 37 | load([datapth,matlist(kk).name],'tform','cent'); 38 | cent=cent*scale;tform.T(3,1:2)=tform.T(3,1:2)*scale; 39 | 40 | xyr = transformPointsForward(tform,xy-cent)+cent; 41 | cc=min(xyr,[],2)>1 & xyr(:,1)0;xyrE=xyrE(cc,:); 53 | scaleE=scale/rsim; 54 | ii=sub2ind(szzE,xyrE(:,2),xyrE(:,1)); 55 | 56 | [a,e]=histcounts(ii,1:max(ii)-1); 57 | xyim=zeros(szzE); 58 | xyim(e(1:end-1))=a; 59 | try 60 | load([pthD,matlist(kk).name],'D'); 61 | D=imresize(D,szzE)*scaleE; 62 | xyim=imwarp(xyim,D,'nearest'); 63 | y=[];x=[]; 64 | for k=1:max(xyim(:)) 65 | [yt,xt]=find(xyim>=k); 66 | y=cat(1,y,yt);x=cat(1,x,xt); 67 | end 68 | xye=[x y]*rsim; 69 | catch 70 | disp('center image elastic') 71 | xye=xyr; 72 | end 73 | %imE=imread([pthimE,matlist(kk).name(1:end-3),'jpg']);figure(2),imshow(imE),hold on,scatter(xye(:,1)/scale,xye(:,2)/scale,'*r'); 74 | 75 | % save 12um/pixel image 76 | %volcell=cat(3,volcell,xyim); 77 | imwrite(uint8(xyim),[outpthim,matlist(kk).name(1:end-3),'tif']); 78 | save([outpth,matlist(kk).name],'xyr','xye'); 79 | disp([kk length(matlist) round(sum(xyim(:))/size(xy,1)*100)]) 80 | clearvars xyim xy cell_unreg tform cent D 81 | end 82 | %volcell=uint8(volcell); 83 | %save([outpth,'cells_12um.mat'],'volcell','-v7.3'); 84 | 85 | 86 | end 87 | 88 | 89 | function [h1,v1]=get_scale_nums(im0,v,h) 90 | imh=imresize(im0,size(im0(:,:,1)).*[1 h],'nearest'); 91 | a=size(imh,2)-size(im0,2); 92 | h1=ceil(a/2); 93 | 94 | imv=imresize(im0,size(im0(:,:,1)).*[v 1],'nearest'); 95 | a=size(imv,1)-size(im0,1); 96 | v1=ceil(a/2); 97 | 98 | end -------------------------------------------------------------------------------- /original upload version/base/register_global_im.m: -------------------------------------------------------------------------------- 1 | function imG=register_global_im(im,tform,cent,f,fillval) 2 | % set up rotation point of reference 3 | Rin=imref2d(size(im)); 4 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 5 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 6 | 7 | % flip if necessary 8 | if f==1 9 | im=im(end:-1:1,:,:); 10 | end 11 | 12 | % register 13 | imG=imwarp(im,Rin,tform,'nearest','Outputview',Rin,'Fillvalues',fillval); 14 | 15 | end 16 | 17 | 18 | -------------------------------------------------------------------------------- /original upload version/base/rescale_ims.m: -------------------------------------------------------------------------------- 1 | function rescale_ims 2 | 3 | pth='G:\Alicia Braxton\TC_90 PDAC\1x\registered_bad_scale\'; 4 | pthTA='G:\Alicia Braxton\TC_90 PDAC\1x\registered_bad_scale\TA\'; 5 | % outpth='G:\Alicia Braxton\TC_90 PDAC\1x\rescale\registered\elastic registration\save_warps\'; 6 | imlist=dir([pth,'*jpg']); 7 | 8 | nums=[198:201 220:223 362:415 416:length(imlist)]; 9 | rfs=[1.1115 1.070 1.054 1.219;1 1 1 1]; 10 | for kk=1:length(nums) 11 | im=imread([pth,imlist(nums(kk)).name]); 12 | 13 | RSC=rfs(kk); 14 | if kk<=4;RSC=rfs(:,1)';end 15 | if kk>4 && kk<=8;RSC=rfs(:,2)';end 16 | if kk>8 && kk<=62;RSC=rfs(:,3)';end 17 | if kk>62;RSC=rfs(:,4)';end 18 | 19 | im=imresize(im,RSC); 20 | 21 | imwrite(im,[pth,imlist(nums(kk)).name]); 22 | save([outpth,imlist(nums(kk)).name(1:end-3),'mat'],'RSC'); 23 | end 24 | 25 | 26 | % 362: 1.04 27 | 28 | 29 | % for kk=198:length(imlist) 30 | % imrf0=imread([pth,imlist(kk-1).name]); 31 | % immv0=imread([pth,imlist(kk).name]); 32 | % [~,immvg,~]=preprocessing(immv0,size(immv0(:,:,1)),[0 0],1); 33 | % [~,imrfg,~]=preprocessing(imrf0,size(imrf0(:,:,1)),[0 0],1); 34 | % 35 | % tic;tform=imregcorr(immvg,imrfg);toc; 36 | % Rfixed = imref2d(size(imrfg)); 37 | % im2 = imwarp(immvg,tform,'OutputView',Rfixed); 38 | % 39 | % figure, 40 | % subplot(1,2,1),imshowpair(imrfg,immvg); 41 | % subplot(1,2,2),imshowpair(imrfg,im2); 42 | % end 43 | 44 | 45 | % RSC=rfs(kk); 46 | % if kk<=4;RSC=rfs(1);end 47 | % if kk>4 && kk<=8;RSC=rfs(2);end 48 | % if kk>8 && kk<=62;RSC=rfs(3);end 49 | % if kk>62;RSC=rfs(4);end 50 | % 51 | % im=imresize(im,RSC); 52 | % 53 | % imwrite(im,[pth,imlist(nums(kk)).name]); 54 | % save([outpth,imlist(nums(kk)).name(1:end-3),'mat'],'RSC'); 55 | % end 56 | % 57 | % 58 | % 59 | % 60 | % 61 | % nums=[198:201 220:223 362:415 416:length(imlist)]; 62 | % rfs=[1.113 1.070 1.054 1.219]; 63 | % for kk=1:length(nums) 64 | % im=imread([pth,imlist(nums(kk)).name]); 65 | % 66 | % RSC=rfs(kk); 67 | % if kk<=4;RSC=rfs(1);end 68 | % if kk>4 && kk<=8;RSC=rfs(2);end 69 | % if kk>8 && kk<=62;RSC=rfs(3);end 70 | % if kk>62;RSC=rfs(4);end 71 | % 72 | % im=imresize(im,RSC); 73 | % 74 | % imwrite(im,[pth,imlist(nums(kk)).name]); 75 | % save([outpth,imlist(nums(kk)).name(1:end-3),'mat'],'RSC'); 76 | % end -------------------------------------------------------------------------------- /original upload version/base/save_images_global.m: -------------------------------------------------------------------------------- 1 | function save_images_global(pthim,pthdata,scale,padnum,tpout) 2 | if ~exist('padnum','var');pd=1;else;pd=0;end 3 | if ~exist('tpout','var');tpout='tif';end 4 | 5 | imlist1=dir([pthim,'*tif']); 6 | imlist2=dir([pthim,'*jpg']); 7 | imlist3=dir([pthim,'*jp2']); 8 | imlist=[imlist1;imlist2;imlist3]; 9 | 10 | try 11 | datafile=[pthdata,imlist(1).name(1:end-3),'mat']; 12 | load(datafile,'szz','padall'); 13 | catch 14 | datafile=[pthdata,imlist(2).name(1:end-3),'mat']; 15 | load(datafile,'szz','padall'); 16 | end 17 | padall=ceil(padall*scale); 18 | refsize=ceil(szz*scale); 19 | 20 | % determine roi and create output folder 21 | outpth=[pthim,'registeredG\']; 22 | if ~isfolder(outpth);mkdir(outpth);end 23 | 24 | % register each image and save to outpth 25 | for kz=1:length(imlist) 26 | imnm=imlist(kz).name; 27 | outnm=[imlist(kz).name(1:end-3),tpout]; 28 | 29 | %if exist([outpth,outnm],'file');continue;end 30 | disp([kz length(imlist)]);disp(imnm) 31 | datafile=[pthdata,imnm(1:end-3),'mat']; 32 | 33 | % load image 34 | IM=imread([pthim,imnm]); 35 | szim=size(IM(:,:,1)); 36 | if pd;padnum=squeeze(mode(mode(IM,2),1))';end 37 | if szim(1)>refsize(1) || szim(2)>refsize(2) 38 | a=min([szim; refsize]); 39 | IM=IM(1:a(1),1:a(2),:); 40 | end 41 | IM=pad_im_both(IM,refsize,padall,padnum); 42 | 43 | try 44 | % if not reference image, register 45 | load(datafile,'tform','cent','f'); 46 | if ~exist('f','var');f=0;end 47 | if f==1;IM=IM(end:-1:1,:,:);end 48 | IMG=register_IM(IM,tform,scale,cent,padnum); 49 | imwrite(IMG,[outpth,outnm]); 50 | clearvars tform 51 | catch 52 | % no transformation if this is reference image 53 | imwrite(IM,[outpth,outnm]); 54 | end 55 | 56 | clearvars f 57 | end 58 | end 59 | 60 | 61 | 62 | function IM=register_IM(IM,tform,scale,cent,abc) 63 | % rough registration 64 | cent=cent*scale; 65 | tform.T(3,1:2)=tform.T(3,1:2)*scale; 66 | Rin=imref2d(size(IM)); 67 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 68 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 69 | IM=imwarp(IM,Rin,tform,'nearest','outputview',Rin,'fillvalues',abc); 70 | end -------------------------------------------------------------------------------- /original upload version/base/save_inverted_Ds.m: -------------------------------------------------------------------------------- 1 | function save_inverted_Ds(pth) 2 | outpth=[pth,'Dnew\']; 3 | mkdir(outpth); 4 | imlist=dir([pth,'*mat']); 5 | 6 | 7 | for k=1:length(imlist) 8 | tic; 9 | if exist([outpth,imlist(k).name],'file');continue;end 10 | if contains(imlist(k).name,'CD');continue;end % skip IHC for now 11 | 12 | % crop and resize if desired 13 | load([pth,imlist(k).name],'D'); 14 | rr=round([929 35 2487 2019].*1.5); 15 | D2=D(rr(2):rr(2)+rr(4),rr(1):rr(1)+rr(3),:); 16 | Dnew2=invert_D(D2); 17 | 18 | % put back in original size and position 19 | Dnew=zeros(size(D)); 20 | Dnew(rr(2):rr(2)+rr(4),rr(1):rr(1)+rr(3),:)=Dnew2; 21 | 22 | save([outpth,imlist(k).name],'Dnew'); 23 | disp([k length(imlist) round(toc/60)]); 24 | clearvars D Dnew 25 | end -------------------------------------------------------------------------------- /original upload version/base/train_deeplab.m: -------------------------------------------------------------------------------- 1 | function train_deeplab(pth,classes,sz,classNames) 2 | disp(' starting training') 3 | nmim='im\'; % nmim='im_blank\'; 4 | nmlabel='label\'; 5 | 6 | pthTrain=[pth,'training\']; 7 | pthVal=[pth,'validation\']; 8 | % pthTest=[pth,'testing\']; 9 | 10 | % 1 make training data 11 | TrainHE=[pthTrain,nmim]; 12 | Trainlabel=[pthTrain,nmlabel]; 13 | imdsTrain = imageDatastore(TrainHE); 14 | pxdsTrain = pixelLabelDatastore(Trainlabel,classNames,classes); 15 | pximdsTrain = pixelLabelImageDatastore(imdsTrain,pxdsTrain); %'DataAugmentation',augmenter); 16 | tbl = countEachLabel(pxdsTrain); 17 | 18 | % make validation data 19 | ValHE=[pthVal,nmim]; 20 | Vallabel=[pthVal,nmlabel]; 21 | imdsVal = imageDatastore(ValHE); 22 | pxdsVal = pixelLabelDatastore(Vallabel,classNames,classes); 23 | pximdsVal = pixelLabelImageDatastore(imdsVal,pxdsVal); %'DataAugmentation',augmenter); 24 | 25 | % make testing data 26 | % TestHE=[pthTest,nmim]; 27 | % Testlabel=[pthTest,nmlabel]; 28 | % imdsTest = imageDatastore(TestHE); 29 | % pxdsTest = pixelLabelDatastore(Testlabel,classNames,classes); 30 | % pximdsTest = pixelLabelImageDatastore(imdsTest,pxdsTest); %'DataAugmentation',augmenter); 31 | 32 | 33 | % visualize pixel counts by class 34 | % frequency = tbl.PixelCount/sum(tbl.PixelCount); 35 | % figure(132),bar(1:numel(classes),frequency),title('distribution of class labels') 36 | % xticks(1:numel(classes)) 37 | % xticklabels(tbl.Name) 38 | % xtickangle(45) 39 | % ylabel('Frequency') 40 | 41 | 42 | options = trainingOptions('adam',... % stochastic gradient descent solver 43 | 'MaxEpochs',8,... 44 | 'MiniBatchSize',4,... % datapoints per 'mini-batch' - ideally a small power of 2 (32, 64, 128, or 256) 45 | 'Shuffle','every-epoch',... % reallocate mini-batches each epoch (so min-batches are new mixtures of data) 46 | 'ValidationData',pximdsVal,... 47 | 'ValidationPatience',6,... % stop training when validation data doesn't improve for __ iterations 5 48 | 'InitialLearnRate',0.0005,... % 'InitialLearnRate',0.0005,... 49 | 'LearnRateSchedule','piecewise',... % drop learning rate during training to prevent overfitting 50 | 'LearnRateDropPeriod',1,... % drop learning rate every _ epochs 51 | 'LearnRateDropFactor',0.75,... % multiply learning rate by this factor to drop it 52 | 'ValidationFrequency',128,... % initial loss should be -ln( 1 / # classes ) 53 | 'ExecutionEnvironment','gpu',... % train on gpu 54 | 'Plots','training-progress');%,... % view progress while training 55 | % 'OutputFcn', @(info)savetrainingplot(info,outpth)); % save training progress as image 56 | 57 | % Design network 58 | numclass = numel(classes); 59 | imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount; 60 | classWeights = median(imageFreq) ./ imageFreq; 61 | 62 | lgraph = deeplabv3plusLayers([sz sz 3],numclass,"resnet50"); 63 | pxLayer = pixelClassificationLayer('Name','labels','Classes',tbl.Name,'ClassWeights',classWeights); 64 | lgraph = replaceLayer(lgraph,"classification",pxLayer); 65 | % lgraph=make_CNN_layers_deeplab(sz,numclass,tbl,classWeights); 66 | 67 | % lgraph = deeplabv3plusLayers([sz sz 3], numclass, "resnet101"); 68 | % pxLayer = pixelClassificationLayer('Name','labels','Classes',tbl.Name,'ClassWeights',classWeights); 69 | % lgraph = replaceLayer(lgraph,"ClassificationLayer_predictions",pxLayer); 70 | 71 | % train 72 | [net, info] = trainNetwork(pximdsTrain,lgraph,options); 73 | save([pth,'net.mat'],'net','info'); 74 | 75 | % I = readimage(imdsTrain,35); 76 | % L = readimage(pxdsTrain,35); 77 | % figure, 78 | % subplot(1,2,1),imshow(I) 79 | % subplot(1,2,2),imagesc(double(L));axis equal;axis off 80 | % test on one image 81 | % I = readimage(imdsTest,35); 82 | % C = semanticseg(I, net); 83 | 84 | % cmap=[121 248 252;... % 1 islet 85 | % 0 0 255;... % 2 duct 86 | % 80 237 80;... % 3 blood vessel 87 | % 255 255 0;... % 4 fat 88 | % 149 35 184;... % 5 acinus 89 | % 255 194 245;... % 6 connective tissue 90 | % 255 255 255;... % 7 whitespace 91 | % 255 0 0;... % 8 PanIN 92 | % 255 255 255]/255; % 9 noise 93 | % 94 | % B = labeloverlay(I,C,'Colormap',cmap,'Transparency',0.4); 95 | % figure(651),imshow(B) 96 | % make_pixel_colorbar(cmap,classes); 97 | % 98 | % expectedResult = readimage(pxdsTest,35); 99 | % actual = uint8(C); 100 | % figure(183) 101 | % subplot(1,2,1),imshow(I) 102 | % subplot(1,2,2),imagesc(actual),axis equal;axis off 103 | 104 | 105 | end 106 | 107 | function stop=savetrainingplot(info,pthSave) 108 | stop=false; %prevents this function from ending trainNetwork prematurely 109 | if info.State=='done' %check if all iterations have completed 110 | saveas(findall(groot, 'Type', 'Figure'),[pthSave,'training_process.png']) 111 | end 112 | 113 | 114 | end 115 | -------------------------------------------------------------------------------- /original upload version/base/xcorrf2.m: -------------------------------------------------------------------------------- 1 | function c = xcorrf2(a,b,pad) 2 | % c = xcorrf2(a,b) 3 | % Two-dimensional cross-correlation using Fourier transforms. 4 | % XCORRF2(A,B) computes the crosscorrelation of matrices A and B. 5 | % XCORRF2(A) is the autocorrelation function. 6 | % This routine is functionally equivalent to xcorr2 but usually faster. 7 | % See also XCORR2. 8 | 9 | % Author(s): R. Johnson 10 | % $Revision: 1.0 $ $Date: 1995/11/27 $ 11 | 12 | if nargin<3 13 | pad='yes'; 14 | end 15 | 16 | 17 | [ma,na] = size(a); 18 | if nargin == 1 19 | % for autocorrelation 20 | b = a; 21 | end 22 | [mb,nb] = size(b); 23 | % make reverse conjugate of one array 24 | b = conj(b(mb:-1:1,nb:-1:1)); 25 | 26 | if strcmp(pad,'yes'); 27 | % use power of 2 transform lengths 28 | mf = 2^nextpow2(ma+mb); 29 | nf = 2^nextpow2(na+nb); 30 | at = fft2(b,mf,nf); 31 | bt = fft2(a,mf,nf); 32 | elseif strcmp(pad,'no'); 33 | at = fft2(b); 34 | bt = fft2(a); 35 | else 36 | disp('Wrong input to XCORRF2'); return 37 | end 38 | 39 | % multiply transforms then inverse transform 40 | c = ifft2(at.*bt); 41 | % make real output for real input 42 | if ~any(any(imag(a))) & ~any(any(imag(b))) 43 | c = real(c); 44 | end 45 | % trim to standard size 46 | 47 | if strcmp(pad,'yes'); 48 | c(ma+mb:mf,:) = []; 49 | c(:,na+nb:nf) = []; 50 | elseif strcmp(pad,'no'); 51 | c=fftshift(c(1:end-1,1:end-1)); 52 | 53 | % c(ma+mb:mf,:) = []; 54 | % c(:,na+nb:nf) = []; 55 | end 56 | -------------------------------------------------------------------------------- /original upload version/make_training_deeplab.m: -------------------------------------------------------------------------------- 1 | path(path,'base'); 2 | 3 | warning ('off','all'); 4 | pth='\'; % path to annotations 5 | umpix=1; % um/pixel of images used % 1=10x, 2=5x, 4=16x 6 | pthim='\'; % path to tif images to classify 7 | nm='06_17_2022'; % today's date 8 | % classes 9 | % 1 diseased 10 | % 2 non-diseased tissue 11 | % 3 non-tissue 12 | cmap=[192 123 224;... % 1 diseased 13 | 224 211 123;... % non-diseased tissue 14 | 255 255 255]; % 3 whtespace 15 | classNames = ["diseased" "nondiseased" "whitespace" "black"]; 16 | 17 | % define actions to take per annotation class 18 | WS{1}=[]; % remove whitespace if 0, keep only whitespace if 1, keep both if 2 19 | WS{2}=; % add removed whitespace to this class 20 | WS{3}=[]; % rename classes accoring to this order 21 | WS{4}=[]; % reverse priority of classes 22 | WS{5}=[]; % delete classes 23 | numclass=length(unique(WS{3})); 24 | sxy=700; 25 | pthDL=[pth,nm,'\']; 26 | nblack=numclass+1; 27 | 28 | %% load and format annotations for each image 29 | 30 | % for each annotation file 31 | imlist=dir([pth,'*xml']); 32 | numann0=[];ctlist=[]; 33 | for kk=1:length(imlist) 34 | % set up names 35 | imnm=imlist(kk).name(1:end-4);tic; 36 | disp(['Image ',num2str(kk),' of ',num2str(length(imlist)),': ',imnm]) 37 | outpth=[pth,'data\',imnm,'\']; 38 | if ~exist(outpth,'dir');mkdir(outpth);end 39 | matfile=[outpth,'annotations.mat']; 40 | 41 | % skip if file hasn't been updated since last load 42 | dm='';bb=0;date_modified=imlist(kk).date; 43 | if exist(matfile,'file');load(matfile,'dm','bb');end 44 | if contains(dm,date_modified) && bb==1 45 | disp(' annotation data previously loaded') 46 | load([outpth,'annotations.mat'],'numann','ctlist0'); 47 | numann0=[numann0;numann];ctlist=[ctlist;ctlist0]; 48 | continue; 49 | end 50 | 51 | % 1 read xml annotation files and saves as mat files 52 | load_xml_file(outpth,[pth,imnm,'.xml'],date_modified); 53 | 54 | % 2 fill annotation outlines and delete unwanted pixels 55 | [I,TA,pthTA]=calculate_tissue_space_082(pthim,imnm); 56 | J0=fill_annotations_file(I,outpth,WS,umpix,TA); 57 | %J=edit_individual_classes_mouse_gut(J0,[outpth,'view_annotations.tif']); 58 | %figure,imshowpair(uint8(I),J) 59 | % 3 make bounding boxes of all annotations 60 | [numann,ctlist0]=annotation_bounding_boxes(I,outpth,nm,numclass); 61 | numann0=[numann0;numann];ctlist=[ctlist;ctlist0]; 62 | toc; 63 | end 64 | 65 | %% combine tiles 66 | numann=numann0; 67 | % make training tiles 68 | ty='training\';obg=[pthDL,ty,'big_tiles\']; 69 | while length(dir([obg,'*tif']))<18 70 | numann=combine_tiles_big(numann0,numann,ctlist,nblack,pthDL,ty,sxy,1); 71 | end 72 | % make validation tiles 73 | ty='validation\';obg=[pthDL,ty,'big_tiles\']; 74 | while length(dir([obg,'*tif']))<4 75 | numann=combine_tiles_big(numann0,numann,ctlist,nblack,pthDL,ty,sxy,1); 76 | end 77 | 78 | %% build model 79 | train_deeplab(pthDL,1:numclass+1,sxy,classNames); 80 | 81 | % classify 82 | deeplab_classification(pthim,pthDL,sxy,nm,cmap,nblack,WS{2},1); 83 | 84 | -------------------------------------------------------------------------------- /original upload version/normalize_HE.m: -------------------------------------------------------------------------------- 1 | function normalize_HE(pth,pthl,outpth) 2 | warning ('off','all'); 3 | path(path,'\\motherserverdw\ashleysync\PanIN Modelling Package\'); 4 | path(path,'\\motherserverdw\ashleysync\PanIN Modelling Package\IHC segmentation\'); 5 | % add in histogram equalization to make outputs better match 6 | 7 | if ~exist('pthl','var');pthl=pth;end 8 | if ~exist('outpth','var');outpth=[pth,'fix stain\'];end 9 | outpthC=[outpth,'CVS\']; 10 | outpthH=[outpth,'Hchannel\']; 11 | mkdir(outpth);mkdir(outpthC);mkdir(outpthH);%mkdir(outpthE); 12 | 13 | 14 | tic; 15 | imlist=dir([pth,'*tif']);if isempty(imlist);imlist=dir([pth,'*jpg']);end 16 | knum=150000; 17 | CVS=[0.644 0.717 0.267;0.093 0.954 0.283;0.636 0.001 0.771]; 18 | 19 | % CVS=[0.578 0.738 0.348;... 20 | % 0.410 0.851 0.328;... 21 | % 0.588 0.625 0.514]; 22 | % CVS=[0.5157 0.7722 0.3712 23 | % 0.3519 0.8409 0.4112 24 | % 0.5662 0.1935 0.6088]; 25 | for kk=1:length(imlist) 26 | imnm=imlist(kk).name; 27 | if exist([outpthH,imnm],'file');disp(['skip ',num2str(kk)]);continue;end 28 | disp([num2str(kk), ' ', num2str(length(imlist)), ' ', imnm(1:end-4)]); 29 | 30 | im0=imread([pth,imnm]); 31 | % [CVS,TA]=make_rgb_kmeans_90(im0,knum,0); 32 | %CVS=[0.759 0.587 0.280;0.673 0.630 0.387;0.763 0.001 0.646]; % im1 33 | %save([outpthC,imnm(1:end-3),'mat'],'CVS'); 34 | 35 | [imout,imH,imE]=colordeconv2pw4_log10(im0,"he",CVS); 36 | %load([outpthC,imlist(1).name(1:end-3),'mat'],'CVS');ODout=CVS; 37 | 38 | %figure(3),imshow(imH) 39 | imwrite(uint8(imH),[outpthH,imnm]); 40 | disp([kk length(imlist)]) 41 | end 42 | warning ('off','all'); 43 | end 44 | 45 | -------------------------------------------------------------------------------- /original upload version/plot_3D_tiss.m: -------------------------------------------------------------------------------- 1 | function plot_3D_tiss(volbody,map,sx,sz,FA,crop,I,shiftxyz) 2 | % volbody = 3D binary matrix of object 3 | % map = rgb color of object (for example, [1 0 0] = red, [0 1 0]=green, [0 0 1]=blue) 4 | % sx = xy resolution (um/pixel) 5 | % sz = z resolution (um/pixel) 6 | if ~exist('sx','var');sx=1;end 7 | if ~exist('sz','var');sz=1;end 8 | if ~exist('crop','var');crop=1;end 9 | if ~exist('map','var');map=[0 0 1];end 10 | if ~exist('FA','var');FA=0.8;end %opacity 1 .5 half invisible test 11 | if ~exist('shiftxyz','var');shiftxyz=[0 0 0];end 12 | ss=0;caps=1; 13 | if exist('I','var') && ~isempty(I);plotim=1;else;plotim=0;end 14 | if max(map(:))>1;map=double(map)/255;end 15 | 16 | 17 | if crop 18 | if plotim;I=imresize(I,size(volbody(:,:,1)));end 19 | 20 | tmp=squeeze(sum(sum(volbody,1),2))>0; 21 | zz=[find(tmp>0,1,'first') find(tmp>0,1,'last')]; 22 | tmp=sum(volbody,3); 23 | yy=[find(sum(tmp,1)>0,1,'first') find(sum(tmp,1)>0,1,'last')]; 24 | xx=[find(sum(tmp,2)>0,1,'first') find(sum(tmp,2)>0,1,'last')]; 25 | shiftxyz(1)=shiftxyz(1)+yy(1)-1; 26 | shiftxyz(2)=shiftxyz(2)+xx(1)-1; 27 | shiftxyz(3)=shiftxyz(3)+zz(1)-1; 28 | volbody=volbody(xx(1):xx(2),yy(1):yy(2),zz(1):zz(2)); 29 | 30 | if plotim;I=I(xx(1):xx(2),yy(1):yy(2),:);end 31 | end 32 | 33 | if plotim 34 | imagesc([0 size(volbody,2)*sx/10000], [0 size(volbody,1)*sx/10000],I); %plotHE 8bit 35 | end 36 | 37 | vt=volbody(:,:,1); 38 | vb=volbody(:,:,end); 39 | szz=size(volbody); 40 | 41 | if sx==1 && sz==1 42 | patch(isosurface(volbody),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars volbody 43 | voltop=zeros(szz);voltop(:,:,1)=vt; 44 | patch(isosurface(voltop),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars voltop 45 | volbot=zeros(szz);volbot(:,:,end)=vb; 46 | patch(isosurface(volbot),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars volbot 47 | else 48 | [xg,yg,zg]=meshgrid(1:size(volbody,2),1:size(volbody,1),1:size(volbody,3)); 49 | xg=(xg+shiftxyz(1))*sx/10000; 50 | yg=(yg+shiftxyz(2))*sx/10000; 51 | zg=(zg+shiftxyz(3))*sz/10000; 52 | patch(isosurface(xg,yg,zg,volbody),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars volbody 53 | 54 | if caps 55 | voltop=zeros(szz);voltop(:,:,1)=vt; 56 | patch(isosurface(xg,yg,zg,voltop),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars voltop 57 | volbot=zeros(szz);volbot(:,:,end)=vb; 58 | patch(isosurface(xg,yg,zg,volbot),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars volbot 59 | end 60 | 61 | if ss 62 | volss=zeros(szz);volss(1,:,:)=volbody(1,:,:); 63 | patch(isosurface(xg,yg,zg,volss),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars voltop 64 | 65 | volss=zeros(szz);volss(end,:,:)=volbody(end,:,:); 66 | patch(isosurface(xg,yg,zg,volss),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars voltop 67 | 68 | volss=zeros(szz);volss(:,1,:)=volbody(:,1,:); 69 | patch(isosurface(xg,yg,zg,volss),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars voltop 70 | 71 | volss=zeros(szz);volss(:,end,:)=volbody(:,end,:); 72 | patch(isosurface(xg,yg,zg,volss),'FaceColor',map,'EdgeColor','none','FaceAlpha',FA);clearvars voltop 73 | end 74 | end 75 | plot_settings(1); 76 | % 77 | % if sx==1 && sz==1 78 | % xlim([0 size(volbody,2)]) 79 | % ylim([0 size(volbody,1)]) 80 | % zlim([0 size(volbody,3)]) 81 | % else 82 | % xlim([min(xg(:)) max(xg(:))]) 83 | % ylim([min(yg(:)) max(yg(:))]) 84 | % zlim([min(zg(:)) max(zg(:))]) 85 | % end 86 | end 87 | 88 | % 89 | % cmap=[121 248 252;... % 1 islet 90 | % 0 0 255;... % 2 duct 91 | % 80 237 80;... % 3 blood vessel 92 | % 255 255 0;... % 4 fat 93 | % 149 35 184;... % 5 acinus 94 | % 255 194 245;... % 6 connective tissue 95 | % 255 255 255;... % 7 whitespace 96 | % 255 0 0;... % 8 PanIN 97 | % 240 159 10;... % 9 PDAC 98 | % 0 0 0]/255; %10 endothelium 99 | % cc=cmap(5,:)*0.5+cmap(6,:)*0.5; 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /original upload version/register_cell_coordinates.m: -------------------------------------------------------------------------------- 1 | function register_cell_coordinates_2022(pth0,pthcoords,scale) 2 | % pth0 = path containing images where registration was calculated 3 | % pthcoords = path to cell coordinates 4 | % scale = scale between coordinate images and registration images (>1) 5 | 6 | % define paths using pth0 7 | pthimG=[pth0,'registered\']; 8 | pthimE=[pthimG,'elastic registration\']; 9 | outpth=[pthimE,'cell_coordinates_registered\'];mkdir(outpth); 10 | datapth=[pthimE,'save_warps\']; 11 | pthD=[datapth,'D\']; 12 | 13 | % set up padding information and find registered images 14 | matlist=dir([pthcoords,'*mat']); 15 | if exist([pthimE,matlist(1).name(1:end-4),'.jpg'],'file');tp='.jpg';tp2='.tif';else;tp='.tif';tp2='.jpg';end 16 | load([datapth,matlist(1).name],'padall','szz'); 17 | szz2=szz+(2*padall); 18 | szz3=round(szz2*scale); 19 | pad2=1; 20 | 21 | % register coordinates for each image 22 | for kk=1:length(matlist) 23 | if ~exist([pthD,matlist(kk).name],'file');continue;end 24 | load([pthcoords,matlist(kk).name],'xy'); 25 | if isempty(xy) || ~isfile([pth0,matlist(kk).name(1:end-4),'.tif']);continue;end 26 | xy=xy/scale; 27 | 28 | % check unregistered image 29 | % if kk==1 30 | % try im0=imread([pth0,matlist(kk).name(1:end-4),tp]);catch im0=imread([pth0,matlist(kk).name(1:end-4),tp2]);end 31 | % figure(31),imshow(im0);hold on;scatter(xy(:,1),xy(:,2),'*y');hold off 32 | % end 33 | 34 | % rough register points 35 | xy=xy+padall; 36 | if pad2==1 37 | a=imfinfo([pth0,matlist(kk).name(1:end-4),'.tif']);a=[a.Height a.Width]; 38 | szim=[szz(1)-a(1) szz(2)-a(2)];szA=floor(szim/2);szA=[szA(2) szA(1)]; 39 | xy=xy+szA; 40 | end 41 | try 42 | load([datapth,matlist(kk).name],'tform','cent','szz'); 43 | xyr = transformPointsForward(tform,xy-cent); 44 | xyr=xyr+cent; 45 | cc=min(xyr,[],2)>1 & xyr(:,1)refsize(1) || szim(2)>refsize(2) 40 | a=min([szim; refsize]); 41 | IM=IM(1:a(1),1:a(2),:); 42 | end 43 | IM=pad_im_both2(IM,refsize,padall,padnum); 44 | 45 | 46 | % if not reference image, register 47 | try 48 | load(datafileE,'tform','cent','f'); 49 | if f==1;IM=IM(end:-1:1,:,:);end 50 | IMG=register_IM(IM,tform,scale,cent,padnum); 51 | 52 | load(datafileD,'D'); 53 | D2=imresize(D,size(IM(:,:,1))); 54 | D2=D2.*scale; 55 | IME=imwarp(IMG,D2,'nearest','FillValues',padnum); 56 | % no transformation if this is reference image 57 | catch 58 | IME=IM; 59 | end 60 | 61 | if kz==1 62 | pth1=[pthdata,'..\']; 63 | try 64 | im=imread([pth1,matlist(kz).name(1:end-3),'jpg']); 65 | catch 66 | im=imread([pth1,matlist(kz).name(1:end-3),'tif']); 67 | end 68 | im2=imresize(IME,size(im(:,:,1)),'nearest'); 69 | figure,imshowpair(im,im2);pause(2); 70 | end 71 | 72 | %rr=[11112 3083 27452 24046]; 73 | %rr=[8280 4817 32014 22462]; 74 | %rr=[9139 3097 37968 26544]; 75 | %IME=imcrop(IME,rr); 76 | imwrite(IME,[outpth,outnm]); 77 | disp([kz length(imlist)]);%disp(imnm) 78 | clearvars tform rsc cent D 79 | end 80 | 81 | end 82 | 83 | function IM=register_IM(IM,tform,scale,cent,abc) 84 | % rough registration 85 | cent=cent*scale; 86 | tform.T(3,1:2)=tform.T(3,1:2)*scale; 87 | Rin=imref2d(size(IM)); 88 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 89 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 90 | IM=imwarp(IM,Rin,tform,'nearest','outputview',Rin,'fillvalues',abc); 91 | end -------------------------------------------------------------------------------- /update 12-13-2023/README.txt: -------------------------------------------------------------------------------- 1 | CODA is a computational pipeline for creation of 3D tissue and cellular 2 | labelled datasets from serial histological datasets. 3 | First published in Kiemen et al (nature methods, 2022) 4 | https://www.nature.com/articles/s41592-022-01650-9 5 | 6 | This package enables: 7 | 1. serial image registration 8 | 2. deep learning segmenation of structures recognizable in biological images 9 | 3. nuclear coordinate generation and validation 10 | 4. 3D reconstruction of tissue and cellular matrices 11 | 12 | For a detailed guide and companion sample dataset, please see the Kiemen lab website: 13 | https://labs.pathology.jhu.edu/kiemen/coda-3d/ 14 | 15 | Provided below is a brief shorthand for application of CODA. 16 | 17 | 1. To downsample ndpi or svs images to 10x, 5x, and 1x tifs: 18 | create_downsampled_tif_images() 19 | or try Openslide in python 20 | 21 | 2. To calculate registration on the low resolution (1x) images 22 | Calculate the tissue area and background pixels: 23 | calculate_tissue_ws() 24 | 25 | Calculate the registration transforms: 26 | calculate_image_registration() 27 | 28 | 3. To build a 3D tissue volume using sematic segmentation: 29 | First, generate manual annotations in Aperio imagescope 30 | 31 | Second, apply the deep learning function to train a model and segment the high resolution (5x or 10x) images: 32 | train_image_segmentation() 33 | 34 | To apply the registration to segmented images: 35 | apply_image_registration() 36 | 37 | To build a 3D tissue matrix from registered, classified images: 38 | build_tissue_volume() 39 | 40 | 4. To build a 3D cell volume containing nuclear coordinates: 41 | Build a mosaic image containing regions of many whole-slide images for cell detection optimization: 42 | make_cell_detection_mosaic() 43 | 44 | Manually annotate the mosaic image to get the ‘ground-truth’ number of cell nuclei: 45 | manual_cell_count() 46 | 47 | Determine cell detection parameters using the manual annotations on the mosaic image: 48 | get_nuclear_detection_parameters() 49 | 50 | Deconvolve the high-resolution (5x or 10x) H&E images before applying the cell detection algorithm: 51 | deconvolve_histological_images() 52 | 53 | Detect cells on the hematoxylin channel of the high-resolution images: 54 | cell_detection() 55 | 56 | Apply the registration to the cell coordinates: 57 | register_cell_coordinates() 58 | 59 | Build a 3D cell coordinate matrix corresponding to the 3D tissue matrix: 60 | build_cell_volume() 61 | -------------------------------------------------------------------------------- /update 12-13-2023/build_cell_volume.m: -------------------------------------------------------------------------------- 1 | function build_cell_volume(pthclassifiedE,pthcoordsE,pthvolume,scale,nwhite) 2 | % creates a volumetric matrix 'volcell' the same size as vol that contains nuclear coordinates 3 | % REQUIRED INPUTS: 4 | % pthclassifiedE: the folder containing the registered, classified images 5 | % pthcoordsE: the folder containing the registered cell coordinates 6 | % pthvolume: the folder containing the tissue matrix 'vol' 7 | % scale: the scale between classified images and images used for cell detection, this is probably 1 8 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 9 | % please cite Kiemen et al, Nature methods (2022) 10 | % last updated in December 2023 by ALK 11 | 12 | if pthclassifiedE(end)~='\';pthclassifiedE=[pthclassifiedE,'\'];end 13 | if pthcoordsE(end)~='\';pthcoordsE=[pthcoordsE,'\'];end 14 | if pthvolume(end)~='\';pthvolume=[pthvolume,'\'];end 15 | 16 | % get crop and subsampling information from tissue matrix 17 | load([pthvolume,'volume.mat'],'rr','sk'); 18 | warning ('off','all'); 19 | 20 | if exist([pthvolume,'volcell.mat'],'file') 21 | disp(' VOLCELL ALREADY MADE'); 22 | else 23 | list=dir([pthcoordsE,'*.mat']); 24 | 25 | % get size of volcell 26 | nm=list(1).name; 27 | nmim=strrep(nm,'.mat','.tif'); 28 | a=imfinfo([pthclassifiedE,strrep(nm,'mat','tif')]); 29 | a=[a.Height a.Width]; 30 | im00=imread([pthclassifiedE,nmim],'PixelRegion',{[1 sk a(1)],[1 sk a(2)]}); 31 | im00=zeros(size(im00)); 32 | im0=imcrop(im00,rr); 33 | 34 | volcell=uint8(zeros([size(im0) length(list)])); 35 | for k=1:length(list) 36 | nm=list(k).name; 37 | load([pthcoordsE,nm],'xye'); 38 | xye=round(xye/scale/sk); 39 | x=xye(:,1);y=xye(:,2); 40 | kp=x>0 & y>0 & x<=size(im00,1) & y10; 39 | 40 | % remove large black objects (these are probably shadows or dust 41 | black_line=imclose(std(im,[],3)<5 & rgb2gray(im0)<160,strel('disk',2)); 42 | TA=TA & ~black_line; 43 | TA=imclose(TA,strel('disk',4)); 44 | else 45 | TA=im(:,:,2)<210; 46 | TA=imclose(TA,strel('disk',4)); 47 | TA=bwareaopen(TA,10); 48 | end 49 | TA=imfill(TA,'holes'); 50 | 51 | % remove objects that are less than 1/10 largest object size 52 | TA=bwlabel(TA); 53 | N=histcounts(TA(:),max(TA(:))+1); 54 | N(1)=0; 55 | N(N<(max(N)/20))=0; 56 | N(N>0)=1; 57 | TA=N(TA+1); 58 | 59 | imwrite(TA,[outpth,nm]); 60 | disp(' done'); 61 | 62 | end 63 | -------------------------------------------------------------------------------- /update 12-13-2023/cell_detection.m: -------------------------------------------------------------------------------- 1 | function cell_detection(pth,pthparams) 2 | % this function will calculate cellular coordinates for all images in pth 3 | % given the optimization parameters saved in pthparams 4 | % REQUIRED INPUTS: 5 | % pth: pth containing color-deconvolved channel of histological images 6 | % pthparams: subfolder containing the optimized 'mb' and 'sz' variables 7 | % Written in 2019 by Ashley Lynn Kiemen, Johns Hopkins University 8 | % please cite Kiemen et al, Nature methods (2022) 9 | % last updated in December 2023 by ALK 10 | 11 | path(path,'cell_detection_base_functions'); % for getting DAB stain from IHC images 12 | 13 | if pth(end)~='\';pth=[pth,'\'];end 14 | if pthparams(end)~='\';pthparams=[pthparams,'\'];end 15 | outpth=[pth,'cell_coords\']; 16 | if ~isfolder(outpth);mkdir(outpth);end 17 | 18 | imlist=dir([pth,'*tif']); 19 | if isempty(imlist);imlist=dir([pth,'*jpg']);end 20 | 21 | % load optimized cell detection settings 22 | load([pthparams,'optimized_params.mat'],'mb','sz'); 23 | 24 | tic; 25 | for kk=1:length(imlist) 26 | imnm=imlist(kk).name; 27 | disp(['detecting cells in image ',num2str(kk),' of ',num2str(length(imlist)),': ',imnm]) 28 | if exist([outpth,imnm(1:end-3),'mat'],'file');disp(' already calculated');continue;end 29 | 30 | % count cells 31 | imH=imread([pth,imnm]); 32 | imH=imH(:,:,1); 33 | ii=imH;ii=ii(ii~=0);imH(imH==0)=mode(ii); 34 | imH=255-imH; 35 | 36 | imB=imgaussfilt(imH,1); 37 | xy=pkfndW(double(imB),mb,sz); % minimum brightness, size of object 38 | %figure(2),imshow(255-imH);axis equal;hold on;plot(xy(:,1),xy(:,2),'ro');hold off; 39 | disp(' done') 40 | 41 | 42 | save([outpth,imnm(1:end-3),'mat'],'xy','mb','sz'); 43 | end 44 | 45 | end 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /update 12-13-2023/cell_detection_base_functions/calculate_optimal_vals.m: -------------------------------------------------------------------------------- 1 | function [mb,sz,xya,TP,FP,FN]=calculate_optimal_vals(imB,mbs,szs,xym) 2 | % determines optimal minimum brightness and object size to match manual 3 | % coordinates to automatic coordinates 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | if length(mbs)>1 9 | sz=szs; 10 | vals=zeros([length(mbs) 3]); 11 | for count=1:length(mbs) 12 | mb=mbs(count); 13 | xya=pkfndW(double(imB),mb,sz); 14 | [xmatch,xaut,xman,~]=cell_cell_dist(xya,xym); 15 | 16 | TP=size(xmatch,1)/size(xym,1);% true positive 17 | FP=size(xaut,1)/size(xym,1); % false positive 18 | FN=size(xman,1)/size(xym,1); % false negative 19 | vals(count,:)=[TP FP FN]; 20 | end 21 | 22 | % find brightness that maximizes TP and minimizes FP + FN 23 | vals2=[1-vals(:,1) vals(:,[2 3])]; 24 | vals2=[vals2(:,[2 3]) std(vals2,[],2)/3]; % add a variable for variation between TP, FP, and FN 25 | vals2=sum(vals2,2); 26 | ii=find(vals2==min(vals2),1,'first'); 27 | mb=mbs(ii); 28 | 29 | % get final output values 30 | TP=vals(ii,1); 31 | FP=vals(ii,2); 32 | FN=vals(ii,3); 33 | xya=pkfndW(double(imB),mb,sz); 34 | else 35 | mb=mbs; 36 | vals=zeros([length(szs) 3]); 37 | for count=1:length(szs) 38 | sz=szs(count); 39 | xya=pkfndW(double(imB),mb,sz); 40 | [xmatch,xaut,xman,~]=cell_cell_dist(xya,xym); 41 | 42 | TP=size(xmatch,1)/size(xym,1);% true positive 43 | FP=size(xaut,1)/size(xym,1);% false positive 44 | FN=size(xman,1)/size(xym,1);% false negative 45 | vals(count,:)=[TP FP FN]; 46 | end 47 | 48 | % find brightness that maximizes TP and minimizes FP + FN 49 | vals2=[1-vals(:,1) vals(:,[2 3])]; % invert true positives 50 | vals2=[vals2(:,[2 3]) std(vals2,[],2)]; % add a variable for variation between TP, FP, and FN 51 | vals2=sum(vals2,2); 52 | ii=find(vals2==min(vals2),1,'first'); 53 | sz=szs(ii); 54 | 55 | % get final output values 56 | TP=vals(ii,1); 57 | FP=vals(ii,2); 58 | FN=vals(ii,3); 59 | xya=pkfndW(double(imB),mb,sz); 60 | end 61 | -------------------------------------------------------------------------------- /update 12-13-2023/cell_detection_base_functions/cell_cell_dist.m: -------------------------------------------------------------------------------- 1 | function [xmatch,x1nomatch,x2nomatch,ii]=cell_cell_dist(x10,x20,ct) 2 | % determines matching and non-matching coordinates for 2 sets of xy 3 | % coordinates and a minimum distance (in pixels) for cells to 'match' 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | if ~exist('ct','var');ct=20;end 9 | x1=permute(x10,[1 3 2]); 10 | x2=permute(x20,[3 1 2]); 11 | x1=double(repmat(x1,[1 size(x2,2) 1])); 12 | x2=double(repmat(x2,[size(x1,1) 1 1])); 13 | 14 | % get index of cell in list 1 closest to cells in list 2 15 | dist=sqrt(sum((x1-x2).^2,3)); 16 | %[m,dd]=min(dist,[],2); 17 | 18 | xmatch=[]; 19 | x2nomatch=[]; 20 | ii=[]; 21 | for bb=1:size(x20,1) 22 | 23 | distbb=dist(:,bb); % distance from cell bb to all cells in other image 24 | nums=find(distbb==min(distbb),1,'first'); % find closest cell 25 | 26 | if distbb(nums)>ct % no cell within distance of cell we want 27 | x2nomatch=[x2nomatch;x20(bb,:)]; 28 | elseif isempty(distbb) 29 | x2nomatch=[x2nomatch;x20(bb,:)]; 30 | else 31 | xmatch=[xmatch;x20(bb,:)]; 32 | dist(nums,:)=ct+1; % make matched cell unavailable 33 | end 34 | 35 | end 36 | m=sum(dist,2); 37 | mm=(ct+1)*size(dist,2); 38 | nmatch=find(m~=mm); 39 | x1nomatch=x10(nmatch,:); 40 | 41 | end -------------------------------------------------------------------------------- /update 12-13-2023/cell_detection_base_functions/colordeconv_log10.m: -------------------------------------------------------------------------------- 1 | function [chgi,imH,imE,imbg,imboth]=colordeconv_log10(sampleRGB,assignedOD) 2 | % sample RGB is input image 3 | % abs == 3x3 matrix. 4 | % each row is rgb absorbance for 1 of 3 channels to deconvolve 5 | % OD = -log((intenisty+1)/256); 6 | % intensity= 256*exp(-OD)-1 7 | 8 | defaultcolor=assignedOD; 9 | 10 | 11 | He = defaultcolor(1,:)'; 12 | Eo = defaultcolor(2,:)'; 13 | BG = defaultcolor(3,:)'; 14 | sampleRGB=single(sampleRGB); 15 | colormix=sampleRGB(:,:,1)+256*sampleRGB(:,:,2)+256^2*sampleRGB(:,:,3); 16 | 17 | [uniquecolors,~,u3]=unique(colormix); 18 | uR=mod(uniquecolors,256); 19 | uG=mod(floor(uniquecolors/256),256); 20 | uB=mod(floor(uniquecolors/256^2),256); 21 | 22 | sampleRGB_OD = -log10((cat(3,uR,uG,uB)+1)./256); 23 | 24 | % Create Deconvolution matrix 25 | M = [He/norm(He) Eo/norm(Eo) BG/norm(BG)]; 26 | D = inv(M); 27 | 28 | RGB=im2mat(sampleRGB_OD)'; 29 | 30 | HEB = D * RGB; % this step need to prevent; only calculated possible RGB combo instead all; 31 | HEB = HEB'; % main output 32 | % matchback from uniqe color space to image color space; 33 | H = reshape(HEB(u3,1),size(colormix)); 34 | E = reshape(HEB(u3,2),size(colormix)); 35 | bg = reshape(HEB(u3,3),size(colormix)); 36 | chgi={}; 37 | chgi{1}=H; 38 | chgi{2}=E; 39 | chgi{3}=bg; 40 | 41 | % convert to color images 42 | [x,y]=size(H); 43 | imH=zeros([x,y,3]); 44 | imE=zeros([x,y,3]); 45 | imbg=zeros([x,y,3]); 46 | imboth=zeros([x,y,3]); 47 | 48 | if nargout>1 49 | for q=1 50 | imH=10.^(-H)*256-1; % including the background channel help the color show 51 | imE=10.^(-E)*256-1; 52 | imbg=10.^(-bg)*256-1; 53 | imboth=10.^(-H-E-bg)*256-1; 54 | end 55 | imH=uint8(imH); 56 | imE=uint8(imE); 57 | imbg=uint8(imbg); 58 | imboth=uint8(imboth); 59 | end 60 | 61 | -------------------------------------------------------------------------------- /update 12-13-2023/cell_detection_base_functions/im2mat.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashleylk/CODA/2984da5d1aeef770d6c8fd02cc46a5d3194aa449/update 12-13-2023/cell_detection_base_functions/im2mat.p -------------------------------------------------------------------------------- /update 12-13-2023/cell_detection_base_functions/pkfndW.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashleylk/CODA/2984da5d1aeef770d6c8fd02cc46a5d3194aa449/update 12-13-2023/cell_detection_base_functions/pkfndW.p -------------------------------------------------------------------------------- /update 12-13-2023/combine_z_projections.m: -------------------------------------------------------------------------------- 1 | function xyc=combine_z_projections(vol,nums,contrasts,cmap,axisxyz) 2 | % this function will create pretty, RGB z-projections of tissue volumes 3 | % REQUIRED INPUTS: 4 | % vol: volumetric matrix (of size [M N Z]) 5 | % nums: numbers of classes you want to create a z-projection of 6 | % contrasts: matrix with constrast numbers for z-projection (1 = base, >1 = increase contrast. 7 | % example for a model with 5 classes: contrast = [1 1 1 1 1.5]; (increase contrast of type 5) 8 | % cmap: rgb colors for each class in the vol. defined in segmentation code 9 | % OPTIONAL INPUT 10 | % axisxyz: which axis do you want to create the projection along? default = 3 (z-projection), could also be 1 or 2 11 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 12 | % please cite Kiemen et al, Nature methods (2022) 13 | % last updated in December 2023 by ALK 14 | 15 | if ~exist('axisxyz','var');axisxyz=3;end 16 | cmap=double(cmap); 17 | if max(cmap(:))>1 18 | cmap=cmap/255; 19 | end 20 | 21 | tmp=squeeze(sum(vol,axisxyz)); 22 | xyc=zeros([size(tmp) 3]); 23 | for k=nums 24 | % make colormap 25 | cmap2=cmap(k,:); 26 | a=linspace(0,cmap2(1),100)'; 27 | b=linspace(0,cmap2(2),100)'; 28 | c=linspace(0,cmap2(3),100)'; 29 | C=[a b c]*contrasts(k); % enhance contrast 30 | C(C>1)=1; 31 | 32 | % make rgb z-projection 33 | xy0=squeeze(sum(vol==k,axisxyz)); 34 | xy0=round(xy0./max(xy0(:))*99)+1; 35 | 36 | xy0(isnan(xy0))=1; 37 | xy=cat(3,C(xy0,1),C(xy0,2),C(xy0,3)); 38 | xy=reshape(xy,[size(xy0) 3]); 39 | xyc=xyc+xy; 40 | end 41 | 42 | -------------------------------------------------------------------------------- /update 12-13-2023/create_downsampled_tif_images.m: -------------------------------------------------------------------------------- 1 | function create_downsampled_tif_images(pth,ds,subfolders) 2 | % this function will create downsampled tif images from high resolution files 3 | % requires a lot of RAM. as an alternative, try openslide 4 | % INPUTS: 5 | % pth: a folder containing .npdi or .svs images 6 | % ds: a matrix containing desired pixel resolutions you want to create 7 | % example: ds=[1 2 10] (create downsampled imaegs with resolutions of 1, 2, and 10 micron/pixel 8 | % subfolders: a string array containing the output folder names to save the tifs in 9 | % example: subfolders=["10x" "5x" "1x"]; (save images in subfolders with these names) 10 | % note: assumes 10x=~1um/pixel, 5x=~2um/pixel, 1x=~10um/pixel 11 | % OUTPUTS: 12 | % 1. tif images will be created at all downsample factors in ds 13 | % and will be saved in folders subfolders created within pth 14 | warning ('off','all'); 15 | disp('creating downsampled tif images') 16 | 17 | 18 | if pth(end)~='\';pth=[pth,'\'];end 19 | imlist=dir([pth,'*.ndpi']);ft='ndpi'; 20 | if isempty(imlist);imlist=dir([pth,'*.svs']);ft='svs';end 21 | if isempty(imlist);imlist=dir([pth,'*.scn']);ft='scn';end 22 | 23 | % set image output folder 24 | if length(ds)>1 25 | outpth=[pth,char(subfolders(1))]; 26 | outpthe=[pth,char(subfolders(end))]; 27 | else 28 | outpth=[pth,subfolders]; 29 | outpthe=outpth; 30 | end 31 | if outpth(end)~='\';outpth=[outpth,'\'];end 32 | if ~isfolder(outpth);mkdir(outpth); end 33 | 34 | % output image type 35 | tps='tif'; 36 | 37 | ds0=ds(1); 38 | for k=1:length(imlist) 39 | nm=imlist(k).name;tic; 40 | mpp=get_mpp_of_image(pth,nm); 41 | disp(['downsampling image ',num2str(k),' of ',num2str(length(imlist)),': ',nm]) 42 | nmout=strrep(nm,ft,tps); 43 | 44 | if exist([outpth,nmout],'file') && exist([outpthe,nmout],'file') 45 | disp(' PREVIOUSLY LOADED'); 46 | continue; 47 | end 48 | 49 | % get size of image 50 | tmp=imfinfo([pth,nm]); 51 | image_layer=cat(1,tmp.Height); 52 | image_layer=find(image_layer==max(image_layer)); 53 | fx=ds0/mpp; % resizing factor to produce image of 2um/pixel 54 | 55 | imtif=imread([pth,nm],image_layer); 56 | xx=ceil(size(imtif(:,:,1))/fx); 57 | imtif=imresize(imtif,xx,'nearest'); 58 | 59 | imwrite(imtif,[outpth,nmout]); 60 | for jj=2:length(ds) 61 | outpth2=[pth,char(subfolders(jj))]; 62 | if outpth2(end)~='\';outpth2=[outpth2,'\'];end 63 | if ~isfolder(outpth2);mkdir(outpth2);end 64 | 65 | % calculate rescale factor: ex. 2um/px --> 8um/px = rescale image to 1/4 66 | xx=ds(1)/ds(jj); 67 | imtif2=imresize(imtif,xx,'nearest'); 68 | imwrite(imtif2,[outpth2,nmout]); 69 | end 70 | disp([' Finished in ',num2str(round(toc)),' seconds.']) 71 | 72 | end 73 | 74 | -------------------------------------------------------------------------------- /update 12-13-2023/deconvolve_histological_images.m: -------------------------------------------------------------------------------- 1 | function deconvolve_histological_images(pth,stain_type) 2 | % this function will deconvolve an H&E or IHC (stained using diaminobenzadine) 3 | % into hematoxylin and eosin / diaminobenzadine channels. 4 | % REQUIRED INPUT: 5 | % pth: folder containing images to deconvolve 6 | % stain_type: 1 for H&E, 2 for IHC. default is H&E 7 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 8 | % please cite Kiemen et al, Nature methods (2022) 9 | % last updated in December 2023 by ALK 10 | 11 | path(path,'cell_detection_base_functions'); 12 | warning ('off','all'); 13 | 14 | if pth(end)~='\';pth=[pth,'\'];end 15 | outpthH=[pth,'Hchannel\'];mkdir(outpthH); 16 | outpthE=[pth,'Echannel\'];mkdir(outpthE); 17 | 18 | 19 | imlist=dir([pth,'*tif']);if isempty(imlist);imlist=dir([pth,'*jpg']);end 20 | 21 | if ~exist('stain_type','var');stain_type=1;end 22 | if stain_type==1 % H&E 23 | CVS=[0.644 0.717 0.267;0.093 0.954 0.283;0.636 0.001 0.771]; 24 | elseif stain_type==2 % IHC 25 | CVS=[0.65 0.70 0.29;0.27 0.57 0.78;0.71 0.42 0.56]; 26 | end 27 | 28 | for k=1:length(imlist) 29 | imnm=imlist(k).name; 30 | disp(['deconvolving histological image ',num2str(k),' of ',num2str(length(imlist)),': ',imnm]) 31 | if exist([outpthH,imnm],'file');disp(' already done');continue;end 32 | 33 | im0=imread([pth,imnm]); 34 | 35 | [~,imH,imE]=colordeconv_log10(im0,CVS); 36 | 37 | imwrite(uint8(imH),[outpthH,imnm]); 38 | imwrite(uint8(imE),[outpthE,imnm]); 39 | disp(' done') 40 | end 41 | warning ('off','all'); 42 | 43 | 44 | end 45 | 46 | -------------------------------------------------------------------------------- /update 12-13-2023/get_mpp_of_image.m: -------------------------------------------------------------------------------- 1 | function mpp=get_mpp_of_image(pth,nm) 2 | 3 | if contains(nm,'.ndpi') 4 | a=imfinfo([pth,nm]); 5 | tmp=a(1).XResolution; 6 | sxNDPI=(1./tmp).*10000; 7 | mpp=sxNDPI; 8 | elseif contains(nm,'.svs') 9 | a=imfinfo([pth,nm]); 10 | tmp=a(1).ImageDescription; 11 | b1=strfind(tmp,'|MPP = '); 12 | b2=strfind(tmp,'|'); 13 | b2=b2(find(b2>b1,1,'first')); 14 | if isempty(b2);b2=length(tmp)+1;end 15 | sxSVS=str2double(tmp(b1+6:b2-1)); 16 | mpp=sxSVS; 17 | end -------------------------------------------------------------------------------- /update 12-13-2023/get_nuclear_detection_parameters.m: -------------------------------------------------------------------------------- 1 | function get_nuclear_detection_parameters(pthmosaic) 2 | % using color deconvolved images and manual cell coordinates, this function 3 | % will optimize parameters for automatic cell detection of H&E images 4 | % REQUIRED INPUT: 5 | % pthmosaic: folder containing the mosaic image. Inside a subfolder named 6 | % manual detectoin folder should be a tif image named 'mosaic.tif', a mat 7 | % file named 'mosaic.mat' containing a variable 'xym' with manually annotated 8 | % nuclear coordinates, % and a subfolder 'Hchannel' containing the color 9 | % deconvolved hematoxylin channel image. 10 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 11 | % with input from Andre Forjaz, Lucie Dequiedt, and Vasco Quieroga 12 | % please cite Kiemen et al, Nature methods (2022) 13 | % last updated in December 2023 by ALK 14 | 15 | if pthmosaic(end)~='\';pthmosaic=[pthmosaic,'\'];end 16 | 17 | % deconvolve mosaic image 18 | deconvolve_histological_images(pthmosaic); 19 | 20 | % load mosaic image hematoxylin channel 21 | pthmosaicH=([pthmosaic,'Hchannel\']); 22 | pthmanual=([pthmosaic,'manual detection\']); 23 | im0=imread([pthmosaic,'mosaic.tif']); % rgb image 24 | imH=imread([pthmosaicH,'mosaic.tif']); % H-channel image 25 | imH=imH(:,:,1); 26 | imH=255-imH; 27 | imB=imgaussfilt(imH,1); 28 | 29 | % load manual cell coordinates 30 | load([pthmanual,'mosaic.mat'],'xym'); 31 | 32 | % first, optimize the brightness 33 | sz=11; % choose a size to start with 34 | mbs=50:2:200; 35 | [mb]=calculate_optimal_vals(imB,mbs,sz,xym); 36 | 37 | % second, optimize size 38 | szs=3:21; 39 | [~,sz]=calculate_optimal_vals(imB,mb,szs,xym); 40 | 41 | % re-optimize brightness over a narrower range 42 | mbs=mb-10:mb+10; 43 | [mb,sz,xya,TP,FP,FN]=calculate_optimal_vals(imB,mbs,sz,xym); 44 | 45 | % save output values 46 | outpth=[pthmosaic,'automatic detection\']; 47 | if ~isfolder(outpth);mkdir(outpth);end 48 | save([outpth,'optimized_params.mat'],'mb','sz'); 49 | save([outpth,'mosaic.mat'],'xya','TP','FP','FN'); 50 | disp(' optimal cell detection paramaters generated.') 51 | disp([' True positives: ',num2str(round(TP*100)),'%']); 52 | disp([' False positives: ',num2str(round(FP*100)),'%']); 53 | disp([' False negatives: ',num2str(round(FN*100)),'%']); 54 | figure;imshow(im0);hold on; 55 | scatter(xym(:,1),xym(:,2),'oc'); 56 | scatter(xya(:,1),xya(:,2),'*y'); 57 | legend({'manual coords','automatic coords'}) 58 | 59 | 60 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/bpassW.m: -------------------------------------------------------------------------------- 1 | function res = bpassW(arr,lnoise,lobject) 2 | % bandpass filter. 3 | % 4 | % Written by Pei-Hsun Wu, 5 | % Post-doc associate, @ JHU, INBT. 6 | 7 | b = double(lnoise); 8 | w = round(lobject); 9 | N=2*w+1; 10 | hg=fspecial('gaussian',N, b*sqrt(2)); 11 | arrg = imfilter(arr,hg,'symmetric','conv') ; 12 | ha = fspecial('average',N); 13 | arra = imfilter(arr,ha,'symmetric','conv'); 14 | rest = max(arrg-arra,0); 15 | res=rest; -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/calculate_elastic_registration.m: -------------------------------------------------------------------------------- 1 | function [D,xgg,ygg,xx,yy]=calculate_elastic_registration(imrfR,immvR,TArf,TAmv,sz,bf,di,cutoff) 2 | % Iterative calculation of registration translation on small tiles for determination of nonlinear alignment of globally aligned images. 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist('cutoff','var');cutoff=0.15;end 8 | cc=10;cc2=cc+1; 9 | szim=size(immvR); 10 | m=(sz-1)/2+1; 11 | 12 | immvR=double(padarray(immvR,[bf bf],mode(immvR(:))));immvR=imgaussfilt(immvR,3); 13 | imrfR=double(padarray(imrfR,[bf bf],mode(imrfR(:))));imrfR=imgaussfilt(imrfR,3); 14 | TAmv=padarray(TAmv,[bf bf],0); 15 | TArf=padarray(TArf,[bf bf],0); 16 | 17 | % make grid for registration points 18 | n1=randi(round(di/2),1)+bf+m; 19 | n2=randi(round(di/2),1)+bf+m; 20 | [x,y]=meshgrid(n1:di:size(immvR,2)-m-bf,n2:di:size(immvR,1)-m-bf); 21 | x=x(:);y=y(:); 22 | 23 | % get percentage of tissue in each registration ROI 24 | checkS=zeros(size(x)); 25 | numb=200; 26 | for b=1:numb:size(x) 27 | b2=min([b+numb-1 length(x)]); 28 | ii=getImLocalWindowInd_rf([x(b:b2) y(b:b2)],size(TAmv),m-1,1); 29 | 30 | imcheck=reshape(permute(TAmv(ii),[2 1]),[sz sz size(ii,1)]); 31 | imcheck2=zeros(size(imcheck)); 32 | imcheck2(cc2:end-cc,cc2:end-cc,:)=imcheck(cc2:end-cc,cc2:end-cc,:); 33 | mvS=squeeze(sum(sum(imcheck2,1),2)); % indices of image tiles with tissue in them 34 | 35 | imcheck=reshape(permute(TArf(ii),[2 1]),[sz sz size(ii,1)]); 36 | rfS=squeeze(sum(sum(imcheck,1),2)); % indices of image tiles with tissue in them 37 | 38 | checkS(b:b2)=min([mvS rfS],[],2); 39 | end 40 | clearvars ii imcheck imcheck2 41 | checkS=checkS/(sz^2); 42 | 43 | yg=(y-min(y))/di+1; 44 | xg=(x-min(x))/di+1; 45 | xgg0=ones([length(unique(y)) length(unique(x))])*-5000; 46 | ygg0=ones([length(unique(y)) length(unique(x))])*-5000; 47 | 48 | for kk=find(checkS>cutoff)' 49 | % setup small tiles 50 | ii=getImLocalWindowInd_rf([x(kk) y(kk)],size(TAmv),m-1,1);ii(ii==-1)=1; 51 | immvS=immvR(ii);imrfS=imrfR(ii); 52 | immvS=reshape(permute(immvS,[2 1]),[sz sz]); 53 | imrfS=reshape(permute(imrfS,[2 1]),[sz sz]); 54 | 55 | % calculate registration for tiles kk 56 | [X,Y,imoutS]=reg_ims_ELS(immvS,imrfS,2,1); 57 | 58 | % [X,Y,imoutS,RS]=reg_ims_ELS(immvS,imrfS,2,1); 59 | % immvS2=zeros(size(immvS));immvS2(cc2:end-cc,cc2:end-cc,:)=immvS(cc2:end-cc,cc2:end-cc,:); 60 | % a=immvS2(immvS2>0 & imrfS>0);b=imrfS(immvS2>0 & imrfS>0);R0=corr2(a,b);RR=[R0 RS]; 61 | % if max(RR)==RS;X=XS;Y=YS;else;X=-5000;Y=-5000;end 62 | % figure(38), 63 | % subplot(1,2,1),imshowpair(imrfS,immvS)%,title(R0/checkS(kk)) 64 | % subplot(1,2,2),imshowpair(imrfS,imoutS)%,title(RS/checkS(kk)) 65 | xgg0(yg(kk),xg(kk))=X; 66 | ygg0(yg(kk),xg(kk))=Y; 67 | end 68 | % smooth registration grid and make interpolated displacement map 69 | if max(szim)>2000;szimout=round(szim/5);x=x/5;y=y/5;bf=bf/5;else;szimout=szim;end 70 | [D,xgg,ygg,xx,yy]=make_final_grids(xgg0,ygg0,bf,x,y,szimout); 71 | 72 | % im=imwarp(immvR,D);%figure,imshowpair(im,imrfR) 73 | % figure(17); 74 | % subplot(1,2,1),imshowpair(imrfR,immvR) 75 | % subplot(1,2,2),imshowpair(imrfR,im) 76 | % ha=get(gcf,'children');linkaxes(ha); 77 | end 78 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/calculate_global_reg.m: -------------------------------------------------------------------------------- 1 | function [imout,tform,cent,f,Rout]=calculate_global_reg(imrf,immv,rf,iternum,IHC) 2 | if IHC==1;bb=0.8;else;bb=0.9;end 3 | % calculates global registration of a pair of greyscale, downsampled histological images. 4 | % imrf == reference image 5 | % immv == moving image 6 | % szz == max size of images in stack 7 | % rf == reduce images by _ times 8 | % count == number of iterations of registration code 9 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 10 | % please cite Kiemen et al, Nature methods (2022) 11 | % last updated in December 2023 by ALK 12 | 13 | % pre-registration image processing 14 | amv=imresize(immv,1/rf);amv=imgaussfilt(amv,2); 15 | arf=imresize(imrf,1/rf);arf=imgaussfilt(arf,2); 16 | sz=[0 0];cent=[0 0]; 17 | if IHC>0;amv=imadjust(amv);arf=imadjust(arf);end 18 | 19 | % calculate registration, flipping image if necessary 20 | iternum0=2; 21 | [R,rs,xy,amv1out]=group_of_reg(amv,arf,iternum0,sz,rf,bb); 22 | %figure(9),imshowpair(amv1out,arf) 23 | f=0; 24 | % figure(12),imshowpair(arf,amv1out),title(num2str(round(R*100))) 25 | ct=0.8; 26 | if RR;rs=rs2;xy=xy2;f=1;amv=amv2;end 31 | % figure(13),imshowpair(arf,amv2out),title(num2str(round(R2*100))) 32 | end 33 | [tform,amvout,~,~,Rout]=reg_ims_com(amv,arf,iternum-iternum0,sz,rf,rs,xy,0); 34 | aa=double(arf>0)+double(amvout>0); 35 | Rout=sum(aa(:)==2)/sum(aa(:)>0); 36 | %figure(9),imshowpair(amvout,arf) 37 | 38 | % create output image 39 | Rin=imref2d(size(immv)); 40 | if sum(abs(cent))==0 41 | mx=mean(Rin.XWorldLimits); 42 | my=mean(Rin.YWorldLimits); 43 | cent=[mx my]; 44 | end 45 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 46 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 47 | 48 | if f==1 49 | immv=immv(end:-1:1,:,:); 50 | end 51 | 52 | % register 53 | imout=imwarp(immv,Rin,tform,'nearest','Outputview',Rin,'Fillvalues',0); 54 | %figure,imshowpair(arf,amvout) 55 | end 56 | 57 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/calculate_global_reg_IHC.m: -------------------------------------------------------------------------------- 1 | function [imout,tform,cent,f,Rout]=calculate_global_reg_IHC(imrf,immv,rf,iternum,IHC) 2 | if IHC==1;bb=0.8;else;bb=0.9;end 3 | % calculates global registration of a pair of greyscale, downsampled histological images containing IHC stained data. 4 | % imrf == reference image 5 | % immv == moving image 6 | % szz == max size of images in stack 7 | % rf == reduce images by _ times 8 | % count == number of iterations of registration code 9 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 10 | % please cite Kiemen et al, Nature methods (2022) 11 | % last updated in December 2023 by ALK 12 | 13 | % pre-registration image processing 14 | amv=imresize(immv,1/rf);amv=imgaussfilt(amv,2); 15 | arf=imresize(imrf,1/rf);arf=imgaussfilt(arf,2); 16 | sz=[0 0];cent=[0 0]; 17 | if IHC>0;amv=imadjust(amv);arf=imadjust(arf);end 18 | 19 | % calculate registration, flipping image if necessary 20 | iternum0=2; 21 | [R,rs,xy,amv1out]=group_of_reg(amv,arf,iternum0,sz,rf,bb); 22 | %figure(9),imshowpair(amv1out,arf) 23 | f=0; 24 | % figure(12),imshowpair(arf,amv1out),title(num2str(round(R*100))) 25 | ct=0.8; 26 | 27 | [tform,amvout,~,~,Rout]=reg_ims_com(amv,arf,iternum-iternum0,sz,rf,rs,xy,0); 28 | aa=double(arf>0)+double(amvout>0); 29 | Rout=sum(aa(:)==2)/sum(aa(:)>0); 30 | %figure(9),imshowpair(amvout,arf) 31 | 32 | % create output image 33 | Rin=imref2d(size(immv)); 34 | if sum(abs(cent))==0 35 | mx=mean(Rin.XWorldLimits); 36 | my=mean(Rin.YWorldLimits); 37 | cent=[mx my]; 38 | end 39 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 40 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 41 | 42 | if f==1 43 | immv=immv(end:-1:1,:,:); 44 | end 45 | 46 | % register 47 | imout=imwarp(immv,Rin,tform,'nearest','Outputview',Rin,'Fillvalues',0); 48 | %figure,imshowpair(arf,amvout) 49 | end 50 | 51 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/calculate_transform.m: -------------------------------------------------------------------------------- 1 | function res=calculate_transform(im,imnxt,xy,p) 2 | % estimate the dislocation of images between frames. 3 | % using PIV. 4 | % 5 | % input: im : current image (required) 6 | % imnxt: image at next frame,(required) 7 | % p : parameters settings 8 | % 9 | % developed by Pei-Hsun Wu, Ph.D 10 | % @ johns Hopkins University, INBT, 0/20/2014; 11 | 12 | [imly,imlx]=size(im); 13 | [imly2,imlx2]=size(imnxt); 14 | imly=min(imly,imly2); 15 | imlx=min(imlx,imlx2); 16 | if nargin<3 || isempty(xy) 17 | xy=floor([(imly-1)/2, (imlx-1)/2])+1; % using center point of a image 18 | end 19 | 20 | if nargin<4 || isempty(p) 21 | p.rm=floor([imly-1,imlx-1]/2); 22 | p.rm=round(p.rm*0.95); % for excluding edge effect % added 6/2/2016 23 | p.rs=floor(p.rm); 24 | p.tm=3; 25 | p.rg=max([imly,imlx]); % search range 26 | end 27 | 28 | x0=xy(2); % location of image pattern 29 | y0=xy(1); 30 | 31 | imptn=im((y0-p.rm(1):y0+p.rm(1)),(x0-p.rm(2):x0+p.rm(2))) ; 32 | imgrid=imnxt((y0-p.rs(1):y0+p.rs(1)),(x0-p.rs(2):x0+p.rs(2))) ; 33 | 34 | % intenitsy normalization 35 | % this might help to take off scale effect; 36 | % this help a lot. especially for fft based transformation 37 | imptn=(imptn-mean(imptn))/std(imptn(:)); 38 | imgrid=(imgrid-mean(imgrid))/std(imgrid(:)); 39 | 40 | % decide pattern recognization method 41 | switch(p.tm) 42 | case(1); y1=xcorr2(imgrid,imptn); 43 | case(2); y1=patrecog(imgrid,imptn); 44 | case(3); y1=xcorrf2(imptn,imgrid); 45 | case(4); y1=xcor2d_nmrd(imgrid,imptn); 46 | case(5); y1=normxcorr2(imptn,imgrid); y1=y1(end:-1:1,end:-1:1)*100; % rescale to larger htan 1 47 | end 48 | 49 | msk=mskcircle2_rect(size(y1),p.rg); 50 | y1m=y1.*msk; 51 | [my,mx]=find(y1m==max(y1m(:))); % pixel resolution; 52 | [cnt]=gcnt(y1,[mx(1),my(1)],3,0,0); 53 | yx=cnt([2 1]); 54 | res=yx-p.rs-p.rm-1; 55 | res=res([2 1]); % xy 56 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/find_tissue_area.m: -------------------------------------------------------------------------------- 1 | function [TA,fillval]=find_tissue_area(im0,IHC) 2 | % calculates the tissue space of histological images for use in a nonlinear image registration algorithm 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist('IHC','var');IHC=0;end 8 | 9 | %im=double(im0); 10 | 11 | im=double(im0); 12 | img=rgb2gray(im0); 13 | img=img==255 | img==0; 14 | im(cat(3,img,img,img))=NaN; 15 | fillval=squeeze(mode(mode(im,2),1))'; 16 | ima=im(:,:,1);imb=im(:,:,2);imc=im(:,:,3); 17 | ima(img)=fillval(1);imb(img)=fillval(2);imc(img)=fillval(3); 18 | im=cat(3,ima,imb,imc); 19 | 20 | if IHC 21 | aa=mean2(im(:,:,2)); 22 | if aa<210;HE=1;else;HE=0;end 23 | else 24 | HE=1; 25 | end 26 | 27 | if HE % H&E image 28 | % remove objects with very small standard deviations 29 | disp('H&E image') 30 | TA=im-permute(fillval,[1 3 2]); 31 | TA=mean(abs(TA),3)>10; 32 | if size(im0,3)==3 33 | black_line=imclose(std(im,[],3)<5 & rgb2gray(im0)<160,strel('disk',2)); 34 | TA=TA & ~black_line; 35 | end 36 | else % IHC image 37 | disp('IHC image') 38 | % TA=im-permute(fillval,[1 3 2]); 39 | % TA=mean(abs(TA),3)>2; 40 | % TA=imclose(TA,strel('disk',4)); 41 | % TA=imfill(TA,'holes'); 42 | 43 | a=mode(im(:)); 44 | TA=abs(mean(im,3)-double(a))>10; 45 | TA=imclose(TA,strel('disk',8)); 46 | TA=imfill(TA,'holes'); 47 | TA=bwareafilt(TA,1); 48 | end 49 | 50 | %TA=imdilate(TA,strel('disk',4)); 51 | TA=imclose(TA,strel('disk',2)); 52 | TA=bwlabel(TA); 53 | 54 | % remove objects that are less than 1/10 largest object size 55 | N=histcounts(TA(:),max(TA(:))+1); 56 | N(1)=0; 57 | N(N<(max(N)/20))=0; 58 | N(N>0)=1; 59 | TA=N(TA+1); 60 | 61 | D=(sum(TA(:))/numel(TA)); 62 | if D<0.05 63 | TA=fix_TA(im,fillval); 64 | end 65 | %figure(8),imagesc(TA) 66 | 67 | end 68 | 69 | function TA=fix_TA(im,fillval) 70 | TA=im-permute(fillval,[1 3 2]); 71 | TA=mean(TA,3)<-20; 72 | TA=imdilate(TA,strel('disk',4)); 73 | TA=imclose(TA,strel('disk',6)); 74 | TA=bwlabel(TA); 75 | 76 | % remove objects that are less than 1/10 largest object size 77 | N=histcounts(TA(:),max(TA(:))+1); 78 | N(1)=0; 79 | N(N<(max(N)/10))=0; 80 | N(N>0)=1; 81 | TA=N(TA+1); 82 | 83 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/gcnt.m: -------------------------------------------------------------------------------- 1 | function [out,outid]=gcnt(im,mx,sz,th,rsq) 2 | %cntrd: calculates the centroid of bright spots to sub-pixel accuracy. 3 | % Inspired by Grier & Crocker's feature for IDL, but greatly simplified and optimized 4 | % for matlab 5 | % INPUTS: 6 | % im: image to process, particle should be bright spots on dark background with little noise 7 | % ofen an bandpass filtered brightfield image or a nice fluorescent image 8 | % 9 | % mx: locations of local maxima to pixel-level accuracy from pkfnd.m 10 | % 11 | % sz: diamter of the window over which to average to calculate the centroid. 12 | % should be big enough 13 | % to capture the whole particle but not so big that it captures others. 14 | % if initial guess of center (from pkfnd) is far from the centroid, the 15 | % window will need to be larger than the particle size. RECCOMMENDED 16 | % size is the long lengthscale used in bpass plus 2. 17 | 18 | %OUTPUT: a N x 3 array containing, x, y and brightness for each feature 19 | % out(:,1) is the x-coordinates 20 | % out(:,2) is the y-coordinates 21 | % out(:,3) is the Peak intensity 22 | % out(:,4) is the intensity radius 23 | % update (12/16/2010) : eliminate the complex output... 24 | 25 | if nargin<4; 26 | th=0; 27 | rsq=0; 28 | elseif nargin <5; 29 | rsq=0; 30 | end 31 | 32 | [nr,nc]=size(im); 33 | %remove all potential locations within distance sz from edges of image 34 | if 1 % remove edge object 35 | ind=find(mx(:,2) > 1.5*sz/2 & mx(:,2) < nr-1.5*sz/2); 36 | mx=mx(ind,:); 37 | ind=find(mx(:,1) > 1.5*sz/2 & mx(:,1) < nc-1.5*sz/2); 38 | mx=mx(ind,:); 39 | end 40 | [nmx,crap] = size(mx); %nmx is number of particles 41 | 42 | 43 | %inside of the window, assign an x and y coordinate for each pixel 44 | 45 | dimm=sz; 46 | x1=zeros(dimm,dimm); 47 | for i=1:dimm 48 | x1(i,:)=1:dimm; 49 | end 50 | x1=x1-(dimm+1)/2; 51 | y1=x1'; 52 | kk=(dimm-1)/2; 53 | pts=[]; 54 | %%%%%%%% generate the diamend mask matrix ( only valid below sz<=5 ) 55 | % msk=mskdiamond(sz); 56 | % msk=mskcircle(sz); 57 | msk=ones(sz); 58 | %%%%%%%% 59 | 60 | %msk=ones(dimm,dimm); 61 | %msk(1,1)=0;msk(dimm,1)=0; msk(dimm,dimm)=0; msk(1,dimm)=0; 62 | AAt=[x1(:).^2+y1(:).^2,x1(:),y1(:),ones(size(x1(:)))]; 63 | %loop through all of the candidate positions 64 | for i=1 :nmx 65 | 66 | %create a small working array around each candidate location, and apply the window function 67 | tmp = msk.*im((mx(i,2)-kk:mx(i,2)+kk),(mx(i,1)-kk:mx(i,1)+kk)) ;% intensity 68 | tmp=tmp - th; 69 | tmp=max(tmp,1); 70 | % AA=AAt; 71 | % intensity=[]; 72 | % xx=[];x0=[];inthat=[]; 73 | eint=0;rint=0;rsquare=0; 74 | 75 | intensity=tmp(:); 76 | cci=intensity>1; 77 | intensity=log(intensity(cci)); 78 | AA=AAt(cci,:); 79 | % for ii =1 : dimm*dimm 80 | % 81 | % if tmp(ii)== 1 82 | % % tmp(ii)= tmp(ii)+1; 83 | % else 84 | % AA=[AA; [x1(ii)^2+y1(ii)^2 ,x1(ii), y1(ii) , 1]]; 85 | % intensity=[intensity;log(tmp(ii))] ; 86 | % 87 | % %xx=[xx; [x1(ii),y1(ii),tmp(ii)]]; 88 | % %AA([1 5 21 25],:)=[]; 89 | % %intensity(1 5 21 25)=[]; 90 | % end 91 | % end 92 | 93 | AT=AA' ; 94 | maa=AT*AA ; 95 | bb=AT*intensity ; 96 | c=maa\bb ; 97 | inthat=AA*inv(maa)*AT*intensity ;% estimated y value from regression model 98 | mint=mean(intensity); 99 | eint=sum((inthat-mint).^2); 100 | rint=sum((intensity-mint).^2); 101 | rsquare=eint/rint; 102 | % use the rsquare to filter bad beads 103 | if rsquare >rsq && isreal(c)== 1 && isreal(rsquare)==1 && c(1)<0 104 | % c(1) (-1/sigma^2) 105 | xavg=-c(2)/c(1)/2 ; 106 | yavg=-c(3)/c(1)/2 ; 107 | peak=exp(c(4)-(xavg^2+yavg^2)*c(1)); 108 | % the initial guess for optimiation 109 | %x0(4)=(-1/c(1)) 110 | x0(4)=sqrt((-1/c(1))/2); % chage to fit the standard deviation for gaussian fucntion 111 | x0(2)=xavg; 112 | x0(3)=yavg; 113 | x0(1)=peak; 114 | %x0=x0' 115 | %x0=ones(4,1); 116 | %global p1 d1 117 | %simplex method :: amoeba, 118 | %steepest cresent method :: mincg. 119 | 120 | %xa=mincg('f801','f801pd','ftau2cg',x0,5e-6); 121 | % [p,y]=amoeba(x0,'f801',1e-5); xa=p(1,:); 122 | xa=x0; 123 | 124 | % call out the function to optimized the solution start from the at 125 | % least estimator. 126 | % 127 | 128 | %concatenate it up 129 | pts=[pts,[mx(i,1)+xa(2),mx(i,2)+xa(3),xa(1),xa(4),rsquare, i]']; 130 | end 131 | end 132 | 133 | pts=pts'; 134 | outid=pts(:,end); 135 | out=pts(:,1:end-1); -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/getImLocalWindowInd_rf.m: -------------------------------------------------------------------------------- 1 | function ind=getImLocalWindowInd_rf(xy,imsz,wndra,skipstep) 2 | % convert the locations in images to indice matrice of regional windows. 3 | % Input : xy; n by 2 vectors of centroid location 4 | % imsz: 1 by 2 vector represent image size (i.e. =size(image)) 5 | % wndra: local window size (in radius) 6 | % output: n by wndra*2+1 indices matrices ; each row represent the local 7 | % windwos 8 | % 9 | % to use: linearize pixel intensity of given locations and window size can be obtinaed by 10 | % image(ind) 11 | % 12 | % 05/17/2018 developed by P-H Wu. at JHU. 13 | % 14 | % identify the location that are within the range of image 15 | 16 | ccx= xy(:,1) wndra+1; 17 | ccy= xy(:,2) wndra+1; 18 | cc=ccx & ccy; 19 | 20 | xmin=xy(:,1)-wndra; 21 | ymin=xy(:,2)-wndra; 22 | indmin=sub2ind(imsz,ymin,xmin); 23 | indmax=imsz(1)*imsz(2); 24 | 25 | if length(skipstep)==1 % if its a value 26 | [gx,gy]=meshgrid([0:skipstep:2*wndra]*imsz(1),0:skipstep:2*wndra); 27 | 28 | else % when skipstep is a vector 29 | gx0=[0:1:2*wndra]*imsz(1); 30 | gy0=[0:1:2*wndra]; 31 | gx0=gx0(skipstep); 32 | gy0=gy0(skipstep); 33 | [gx,gy]=meshgrid(gx0,gy0); 34 | end 35 | gxy=gx+gy; 36 | gxy=gxy(:)'; 37 | ind=indmin+gxy(:)'; 38 | end 39 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/get_ims.m: -------------------------------------------------------------------------------- 1 | function [im,TA]=get_ims(pth,nm,tp,IHC) 2 | % loads RGB histological image and loads or creates tissue space image for use in a nonlinear image registration algorithm. 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist([pth,'TA\'],'dir');mkdir([pth,'TA\']);end 8 | 9 | im=imread([pth,nm,tp]); 10 | if size(im,3)==1;im=cat(3,im,im,im);end 11 | pthTA=[pth,'TA\']; 12 | if exist([pthTA,nm,'tif'],'file') 13 | TA=imread([pthTA,nm,'tif']); 14 | else 15 | TA=find_tissue_area(im,IHC); 16 | imwrite(TA,[pthTA,nm,'tif']); 17 | end 18 | TA=uint8(TA>0); 19 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/group_of_reg.m: -------------------------------------------------------------------------------- 1 | function [R,rs,xy,amv]=group_of_reg(amv0,arf,iternum0,sz,rf,bb) 2 | % calculates sets of global registrations considering different initialization angles for a pair of greyscale images 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist('bb','var');bb=0.9;end 8 | T=[-2 177 87 268 -1 88 269 178 -7:2:7 179:183 89:93 270:272]; 9 | R=0.2; 10 | rs=0; 11 | xy=0; 12 | aa=arf==0;ab=amv0==0; 13 | arf=double(arf);amv0=double(amv0); 14 | arf=(arf-mean(arf(:)))/std(amv0(:)); 15 | amv0=(amv0-mean(amv0(:)))/std(amv0(:)); 16 | arf=arf-min(arf(:));arf(aa==1)=0; 17 | amv0=amv0-min(amv0(:));amv(ab==1)=0; 18 | amv=amv0; 19 | RR0=sum(amv(:)>0) + sum(arf(:)>0); 20 | for kp=1:length(T) 21 | try 22 | [~,amv1,rs1,xy1,RR]=reg_ims_com(amv0,arf,iternum0,sz,rf,T(kp),[0; 0],1); 23 | if RR~=0;aa=double(arf>0)+double(amv1>0);RR=sum(aa(:)==2)/sum(aa(:)>0);end 24 | catch 25 | disp('catch') 26 | RR=0; 27 | end 28 | 29 | %figure(8),imshowpair(arf,amv1),title(num2str(RR)) 30 | if RR>R;R=RR;rs=rs1;xy=xy1;amv=amv1;end 31 | if R>bb && kp>16;break;end 32 | %disp([RR R bb]) 33 | end 34 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/im2mat.p: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ashleylk/CODA/2984da5d1aeef770d6c8fd02cc46a5d3194aa449/update 12-13-2023/image registration base functions/im2mat.p -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/invert_D.m: -------------------------------------------------------------------------------- 1 | function Dnew=invert_D(D,skk2) 2 | % inverts the 'D' elastic registration transform for registration of cell coordinates. 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist('skk','var');skk=5;end 8 | if ~exist('skk2','var');skk2=5;end 9 | 10 | % calculate coordinates 11 | [xx,yy]=meshgrid(1:size(D,2),1:size(D,1)); 12 | xnew=xx+D(:,:,1); % new position of x 13 | ynew=yy+D(:,:,2); % new position of y 14 | 15 | % interpolate D at original position 16 | D1=D(:,:,1);D2=D(:,:,2); 17 | D1=D1(:);D2=D2(:);xnew2=xnew(:);ynew2=ynew(:); 18 | F1=scatteredInterpolant(xnew2(1:skk:end),ynew2(1:skk:end),D1(1:skk:end)); 19 | F2=scatteredInterpolant(xnew2(1:skk:end),ynew2(1:skk:end),D2(1:skk:end)); 20 | 21 | [xx,yy]=meshgrid(1:skk2:size(D,2),1:skk2:size(D,1)); 22 | D1=-F1(xx,yy); 23 | D2=-F2(xx,yy); 24 | Dnew=zeros(size(D)); 25 | Dnew(:,:,1)=imresize(D1,size(D(:,:,1))); 26 | Dnew(:,:,2)=imresize(D2,size(D(:,:,1))); 27 | Dnew(isnan(Dnew))=0; 28 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/make_final_grids.m: -------------------------------------------------------------------------------- 1 | function [D,xgg,ygg,x,y]=make_final_grids(xgg0,ygg0,bf,x,y,szim) 2 | % creates final nonlinear image registration matrices for a pair of registered images 3 | % xgg / ygg = 20x17 or whatever grid of displacements for image 4 | % x / y = points on 1x image that xgg and ygg are centered at 5 | % C0 = corr2 of immv and imrf tiles before elastic registration 6 | % CR = corr2 of immv and imrf tiles after elastic registration (same size as xgg / ygg) 7 | % bf = buffer I applied around 1x image (bf = 0 if you have no buffer) 8 | % sz = size of each tile that finetune registration is calculated on (mine are 250) 9 | % szim = size of 1x image 10 | % xgg / ygg are -5000 at locations that don't have tissue space 11 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 12 | % please cite Kiemen et al, Nature methods (2022) 13 | % last updated in December 2023 by ALK 14 | 15 | xgg=xgg0; 16 | ygg=ygg0; 17 | if 1 18 | mxy=75;%50 % allow no translation larger than this cutoff 19 | cmxy=xgg>mxy | ygg>mxy; % non continuous values 20 | xgg(cmxy)=-5000; 21 | 22 | % find points where registration was calculated 23 | cempty=xgg==-5000; 24 | xgg(cempty)=0;ygg(cempty)=0; 25 | 26 | %xgg0=xgg;ygg0=ygg; 27 | % replace non continuous values with mean of neighbors 28 | [~,~,dxgg,dygg,~,sxgg,sygg]=fill_vals(xgg,ygg,cempty,1); 29 | m1=abs((xgg-dxgg)./dxgg);m1(m1==Inf)=0; % percent difference between x and mean of surrounding 30 | m2=abs((ygg-dygg)./dygg);m2(m2==Inf)=0; 31 | dds=sxgg>50 | sygg>50;ddm=m1>5 | m2>5;ddp=abs(xgg)>80 | abs(ygg)>80; 32 | dd=(dds | ddm | ddp) & ~cempty; 33 | xgg(dd)=dxgg(dd);ygg(dd)=dygg(dd); 34 | 35 | % fill in values outside tissue region with mean of neighbors 36 | cc=cempty;count=1; 37 | while sum(cc(:))>0 && count<500 38 | [~,~,dxgg,dygg,denom]=fill_vals(xgg,ygg,cc); 39 | cfill=denom>2 & cc; % touching 3+ numbers and needs to be filled 40 | xgg(cfill)=dxgg(cfill); 41 | ygg(cfill)=dygg(cfill); 42 | cc=cc & ~cfill; % needs to be filled and has not been filled 43 | count=count+1; 44 | end 45 | disp([count 500]) 46 | xgg=imgaussfilt(xgg,1); 47 | ygg=imgaussfilt(ygg,1); 48 | else 49 | cempty=xgg==-5000; 50 | xgg(cempty)=0;ygg(cempty)=0; 51 | 52 | ct=30; 53 | xgg(abs(xgg)>ct)=0; 54 | ygg(abs(ygg)>ct)=0; 55 | 56 | xgg=imgaussfilt(xgg,2); 57 | ygg=imgaussfilt(ygg,2); 58 | end 59 | 60 | % add buffer to outline of displacement map to avoid discontinuity 61 | xgg=cat(1,xgg(1,:),xgg,xgg(end,:)); 62 | xgg=cat(2,xgg(:,1),xgg,xgg(:,end)); 63 | ygg=cat(1,ygg(1,:),ygg,ygg(end,:)); 64 | ygg=cat(2,ygg(:,1),ygg,ygg(:,end)); 65 | 66 | x=[1; unique(x)-bf; szim(2)]; 67 | y=[1; unique(y)-bf; szim(1)]; 68 | 69 | % get D 70 | [xq,yq] = meshgrid(1:szim(2),1:szim(1)); 71 | xgq=interp2(x,y,xgg,xq,yq,'spline'); 72 | ygq=interp2(x,y,ygg,xq,yq,'spline'); 73 | D=cat(3,xgq,ygq); 74 | 75 | end 76 | 77 | 78 | function [xgg,ygg,dxgg,dygg,denom,sxgg,sygg]=fill_vals(xgg,ygg,cc,xystd) 79 | if ~exist('xystd','var');xystd=0;sxgg=[];sygg=[];end 80 | % xgg ygg are matrices to be smoothed 81 | % cc is logical matrix containing locations to be smoothed 82 | denom=imfilter(double(~cc),[1 1 1;1 0 1;1 1 1]); 83 | 84 | if xystd % get standard deviation of nearest neighbors 85 | gridX=get_nn_grids(xgg); 86 | gridY=get_nn_grids(ygg); 87 | gridD=get_nn_grids(~cc); 88 | gridX=(gridX-xgg).^2.*gridD; 89 | gridY=(gridY-ygg).^2.*gridD; 90 | 91 | sxgg=(sum(gridX,3)./sum(denom,3)).^0.5;sxgg(cc)=0; 92 | sygg=(sum(gridY,3)./sum(denom,3)).^0.5;sygg(cc)=0; 93 | end 94 | 95 | denom(denom==0)=1; 96 | dxgg=imfilter(xgg,[1 1 1;1 0 1;1 1 1])./denom; 97 | dygg=imfilter(ygg,[1 1 1;1 0 1;1 1 1])./denom; 98 | xgg(cc)=dxgg(cc); 99 | ygg(cc)=dygg(cc); 100 | end 101 | 102 | function gridX=get_nn_grids(xgg) 103 | d1=imfilter(xgg,[1 0 0;0 0 0;0 0 0]); 104 | d2=imfilter(xgg,[0 1 0;0 0 0;0 0 0]); 105 | d3=imfilter(xgg,[0 0 1;0 0 0;0 0 0]); 106 | d4=imfilter(xgg,[0 0 0;1 0 0;0 0 0]); 107 | d5=imfilter(xgg,[0 0 0;0 0 1;0 0 0]); 108 | d6=imfilter(xgg,[0 0 0;0 0 0;1 0 0]); 109 | d7=imfilter(xgg,[0 0 0;0 0 0;0 1 0]); 110 | d8=imfilter(xgg,[0 0 0;0 0 0;0 0 1]); 111 | gridX=cat(3,d1,d2,d3,d4,d5,d6,d7,d8); 112 | end 113 | 114 | % CP=(CR-C0)./C0; 115 | % cc=CP<0; % cc=(CR-C0)./C0<0 | abs(xgg)>sz/4 | abs(ygg)>sz/4; 116 | % [xgg,ygg]=fill_vals(xgg,ygg,cc); -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/mskcircle2_rect.m: -------------------------------------------------------------------------------- 1 | function msk=mskcircle2_rect(sz,da) 2 | % generate the circle mask based on size of input matrix 3 | % sz : matrix size 4 | % da : diameter of circle; 5 | if nargin==1 6 | da=min(sz); 7 | end 8 | msk=zeros(sz); 9 | m = sz; 10 | cent = (m+1)/2; 11 | 12 | x = 1:m(2) ; 13 | y = 1:m(1) ; 14 | [gx,gy]=meshgrid(x,y); 15 | gx=gx-cent(2); 16 | gy=gy-cent(1); 17 | 18 | % calculate distance; 19 | D=sqrt(gx.^2 + gy.^2); 20 | rr=(da-1)/2; 21 | ind= D <= rr; 22 | msk(ind)=1; 23 | 24 | 25 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/pad_im_both2.m: -------------------------------------------------------------------------------- 1 | function im=pad_im_both2(im,sz,ext,fillval) 2 | % pads histological images with whitespace to make files the same size for use in nonlinear image registration algorithm 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if nargin<4;fillval=squeeze(mode(mode(im,2),1))';end 8 | if nargin<3;ext=0;end 9 | 10 | szim=[sz(1)-size(im,1) sz(2)-size(im,2)]; 11 | szA=floor(szim/2); 12 | szB=szim-szA+ext; 13 | szA=szA+ext; 14 | if size(im,3)==3 15 | if length(fillval)==1;fillval=[fillval fillval fillval];end 16 | ima=padarray(im(:,:,1),szA,fillval(1),'pre'); 17 | imb=padarray(im(:,:,2),szA,fillval(2),'pre'); 18 | imc=padarray(im(:,:,3),szA,fillval(3),'pre'); 19 | ima=padarray(ima,szB,fillval(1),'post'); 20 | imb=padarray(imb,szB,fillval(2),'post'); 21 | imc=padarray(imc,szB,fillval(3),'post'); 22 | im=cat(3,ima,imb,imc); 23 | else 24 | try 25 | im=padarray(im,szA,fillval,'pre'); 26 | catch 27 | disp('fds') 28 | end 29 | try im=padarray(im,szB,fillval,'post'); 30 | catch 31 | disp('vcxzfezrs') 32 | end 33 | end 34 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/preprocessing.m: -------------------------------------------------------------------------------- 1 | function [im,impg,TA,fillval]=preprocessing(im,TA,szz,padall,IHC) 2 | % performs pre-processing of histological images for use in a nonlinear image registration algorithm 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | % pad image 8 | fillval=squeeze(mode(mode(im,2),1))'; 9 | %if IHC==2;fillval=[241 241 241];end 10 | if IHC==5;fillval=[0 0 0];end 11 | if ~isempty(padall) 12 | im=pad_im_both2(im,szz,padall,fillval); 13 | if size(TA,1)~=size(im,1) || size(TA,2)~=size(im,2) 14 | TA=pad_im_both2(TA,szz,padall,0); 15 | end 16 | end 17 | 18 | % remove noise and complement images 19 | TA=TA>0; 20 | if ~isa(im,'uint8') 21 | im=im2uint8(im); 22 | end 23 | 24 | if IHC==2 % IF image 25 | impg=rgb2gray(im); 26 | TA=ones(size(im(:,:,1))); 27 | elseif IHC==5 % do not remove nontissue intensity 28 | impg=imcomplement(rgb2gray(im)); 29 | %TA=ones(size(im(:,:,1)));fillval=0; 30 | elseif size(im,3)==3 31 | ima=im(:,:,1);ima(~TA)=255; 32 | imb=im(:,:,2);imb(~TA)=255; 33 | imc=im(:,:,3);imc(~TA)=255; 34 | imp=cat(3,ima,imb,imc); 35 | impg=imcomplement(rgb2gray(imp)); 36 | else 37 | imp=im; 38 | imp(~TA)=255; 39 | impg=imcomplement(imp); 40 | end 41 | 42 | impg=imgaussfilt(impg,2); 43 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/reg_ims_ELS.m: -------------------------------------------------------------------------------- 1 | function [X,Y,imout,RR]=reg_ims_ELS(amv0,arf0,rf,v) 2 | % calculates registration translation only for a pair of tissue images 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist('v','var');v=0;imout=[];RR=[];end 8 | arf=imresize(arf0,1/rf); 9 | amv=imresize(amv0,1/rf); 10 | try xy1=calculate_transform(arf,amv);catch;xy1=[0 0];end 11 | try xy2=calculate_transform(amv,arf);catch;xy2=[0 0];end 12 | xyt=mean([xy1;-xy2]); 13 | 14 | X=-(xyt(1)*rf); 15 | Y=-(xyt(2)*rf); 16 | 17 | if v 18 | imout=imtranslate(amv0,[-X -Y]); 19 | a=imout(imout>0 & arf0>0); 20 | b=arf0(imout>0 & arf0>0); 21 | RR=corr2(a,b); 22 | end 23 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/reg_ims_com.m: -------------------------------------------------------------------------------- 1 | function [tform,amv,rsft,xyt,RR]=reg_ims_com(amv0,arf,count,sz,rf,deg0,xy0,r,th) 2 | % determines the global registration angle and translation to maximize cross correlation of two images 3 | 4 | tform=[];rsft=0;xyt=[0; 0];RR=0;amv=amv0; 5 | mm=mode(amv(:));mmr=mode(arf(:)); 6 | 7 | if nargin<10 8 | theta=-90:0.5:90; 9 | thetaout=2; 10 | else 11 | theta=th{1}; 12 | thetaout=th{2}; 13 | end 14 | if nargin<8;r=0;end 15 | if nargin<7;xy0=[0; 0];end 16 | if nargin<6;deg0=0;end 17 | 18 | brf=single(arf); 19 | 20 | xyt=[0; 0]; 21 | 22 | rsft=zeros([1 count+1]); 23 | rsft(1)=deg0; 24 | amvr=imrotate(amv0,sum(rsft),'crop'); 25 | if sum(amvr(:))==0;rsft=sum(rsft);return;end 26 | 27 | % first translation dictated by center of mass 28 | if r 29 | % find center of mass of images 30 | amv2=bpassW(amvr,2,50);amv2=amv2>0; 31 | arf2=bpassW(arf,2,50);arf2=arf2>0; 32 | cx=sum(amv2);cy=sum(amv2,2); 33 | cx=cumsum(cx);cy=cumsum(cy); 34 | cmamv=[find(cx>cx(end)/2,1) find(cy>cy(end)/2,1)]; 35 | cx=sum(arf2);cy=sum(arf2,2); 36 | cx=cumsum(cx);cy=cumsum(cy); 37 | cmarf=[find(cx>cx(end)/2,1) find(cy>cy(end)/2,1)]; 38 | xy=cmarf-cmamv; 39 | xyt=[xy(1); xy(2)]*rf; 40 | end 41 | 42 | xyt=xyt+xy0; 43 | amv=imtranslate(amvr,xyt'/rf,'OutputView','same'); 44 | a=arf>0; 45 | RR0=corr2(amv(a),arf(a)); 46 | 47 | % iterate 'count' times to achieve sufficient global registration on resized, blurred image 48 | for kk=1:count 49 | bmv=single(amv); 50 | 51 | % use radon for rotational registration 52 | R0 = radon(arf, theta); 53 | Rn = radon(amv, theta); 54 | R0=bpassW(R0,1,3); 55 | Rn=bpassW(Rn,1,3); 56 | 57 | try rsf1=calculate_transform(R0,Rn);catch;rsf1=0;end 58 | rsf=rsf1(1)/thetaout; 59 | 60 | % rotate image then calculate translational registration 61 | bmvr=imrotate(bmv,rsf(1),'crop'); 62 | bmvr(bmvr==0)=mm; 63 | 64 | try xy1=calculate_transform(brf,bmvr);catch;xy1=[0 0];end 65 | try xy2=calculate_transform(bmvr,brf);catch;xy2=[0 0];end 66 | xy=mean([xy1;-xy2]); 67 | 68 | % keep old transform in case update is bad 69 | rsft0=rsft; 70 | xyt0=xyt; 71 | 72 | % update total rsf and xy 73 | rsft(kk+1)=rsf; 74 | if rsf>0 % update rotation 75 | xyt=[cosd(rsf) -sind(rsf);sind(rsf) cosd(rsf)]*xyt; % clockwise rotation 76 | else 77 | xyt=[cosd(rsf) sind(rsf);-sind(rsf) cosd(rsf)]*xyt; % counterclockwise rotation 78 | end 79 | xyt=xyt+([xy(1); xy(2)]*rf); % translation 80 | 81 | % update registration image 82 | amv=imrotate(amv0,sum(rsft),'crop'); 83 | amv=imtranslate(amv,xyt'/rf,'OutputView','same'); 84 | amv(amv==0)=mm; 85 | 86 | a=arf>0; 87 | RR=corr2(amv(a),arf(a)); 88 | if RR+0.022 % if iteration hasn't improved correlation of images, then stop 89 | rsft=rsft0; 90 | xyt=xyt0; 91 | amv=imrotate(amv0,sum(rsft),'crop'); 92 | amv=imtranslate(amv,xyt'/rf,'OutputView','same'); 93 | amv(amv==0)=mm; 94 | RR=corr2(amv(a),arf(a)); 95 | break; 96 | end 97 | 98 | x1=round(size(amv,2)/2); % maximum distance a point in the image moves 99 | y1=round(size(amv,1)/2); 100 | x2=x1*cosd(rsft(kk+1))-y1*sind(rsft(kk+1))+xy(2)-x1; 101 | y2=x1*sind(rsft(kk+1))+ y1*cosd(rsft(kk+1))+xy(1)-y1; 102 | rff=sqrt(x2^2+y2^2); 103 | if rff<0.75 || RR>0.9 104 | break 105 | end 106 | 107 | end 108 | % apply calculated registration to fullscale image 109 | % (account for translation 'sz' due to cropping tissue from full images) 110 | rsft=sum(rsft); 111 | tform=affine2d([cosd(rsft) -sind(rsft) 0;sind(rsft) cosd(rsft) 0;xyt(1)+sz(1) xyt(2)+sz(2) 1]); 112 | end -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/register_global_im.m: -------------------------------------------------------------------------------- 1 | function imG=register_global_im(im,tform,cent,f,fillval) 2 | % applies previously calculated global image registration to an image 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | % set up rotation point of reference 8 | Rin=imref2d(size(im)); 9 | Rin.XWorldLimits = Rin.XWorldLimits-cent(1); 10 | Rin.YWorldLimits = Rin.YWorldLimits-cent(2); 11 | 12 | % flip if necessary 13 | if f==1 14 | im=im(end:-1:1,:,:); 15 | end 16 | 17 | % register 18 | imG=imwarp(im,Rin,tform,'nearest','Outputview',Rin,'Fillvalues',fillval); 19 | 20 | end 21 | 22 | 23 | -------------------------------------------------------------------------------- /update 12-13-2023/image registration base functions/xcorrf2.m: -------------------------------------------------------------------------------- 1 | function c = xcorrf2(a,b,pad) 2 | % c = xcorrf2(a,b) 3 | % Two-dimensional cross-correlation using Fourier transforms. 4 | % XCORRF2(A,B) computes the crosscorrelation of matrices A and B. 5 | % XCORRF2(A) is the autocorrelation function. 6 | % This routine is functionally equivalent to xcorr2 but usually faster. 7 | % See also XCORR2. 8 | 9 | % Author(s): R. Johnson 10 | % $Revision: 1.0 $ $Date: 1995/11/27 $ 11 | 12 | if nargin<3 13 | pad='yes'; 14 | end 15 | 16 | 17 | [ma,na] = size(a); 18 | if nargin == 1 19 | % for autocorrelation 20 | b = a; 21 | end 22 | [mb,nb] = size(b); 23 | % make reverse conjugate of one array 24 | b = conj(b(mb:-1:1,nb:-1:1)); 25 | 26 | if strcmp(pad,'yes'); 27 | % use power of 2 transform lengths 28 | mf = 2^nextpow2(ma+mb); 29 | nf = 2^nextpow2(na+nb); 30 | at = fft2(b,mf,nf); 31 | bt = fft2(a,mf,nf); 32 | elseif strcmp(pad,'no'); 33 | at = fft2(b); 34 | bt = fft2(a); 35 | else 36 | disp('Wrong input to XCORRF2'); return 37 | end 38 | 39 | % multiply transforms then inverse transform 40 | c = ifft2(at.*bt); 41 | % make real output for real input 42 | if ~any(any(imag(a))) & ~any(any(imag(b))) 43 | c = real(c); 44 | end 45 | % trim to standard size 46 | 47 | if strcmp(pad,'yes'); 48 | c(ma+mb:mf,:) = []; 49 | c(:,na+nb:nf) = []; 50 | elseif strcmp(pad,'no'); 51 | c=fftshift(c(1:end-1,1:end-1)); 52 | 53 | % c(ma+mb:mf,:) = []; 54 | % c(:,na+nb:nf) = []; 55 | end 56 | -------------------------------------------------------------------------------- /update 12-13-2023/make_cell_detection_mosaic.m: -------------------------------------------------------------------------------- 1 | function make_cell_detection_mosaic(pthHR,pthLR,num,sxy) 2 | % this function will create a num x num mosaic image containing tiles from 3 | % several histological images for use in validating automated cell detection 4 | % the code will display num x num low resolution images to the user. the 5 | % user must click once on each image to select the region to perform manual 6 | % cell detection on 7 | % REQUIRED INPUTS: 8 | % pthHR: path to high resolution images to create mosaic from 9 | % pthLR: path to low resolution images to choose mosaic locations 10 | % OPTIONAL INPUTS: 11 | % num: square root of number of tiles to create. default value = 3 12 | % sxy: pixel size (in high resolution image space) of each desired tile. default value = 300 13 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 14 | % please cite Kiemen et al, Nature methods (2022) 15 | % last updated in December 2023 by ALK 16 | 17 | if ~exist('num','var');num=3;end 18 | if ~exist('sxy','var');sxy=100;end 19 | 20 | if pthHR(end)~='\';pthHR=[pthHR,'\'];end 21 | if pthLR(end)~='\';pthLR=[pthLR,'\'];end 22 | 23 | % make the output folder 24 | pthmosaic=[pthHR,'cell_detection_validation\']; 25 | if ~isfolder(pthmosaic);mkdir(pthmosaic);end 26 | 27 | % randomly choose images to create tiles from 28 | imlist=dir([pthHR,'*tif']); 29 | if num^2>length(imlist) 30 | % allow duplicates if number of tiles needed exceeds number of images 31 | imnums=randi(length(imlist),[1 num^2]); 32 | else 33 | % must choose non-repeating images 34 | imnums=randperm(length(imlist),num^2); 35 | end 36 | imlist=imlist(imnums); 37 | 38 | % determine scale between low and high-resolution images 39 | m1=imfinfo([pthHR,imlist(1).name]);m1=m1.Height; 40 | m2=imfinfo([pthLR,imlist(1).name]);m2=m2.Height; 41 | scale=m1/m2; 42 | 43 | % load low-resolution images and manually select locations for validation 44 | coords=zeros([length(imnums) 2]); 45 | for b=1:length(imlist) 46 | nm=imlist(b).name; 47 | im=imread([pthLR,nm]); 48 | 49 | h=figure; 50 | imshow(im); 51 | title('click on an ROI in this image') 52 | [x,y]=ginput(1); 53 | close(h); 54 | coords(b,:)=[x y]; 55 | end 56 | 57 | % create mosaic from high-resolution images 58 | count=1; 59 | imXX=[]; 60 | for xx=1:num 61 | imYY=[]; 62 | for yy=1:num 63 | nm=imlist(count).name; 64 | coordxy=round(coords(count,:)*scale); 65 | xx=coordxy(1)-sxy:coordxy(1)+sxy; 66 | yy=coordxy(2)-sxy:coordxy(2)+sxy; 67 | imtmp=imread([pthHR,nm],'PixelRegion',{[yy(1) yy(end)],[xx(1) xx(end)]}); 68 | 69 | imYY=cat(1,imYY,imtmp); 70 | count=count+1; 71 | end 72 | 73 | imXX=cat(2,imXX,imYY); 74 | end 75 | 76 | imwrite(imXX,[pthmosaic,'mosaic.tif']); 77 | -------------------------------------------------------------------------------- /update 12-13-2023/manual_cell_count.m: -------------------------------------------------------------------------------- 1 | function manual_cell_count(pth) 2 | % reads all tif images in pth and allows manual annotation of images 3 | % saves annotation locations to datafile 4 | % REQUIRED INPUT: 5 | % pth: character string of path to images (remember backslash at the end of the path 6 | % NOTES: zoom and toggle to desired location 7 | % 1. press space to start annotating 8 | % 2. click or press space to annotate a coordinate 9 | % 3. press 'z' to change zoom or field of view 10 | % 4. press space to return to annotating 11 | % Written in 2019 by Ashley Lynn Kiemen, Johns Hopkins University 12 | % please cite Kiemen et al, Nature methods (2022) 13 | % last updated in December 2023 by ALK 14 | 15 | if pth(end)~='\';pth=[pth,'\'];end 16 | outpth=[pth,'manual detection\']; 17 | if ~isfolder(outpth);mkdir(outpth);end 18 | 19 | %close all 20 | imlist=dir([pth,'*tif']); 21 | warning('off','all') 22 | 23 | count=1; 24 | for kk=1:length(imlist) 25 | % load and display image 26 | nm=imlist(kk).name(1:end-4); 27 | im=imread([pth,nm,'.tif']); 28 | h=figure(22);title(nm);imshow(im);hold on 29 | answer=''; 30 | 31 | % load previous data if it exists 32 | datafile=[nm,'.mat']; 33 | if exist([outpth,datafile],'file');load([outpth,datafile],'xym');else;xym=[];end 34 | 35 | % if the image's datafile is not empty, add previous annotations 36 | if ~isempty(xym) 37 | scatter(xym(:,1),xym(:,2),'*c') 38 | answer = questdlg('Is this image finished?','Annotation Window','Yes','No','No'); 39 | switch answer 40 | case 'No' 41 | disp('Click to annotate. Press enter or z to change zoom or end') 42 | case 'Yes' 43 | close(h); 44 | continue; 45 | end 46 | end 47 | 48 | 49 | % While annotations are not finished 50 | while ~contains(answer,'Next image') 51 | tt=get(gcf, 'CurrentCharacter'); 52 | if isempty(xym) || isempty(tt) || tt=='z' 53 | answer = questdlg('Change zoom or end annotation?','Annotation prompt','Zoom','Next image','Quit','Zoom'); 54 | switch answer 55 | case 'Zoom' 56 | zoom on; 57 | disp('Press spacebar to return to annotation') 58 | waitfor(gcf,'CurrentCharacter',' ');clc 59 | disp('click to annotate. Press z to change zoom or end') 60 | zoom off 61 | case 'Next image' 62 | close all; 63 | save_annotations(xym,[outpth,datafile]);clc 64 | case 'Quit' 65 | close all; 66 | save_annotations(xym,[outpth,datafile]) 67 | return; 68 | end 69 | end 70 | 71 | if ~contains(answer,'Next image') 72 | [x,y]=ginput(1); 73 | if get(gcf, 'CurrentCharacter')~='z' 74 | scatter(x,y,'*c') 75 | xym=[xym; round([x y])]; 76 | if rem(count,50)==0;disp('saving progress ..');save_annotations(xym,[outpth,datafile]);end 77 | count=count+1; 78 | end 79 | end 80 | end 81 | end 82 | 83 | % turn warnings back on for future code 84 | warning('on','all') 85 | 86 | end 87 | 88 | function save_annotations(xym,outfile) 89 | if exist(outfile,'file') 90 | save(outfile,'xym','-append'); 91 | else 92 | save(outfile,'xym'); 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/build_model_tiles.m: -------------------------------------------------------------------------------- 1 | function build_model_tiles(pthDL,classNames,nblack,sxy,numann0,ctlist,ntrain,nvalidate) 2 | % this function will separately create training and validation tiles for 3 | % construction and optimization of the semantic segmentation model 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | titles=classNames(1:end-1); 9 | 10 | %% combine tiles 11 | numann=numann0; 12 | percann=double(numann0>0); 13 | percann=cat(3,percann,percann); 14 | percann0=percann; 15 | ty='training\';obg=[pthDL,ty,'big_tiles\'];tic; 16 | while length(dir([obg,'HE*jpg']))0); 33 | percann=cat(3,percann,percann); 34 | percann0=percann;tic; 35 | while length(dir([obg,'HE*jpg'])) 45 | end 46 | end 47 | 48 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/calculate_tissue_space.m: -------------------------------------------------------------------------------- 1 | function [im0,TA,outpth]=calculate_tissue_space(pth,imnm) 2 | % creates logical image with tissue area 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | outpth=[pth,'TA\']; 8 | if ~isfolder(outpth);mkdir(outpth);end 9 | 10 | try im0=imread([pth,imnm,'.tif']); 11 | catch 12 | try im0=imread([pth,imnm,'.jp2']); 13 | catch 14 | im0=imread([pth,imnm,'.jpg']); 15 | end 16 | end 17 | if exist([outpth,imnm,'.tif'],'file') 18 | TA=imread([outpth,imnm,'.tif']);disp(' existing TA loaded') 19 | return; 20 | end 21 | 22 | % im=double(imgaussfilt(im0,1)); 23 | % TA=std(im,[],3); 24 | % TA=TA>6.5; 25 | TA=im0(:,:,2)<220; % 210 26 | TA=imclose(TA,strel('disk',1)); 27 | TA=bwareaopen(TA,4); 28 | %figure,imshowpair(im0,TA);axis equal;axis off 29 | imwrite(uint8(TA),[outpth,imnm,'.tif']); 30 | 31 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/deeplab_classification.m: -------------------------------------------------------------------------------- 1 | function deeplab_classification(pth5x,pthDL,sxy,nm,cmap,nblack,nwhite,col) 2 | % this function will classify RGB images using the trained deeplab model 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if ~exist('col','var');col=0;end 8 | 9 | load([pthDL,'net.mat'],'net'); 10 | outpth=[pth5x,'classification_',nm,'\']; 11 | mkdir(outpth); 12 | 13 | b=100; 14 | imlist=dir([pth5x,'*tif']); 15 | if isempty(imlist);imlist=dir([pth5x,'*jp2']);end 16 | if isempty(imlist);imlist=dir([pth5x,'*jpg']);end 17 | 18 | x=tic; 19 | 20 | for kk=1:length(imlist) 21 | tic; 22 | disp(['segmenting image ',num2str(kk),' of ',num2str(length(imlist)),': ',imlist(kk).name]) 23 | if exist([outpth,imlist(kk).name(1:end-3),'tif'],'file');disp(' already segmented');continue;end 24 | 25 | im=imread([pth5x,imlist(kk).name]); 26 | try 27 | TA=imread([pth5x,'TA\',imlist(kk).name(1:end-3),'tif']); 28 | catch 29 | TA=rgb2gray(im)<220; 30 | imfill(TA,'holes'); 31 | end 32 | 33 | % pad image so we classify all the way to the edge 34 | im=padarray(im,[sxy+b sxy+b],0,'both'); 35 | TA=padarray(TA,[sxy+b sxy+b],1,'both'); 36 | 37 | imclassify=zeros(size(TA)); 38 | sz=size(im); 39 | for s1=1:sxy-b*2:sz(1)-sxy 40 | for s2=1:sxy-b*2:sz(2)-sxy 41 | tileHE=im(s1:s1+sxy-1,s2:s2+sxy-1,:); 42 | tileTA=TA(s1:s1+sxy-1,s2:s2+sxy-1,:); 43 | if sum(tileTA(:))<100 44 | tileclassify=zeros(size(tileTA)); 45 | else 46 | tileclassify=semanticseg(tileHE,net); 47 | end 48 | tileclassify=tileclassify(b+1:end-b,b+1:end-b,:); 49 | imclassify(s1+b:s1+sxy-b-1,s2+b:s2+sxy-b-1)=tileclassify; 50 | 51 | % tileclassify=double(tileclassify); 52 | % tileclassify(tileclassify==nblack | tileclassify==0)=nwhite; 53 | % imcolor=uint8(cat(3,am(tileclassify),bm(tileclassify),cm(tileclassify))); 54 | % tileHE=tileHE(b+1:end-b,b+1:end-b,:); 55 | % figure(18), 56 | % subplot(1,2,1),imshow(tileHE) 57 | % subplot(1,2,2),imshow(imcolor) 58 | end 59 | end 60 | 61 | % remove padding 62 | im=im(sxy+b+1:end-sxy-b,sxy+b+1:end-sxy-b,:); 63 | imclassify=imclassify(sxy+b+1:end-sxy-b,sxy+b+1:end-sxy-b,:); 64 | 65 | disp([' finished in ',num2str(round(toc)),' seconds']) 66 | imclassify(imclassify==nblack | imclassify==0)=nwhite; % make black class and zeros class whitespace 67 | imwrite(uint8(imclassify),[outpth,imlist(kk).name(1:end-3),'tif']); 68 | 69 | if col==1 70 | outpthcolor=[outpth,'color\']; 71 | if ~isfolder(outpthcolor);mkdir(outpthcolor);end 72 | am=cmap(:,1);bm=cmap(:,2);cm=cmap(:,3); 73 | imcolor=uint8(cat(3,am(imclassify),bm(imclassify),cm(imclassify))); 74 | imwrite(imcolor,[outpthcolor,imlist(kk).name(1:end-3),'tif']); 75 | end 76 | 77 | if kk==3 && ~isempty(cmap) 78 | am=cmap(:,1);bm=cmap(:,2);cm=cmap(:,3); 79 | try 80 | imcolor=uint8(cat(3,am(imclassify),bm(imclassify),cm(imclassify))); 81 | catch 82 | disp('fds') 83 | end 84 | figure;subplot(1,2,1);imshow(im);subplot(1,2,2);imshow(imcolor) 85 | ha=get(gcf,'children');linkaxes(ha); 86 | end 87 | 88 | if rem(kk,1)==0%col==2 89 | outpth2=[outpth,'check_classification\']; 90 | if ~isfolder(outpth2);mkdir(outpth2);end 91 | make_check_annotation_classified_image(im,imclassify,cmap,2,[outpth2,imlist(kk).name(1:end-3),'tif']); 92 | end 93 | end 94 | disp(['total time: ',num2str(round(toc(x)/60)),' minutes']) -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/fill_annotations_file.m: -------------------------------------------------------------------------------- 1 | function J=fill_annotations_file(I,outpth,WS,umpix,TA,kpb) 2 | % this function will take coordinates of annotations and convert them into 3 | % a mask, using layering and whitespace decisions defined in WS 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | if ~exist('kpb','var');kpb=0;end 9 | disp(' 2. of 3. interpolating and filling annotated regions') 10 | % indices=[layer# annotation# x y] 11 | num=length(WS{1}); 12 | 13 | load([outpth,'annotations.mat'],'xyout'); 14 | if ~isempty(xyout) 15 | xyout(:,3:4)=round(xyout(:,3:4)/umpix); % indices are already at desired resolution 16 | 17 | % find areas of image containing tissue 18 | TA=TA>0; % remove small background 19 | TA=bwareaopen(TA,30); 20 | TA=~TA; 21 | 22 | Ig=find(TA); 23 | szz=size(TA); 24 | J=cell([1 num]); 25 | % interpolate annotation points to make closed objects 26 | for k=unique(xyout(:,1))' % for each annotation type k 27 | Jtmp=zeros(szz); 28 | bwtypek=Jtmp; 29 | cc=xyout(:,1)==k; 30 | xyz=xyout(cc,:); 31 | for pp=unique(xyz(:,2))' % for each individual annotation 32 | if pp==0 33 | continue 34 | end 35 | cc=find(xyz(:,2)==pp); 36 | 37 | xyv=[xyz(cc,3:4); xyz(cc(1),3:4)]; 38 | dxyv=sqrt(sum((xyv(2:end,:)-xyv(1:end-1,:)).^2,2)); 39 | 40 | xyv(dxyv==0,:)=[]; % remove the repeating points 41 | dxyv(dxyv==0)=[]; 42 | dxyv=[0;dxyv]; 43 | 44 | ssd=cumsum(dxyv); 45 | ss0=1:0.49:ceil(max(ssd)); % increase by <0.5 to avoid rounding gaps 46 | xnew=interp1(ssd,xyv(:,1),ss0); 47 | ynew=interp1(ssd,xyv(:,2),ss0); 48 | xnew=round(xnew); 49 | ynew=round(ynew); 50 | try 51 | indnew=sub2ind(szz,ynew,xnew); 52 | catch 53 | disp('annotation out of bounds'); 54 | continue 55 | end 56 | indnew(isnan(indnew))=[]; 57 | bwtypek(indnew)=1; 58 | end 59 | bwtypek=imfill(bwtypek>0,'holes'); 60 | Jtmp(bwtypek==1)=k; 61 | if ~kpb;Jtmp(1:401,:)=0;Jtmp(:,1:401)=0;Jtmp(end-401:end,:)=0;Jtmp(:,end-401:end)=0;end 62 | J{k}=find(Jtmp==k); 63 | end 64 | clearvars bwtypek Jtmp xyout xyz 65 | 66 | % format annotations to keep or remove whitespace 67 | J=format_white(J,Ig,WS,szz); 68 | %figure,imshowpair(J,I); 69 | imwrite(uint8(J),[outpth,'view_annotations_raw.tif']); 70 | else 71 | J=zeros(size(I(:,:,1))); 72 | end 73 | 74 | 75 | end 76 | 77 | function [J,ind]=format_white(J0,Ig,WS,szz) 78 | p=1; % image number I think 79 | ws=WS{1}; % defines keep or delete whitespace 80 | wsa0=WS{2}; % defines non-tissue label 81 | wsa=wsa0(1); 82 | try wsfat=wsa0(2);catch;wsfat=0;end 83 | wsorder=WS{3}; % gives order of annotations 84 | wsnew=WS{4}; % redefines CNN label names 85 | 86 | Jws=zeros(szz); 87 | ind=[]; 88 | % remove white pixels from annotations areas 89 | for k=wsorder 90 | try ii=J0{k};catch;continue;end 91 | iiNW=setdiff(ii,Ig); % indices that are not white 92 | iiW=intersect(ii,Ig); % indices that are white 93 | if ws(k)==0 % remove whitespace and add to wsa 94 | Jws(iiNW)=k; 95 | Jws(iiW)=wsa; 96 | elseif ws(k)==1 % keep only whitespace 97 | Jws(iiW)=k; 98 | Jws(iiNW)=wsfat; 99 | elseif ws(k)==2 % keep both whitespace and non whitespace 100 | Jws(iiNW)=k; 101 | Jws(iiW)=k; 102 | elseif ws(k)>10 103 | Jws(iiW)=k; 104 | Jws(iiNW)=ws(k)-10; 105 | end 106 | end 107 | 108 | % remove small objects and redefine labels (combine labels if desired) 109 | J=zeros(szz); 110 | for k=1:max(Jws(:)) 111 | tmp=Jws==k; 112 | ii=find(tmp==1); 113 | J(tmp)=wsnew(k); 114 | P=[ones([length(ii) 2]).*[p wsnew(k)] ii]; 115 | ind=cat(1,ind,P); 116 | end 117 | end 118 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/load_xml_loop.m: -------------------------------------------------------------------------------- 1 | function [ctlist0,numann0]=load_xml_loop(pth,pthim,WS,umpix,nm,numclass,cmap,classcheck) 2 | % this function loads the xml data from the annotations and generates 3 | % masks, colored masks for validation, and annotation bounding boxes for 4 | % construction of training tiles. 5 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 6 | % please cite Kiemen et al, Nature methods (2022) 7 | % last updated in December 2023 by ALK 8 | 9 | cmap2=cat(1,[0 0 0],cmap)/255; 10 | 11 | imlist=dir([pth,'*xml']); 12 | numann0=[];ctlist0=[]; 13 | outim=[pth,'check_annotations\'];mkdir(outim); 14 | % for each annotation file 15 | for kk=1:length(imlist) 16 | % set up names 17 | imnm=imlist(kk).name(1:end-4);tic; 18 | 19 | disp(['Image ',num2str(kk),' of ',num2str(length(imlist)),': ',imnm]) 20 | outpth=[pth,'data\',imnm,'\']; 21 | if ~exist(outpth,'dir');mkdir(outpth);end 22 | matfile=[outpth,'annotations.mat']; 23 | 24 | % skip if file hasn't been updated since last load 25 | dm='';bb=0;date_modified=imlist(kk).date; 26 | if exist(matfile,'file');load(matfile,'dm','bb');end 27 | if contains(dm,date_modified) && bb==1 28 | disp(' annotation data previously loaded') 29 | load([outpth,'annotations.mat'],'numann','ctlist'); 30 | numann0=cat(1,numann0,numann); 31 | ctlist0=cat(1,ctlist0,ctlist); 32 | continue; 33 | end 34 | 35 | % 1 read xml annotation files and saves as mat files 36 | load_xml_file(outpth,[pth,imnm,'.xml'],date_modified); 37 | 38 | % 2 fill annotation outlines and delete unwanted pixels 39 | try 40 | [I0,TA,~]=calculate_tissue_space(pthim,imnm); 41 | catch 42 | disp('');disp(['SKIP IMAGE ',imnm]);disp('') 43 | continue; 44 | end 45 | J=fill_annotations_file(I0,outpth,WS,umpix,TA,1); 46 | imwrite(uint8(J),[outpth,'view_annotations.tif']); 47 | 48 | % show mask in color: 49 | I=im2double(I0(1:2:end,1:2:end,:));J=double(J(1:2:end,1:2:end,:)); 50 | J1=cmap2(J+1,1);J1=reshape(J1,size(J)); 51 | J2=cmap2(J+1,2);J2=reshape(J2,size(J)); 52 | J3=cmap2(J+1,3);J3=reshape(J3,size(J)); 53 | mask=cat(3,J1,J2,J3); 54 | I=(I*0.5)+(mask*0.5); 55 | imwrite(im2uint8(I),[outim,imnm,'.jpg']); 56 | clearvars I J J1 J2 J3 57 | % create annotation bounding boxes 58 | if exist('classcheck','var') 59 | [numann,ctlist]=save_annotation_bounding_boxes(I0,outpth,nm,numclass,pthim,imnm,classcheck); 60 | else 61 | [numann,ctlist]=save_annotation_bounding_boxes(I0,outpth,nm,numclass); 62 | end 63 | numann0=cat(1,numann0,numann); 64 | ctlist0=cat(1,ctlist0,ctlist); 65 | 66 | toc; 67 | end 68 | 69 | end -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/make_check_annotation_classified_image.m: -------------------------------------------------------------------------------- 1 | function I2=make_check_annotation_classified_image(im,J,cmap,ds,filename) 2 | % this function will make you a nice visualization of the classfication 3 | % overlayed on top of the original, RGB image 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | if ~exist('ds','var');ds=2;end 9 | 10 | cmap2=cat(1,[0 0 0],cmap)/255; 11 | 12 | im=im(1:ds:end,1:ds:end,:);J=J(1:ds:end,1:ds:end,:); 13 | I=im2double(im);J=double(J); 14 | J1=cmap2(J+1,1);J1=reshape(J1,size(J)); 15 | J2=cmap2(J+1,2);J2=reshape(J2,size(J)); 16 | J3=cmap2(J+1,3);J3=reshape(J3,size(J)); 17 | mask=cat(3,J1,J2,J3); 18 | I2=(I*0.6)+(mask*0.4); 19 | I2=uint8(I2*255); 20 | 21 | if exist('filename','var') 22 | imwrite(I2,strrep(filename,'.tif','.jpg')); 23 | end 24 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/make_cmap_legend.m: -------------------------------------------------------------------------------- 1 | function make_cmap_legend(cmap,titles) 2 | % this function will make you a visual of your deep learning colors 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | % sample inputs: 8 | % cmap=[240 159 10; % 1 PDAC 9 | % 255 0 0;... % 2 PanIN 10 | % 0 0 255;... % 3 duct 11 | % 121 248 252;... % 4 islet 12 | % 80 237 80;... % 5 blood vessel 13 | % 110 90 40;... % 6 nerve 14 | % 0 0 0;... % 7 lymph 15 | % 255 255 0;... % 8 fat 16 | % 149 35 184;... % 9 acinus 17 | % 255 194 245;... % 10 connective tissue 18 | % 255 255 255];... % 11 whitespace 19 | % titles=["PDAC" "PanIN" "duct" "islet" "blood vessel" "nerve" "lymph" "fat" "acinus" "collagen" "whitespace"]; 20 | 21 | im=uint8([]); 22 | for k=1:size(cmap,1) 23 | tmp=permute(cmap(k,:),[1 3 2]); 24 | tmp=repmat(tmp,[50 50 1]); 25 | im=cat(2,im,tmp); 26 | end 27 | 28 | for b=1:length(titles);titles(b)=strrep(titles(b),'_',' ');end 29 | 30 | if exist('titles','var') 31 | im=imrotate(im,270); 32 | figure,imagesc(im) 33 | axis equal 34 | xlim([0 size(im,2)]);ylim([0 size(im,1)]) 35 | yticks(15:50:size(im,1)) 36 | set(gca,'TickLength',[0 0]) 37 | xticks([]) 38 | yticklabels(titles)%ytickangle(90) 39 | set(gca,'fontsize',15); 40 | else 41 | figure,imshow(im) 42 | end 43 | set(gcf,'color','w');set(gca,'LineWidth',1); 44 | 45 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/make_confusion_matrix.m: -------------------------------------------------------------------------------- 1 | function make_confusion_matrix(D,classNames) 2 | % this function will create a pretty confusion matrix given as input a 3 | % table of numbers and classnames for the rows and columns 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | if length(classNames)>size(D,1);classNames=classNames(1:end-1);end 9 | 10 | if isempty(D);disp('no testing annotations');return;end 11 | 12 | % make D into blue-red heatmap 13 | nn=20; 14 | Dc=round(D/max(D(:))*(nn-1))+1; 15 | Cb=[39 123 227]/255; % blue 16 | Cw=[245 242 237]/255; % white 17 | Cr=[227 57 39]/255; % red 18 | a1=linspace(Cw(1),Cb(1),nn)'; 19 | b1=linspace(Cw(2),Cb(2),nn)'; 20 | c1=linspace(Cw(3),Cb(3),nn)'; 21 | a2=linspace(Cw(1),Cr(1),nn)'; 22 | b2=linspace(Cw(2),Cr(2),nn)'; 23 | c2=linspace(Cw(3),Cr(3),nn)'; 24 | Cb=[a1 b1 c1]; 25 | 26 | % eye white to blue, non eye is white to red 27 | Deye=(cat(3,a1(Dc),b1(Dc),c1(Dc))); 28 | Dneye=(cat(3,a2(Dc),b2(Dc),c2(Dc))); 29 | DD=Deye.*eye(size(D))+Dneye.*~eye(size(D)); 30 | 31 | D1=D.*eye(size(D)); 32 | D2=sum(D1,1)./sum(D,1)*100; 33 | D3=sum(D1,2)./sum(D,2)*100; 34 | D4=sum(D1(:))/sum(D(:))*100; 35 | 36 | % round accuracy to nearest 10th place 37 | D2=round(D2*10)/10; 38 | D3=round(D3*10)/10; 39 | D4=round(D4*10)/10; 40 | 41 | % make text and RGB of combined confusion data 42 | D2b=ones(size(D2))*0.9;D3b=ones(size(D3))*0.9;D4b=ones(size(D4))*0.9; 43 | Db=cat(2,cat(1,D,D2),[D3;D4]); 44 | a=DD(:,:,1);a=cat(2,cat(1,a,D2b),[D3b;D4b]); 45 | b=DD(:,:,2);b=cat(2,cat(1,b,D2b),[D3b;D4b]); 46 | c=DD(:,:,3);c=cat(2,cat(1,c,D2b),[D3b;D4b]); 47 | DDb=cat(3,a,b,c); 48 | for b=1:length(classNames);classNames(b)=strrep(classNames(b),'_',' ');end 49 | classnamesA=[classNames "PRECISION"]; 50 | classnamesB=[classNames "RECALL"]; 51 | 52 | Db(isnan(Db))=0; 53 | figure('units','normalized','outerposition',[0 0 1 1]); 54 | imagesc(DDb);axis equal; 55 | xlim([0.5 size(Db,1)+0.5]);ylim([0.5 size(Db,1)+0.5]); 56 | xticks(1:size(Db,1));yticks(1:size(Db,1)) 57 | grid off;hold on; 58 | add_text(Db); 59 | xticklabels(classnamesB);yticklabels(classnamesA) 60 | set(gca,'XAxisLocation','top') 61 | xlabel('predicted') 62 | ylabel('ground truth') 63 | set(gca,'Fontsize',15) 64 | 65 | 66 | end 67 | 68 | 69 | function add_text(D) 70 | % make colormap 71 | 72 | % Cb=[11, 163, 29]/255; % green 73 | % Cw=[163, 34, 11]/255; % red 74 | % a1=linspace(C1(1),C2(1),5)'; 75 | % b1=linspace(C1(2),C2(2),5)'; 76 | % c1=linspace(C1(3),C2(3),5)'; 77 | % Cb=[a1 b1 c1]; 78 | % Cb=cat(1,Cb,Cb(end,:)); 79 | 80 | Cb=[50, 168, 76;... 81 | 168, 140, 50;... 82 | 168, 121, 50;... 83 | 168, 74, 50;... 84 | 168, 58, 50;... 85 | 168, 58, 50;... 86 | 168, 58, 50;... 87 | 168, 58, 50;... 88 | 168, 58, 50;... 89 | 168, 58, 50]/255; 90 | Cb=Cb(end:-1:1,:); 91 | 92 | for ii = 1:size(D,1) 93 | for jj = 1:size(D,2) 94 | nu = D(ii,jj); 95 | val = num2str(round(nu,2)); 96 | 97 | % choose fontcolor 98 | if jj==ii;c='w';else;c='k';end 99 | if jj==size(D,2) || ii==size(D,1) 100 | kk=ceil(nu/10); 101 | if kk==0;kk=1;end 102 | c=Cb(kk,:); 103 | end 104 | 105 | text(jj,ii,val,'Color',c,'HorizontalAlignment','center','FontSize',12) 106 | end 107 | end 108 | 109 | 110 | end 111 | 112 | 113 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/random_augmentation.m: -------------------------------------------------------------------------------- 1 | function [imh,imlabel]=random_augmentation(imh0,imlabel0,rot,sc,hue,blr,rsz) 2 | % randomly augments with rotation, scaling, and hue 3 | % imh0 = H&E image tile 4 | % imlabel0 = mask image for H&E 5 | % rot = logical, do rotation augmentation? 6 | % sc = logical, do scaling augmentation? 7 | % hue = logical, do hue augmentation? 8 | % blr = logical, do blur augmentation? 9 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 10 | % please cite Kiemen et al, Nature methods (2022) 11 | % last updated in December 2023 by ALK 12 | 13 | if nargin<7 14 | rot=1; 15 | sc=1; 16 | hue=1; 17 | rsz=0; 18 | blr=0; 19 | end 20 | if ~exist('imlabel0','var');imlabel0=imh0;end 21 | 22 | imh=double(imh0); 23 | imlabel=double(imlabel0); 24 | szz=size(imh0,1); 25 | 26 | % random rotation 27 | if rot 28 | angs=0:5:355; 29 | ii=randperm(length(angs),1); 30 | imh=imrotate(imh,angs(ii)); 31 | imlabel=imrotate(imlabel,angs(ii)); 32 | end 33 | 34 | % random scaling 35 | if sc 36 | scales=[0.6:0.01:0.95 1.1:0.01:1.4]; 37 | ii=randperm(length(scales),1); 38 | imh=imresize(imh,scales(ii),'nearest'); 39 | imlabel=imresize(imlabel,scales(ii),'nearest'); 40 | end 41 | 42 | if hue 43 | % scale blue / red 44 | rd=[0.88:0.01:0.98 1.02:0.01:1.12]; 45 | bl=[0.88:0.01:0.98 1.02:0.01:1.12]; 46 | gr=[0.88:0.01:0.98 1.02:0.01:1.12]; 47 | ird=randperm(length(rd),1); 48 | ibl=randperm(length(bl),1); 49 | igr=randperm(length(gr),1); 50 | 51 | % scale red 52 | imr=255-imh(:,:,1); 53 | imh(:,:,1)=255-(imr*rd(ird)); 54 | % scale blue 55 | imb=255-imh(:,:,3); 56 | imh(:,:,3)=255-(imb*bl(ibl)); 57 | % scale green 58 | img=255-imh(:,:,2); 59 | imh(:,:,2)=255-(img*gr(igr)); 60 | end 61 | 62 | % if scaling augmentation was performed, resize images to be correct tilesize 63 | if blr 64 | bll=ones([1 50]); 65 | bll(1)=1.05; 66 | bll(2)=1.1; 67 | bll(3)=1.15; 68 | bll(4)=1.2; 69 | ibl=randperm(length(bll),1); 70 | bll=bll(ibl); 71 | 72 | if bll~=1 73 | imh=imgaussfilt(imh,bll); 74 | end 75 | end 76 | 77 | 78 | if rsz 79 | szh=size(imh,1); 80 | if szh>szz 81 | cent=round(size(imh,1)/2); 82 | sz1=floor((szz-1)/2); 83 | sz2=ceil((szz-1)/2); 84 | imh=imh(cent-sz1:cent+sz2,cent-sz1:cent+sz2,:); 85 | imlabel=imlabel(cent-sz1:cent+sz2,cent-sz1:cent+sz2,:); 86 | end 87 | 88 | if szh0,strel('disk',10)); 30 | tmp=imfill(tmp,'holes'); 31 | tmp=bwareaopen(tmp,300); 32 | L=bwlabel(tmp); 33 | numann=zeros([max(L(:)) numclass]); 34 | for pk=1:max(L(:)) 35 | tmp=double(L==pk); 36 | a=sum(tmp,1);b=sum(tmp,2); 37 | rect=[find(a,1,'first') find(a,1,'last') find(b,1,'first') find(b,1,'last')]; 38 | tmp=tmp(rect(3):rect(4),rect(1):rect(2)); 39 | 40 | % make label and H&E bounding boxes 41 | tmplabel=imlabel(rect(3):rect(4),rect(1):rect(2)).*tmp; 42 | tmpim=im(rect(3):rect(4),rect(1):rect(2),:); 43 | nm=num2str(pk,'%05.f'); 44 | imwrite(uint8(tmpim),[pthim,nm,'.tif']); 45 | imwrite(uint8(tmplabel),[pthlabel,nm,'.tif']); 46 | 47 | if exist('classcheck','var') 48 | if sum(tmplabel(:)==classcheck)>0 49 | imnum=dir([outim,'*',imnm,'*']); 50 | nn=num2str(length(imnum)+1,'%03.f'); 51 | nmout=[imnm,'_',nn,'.jpg']; 52 | imwrite(uint8(tmpim),[outim,nmout]); 53 | end 54 | end 55 | 56 | for anns=1:numclass 57 | numann(pk,anns)=sum(tmplabel(:)==anns); 58 | end 59 | end 60 | ctlist=dir([pthim,'*tif']); 61 | 62 | bb=1; % indicate that xml file is fully analyzed 63 | if exist([outpth,'annotations.mat'],'file') 64 | save([outpth,'annotations.mat'],'numann','ctlist','bb','-append'); 65 | else 66 | save([outpth,'annotations.mat'],'numann','ctlist','bb'); 67 | end 68 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/test_model_performance.m: -------------------------------------------------------------------------------- 1 | function test_model_performance(pthdata,pthclassify,nwhite,nblack,titles) 2 | % this function will compare a classified image to manual annotations to 3 | % determine the independent, testing accuracy of the model 4 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 5 | % please cite Kiemen et al, Nature methods (2022) 6 | % last updated in December 2023 by ALK 7 | 8 | numclass=nblack-1; 9 | 10 | plist=dir(pthdata); 11 | pDL=[]; 12 | ptrue=[]; 13 | for k=3:length(plist) 14 | tic; 15 | pth=[pthdata,plist(k).name,'\']; 16 | if exist([pth,'view_annotations.tif'],'file') || exist([pth,'view_annotations_raw.tif'],'file') 17 | try 18 | J0=double(imread([pth,'view_annotations.tif'])); 19 | catch 20 | J0=double(imread([pth,'view_annotations_raw.tif'])); 21 | end 22 | %im=imread([pthim,plist(k).name,'.tif']); 23 | imDL=imread([pthclassify,plist(k).name,'.tif']); 24 | 25 | % remove small pixels 26 | for b=1:max(J0) 27 | tmp=J0==b; 28 | J0(J0==b)=0; 29 | tmp=bwareaopen(tmp,25); 30 | J0(tmp==1)=b; 31 | end 32 | 33 | % get true and predicted class at testing annotation locations 34 | L=find(J0>0); 35 | ptrue=cat(1,ptrue,J0(L)); 36 | pDL=cat(1,pDL,imDL(L)); 37 | 38 | end 39 | disp([k length(plist) round(toc)]) 40 | end 41 | pDL(pDL==nblack)=nwhite; 42 | % fx=ptrue==numclass | pDL==numclass; 43 | % ptrue(fx)=[];pDL(fx)=[]; 44 | 45 | % normalize to the minimum number of pixels, rounded to neartest 1000 46 | km=min(histcounts(ptrue,numclass)); 47 | if km<100 48 | km=floor(km/10)*10; 49 | elseif km<1000 50 | km=floor(km/100)*100; 51 | else 52 | km=floor(km/1000)*1000; 53 | end 54 | ptrue2=[]; 55 | pDL2=[]; 56 | for k=unique(ptrue)' 57 | a=find(ptrue==k); 58 | b=randperm(length(a),km); 59 | ptrue2=[ptrue2;ptrue(a(b))]; 60 | pDL2=[pDL2;pDL(a(b))]; 61 | end 62 | 63 | % confusion matrix with equal number of pixels of each class 64 | Dn=zeros([max(ptrue) max(pDL)]); 65 | if length(titles)>size(Dn,1);titles=titles(1:end-1);end 66 | for a=1:max(ptrue2) 67 | for b=1:max(pDL2) 68 | tmp1=ptrue2==a; 69 | tmp2=pDL2==b; 70 | Dn(a,b)=sum(tmp1 & tmp2); 71 | end 72 | end 73 | Dn(isnan(Dn))=0; 74 | make_confusion_matrix(Dn,titles) 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /update 12-13-2023/segmentation base functions/train_deeplab.m: -------------------------------------------------------------------------------- 1 | function train_deeplab(pth,classes,sz,classNames) 2 | % this function will train the deepLab model 3 | % Written in 2020 by Ashley Lynn Kiemen, Johns Hopkins University 4 | % please cite Kiemen et al, Nature methods (2022) 5 | % last updated in December 2023 by ALK 6 | 7 | if exist([pth,'net.mat'],'file');disp(' model already built');return;end 8 | 9 | disp(' normalizing') 10 | nmim='im\'; % nmim='im_blank\'; 11 | nmlabel='label\'; 12 | 13 | pthTrain=[pth,'training\']; 14 | pthVal=[pth,'validation\']; 15 | % pthTest=[pth,'testing\']; 16 | 17 | % 1 make training data 18 | TrainHE=[pthTrain,nmim]; 19 | Trainlabel=[pthTrain,nmlabel]; 20 | imdsTrain = imageDatastore(TrainHE); 21 | pxdsTrain = pixelLabelDatastore(Trainlabel,classNames,classes); 22 | pximdsTrain = pixelLabelImageDatastore(imdsTrain,pxdsTrain); %'DataAugmentation',augmenter); 23 | tbl = countEachLabel(pxdsTrain); 24 | 25 | % make validation data 26 | ValHE=[pthVal,nmim]; 27 | Vallabel=[pthVal,nmlabel]; 28 | imdsVal = imageDatastore(ValHE); 29 | pxdsVal = pixelLabelDatastore(Vallabel,classNames,classes); 30 | pximdsVal = pixelLabelImageDatastore(imdsVal,pxdsVal); %'DataAugmentation',augmenter); 31 | 32 | % make testing data 33 | % TestHE=[pthTest,nmim]; 34 | % Testlabel=[pthTest,nmlabel]; 35 | % imdsTest = imageDatastore(TestHE); 36 | % pxdsTest = pixelLabelDatastore(Testlabel,classNames,classes); 37 | % pximdsTest = pixelLabelImageDatastore(imdsTest,pxdsTest); %'DataAugmentation',augmenter); 38 | 39 | 40 | % visualize pixel counts by class 41 | % frequency = tbl.PixelCount/sum(tbl.PixelCount); 42 | % figure(132),bar(1:numel(classes),frequency),title('distribution of class labels') 43 | % xticks(1:numel(classes)) 44 | % xticklabels(tbl.Name) 45 | % xtickangle(45) 46 | % ylabel('Frequency') 47 | 48 | disp(' starting training') 49 | options = trainingOptions('adam',... % stochastic gradient descent solver 50 | 'MaxEpochs',8,... 51 | 'MiniBatchSize',4,... % datapoints per 'mini-batch' - ideally a small power of 2 (32, 64, 128, or 256) 52 | 'Shuffle','every-epoch',... % reallocate mini-batches each epoch (so min-batches are new mixtures of data) 53 | 'ValidationData',pximdsVal,... 54 | 'ValidationPatience',6,... % stop training when validation data doesn't improve for __ iterations 5 55 | 'InitialLearnRate',0.0005,... % 'InitialLearnRate',0.0005,... 56 | 'LearnRateSchedule','piecewise',... % drop learning rate during training to prevent overfitting 57 | 'LearnRateDropPeriod',1,... % drop learning rate every _ epochs 58 | 'LearnRateDropFactor',0.75,... % multiply learning rate by this factor to drop it 59 | 'ValidationFrequency',128,... % initial loss should be -ln( 1 / # classes ) 60 | 'ExecutionEnvironment','gpu',... % train on gpu 61 | 'Plots','training-progress');%,... % view progress while training 62 | %'OutputFcn', @(info)savetrainingplot(info,pth)); % save training progress as image 63 | 64 | % Design network 65 | numclass = numel(classes); 66 | imageFreq = tbl.PixelCount ./ tbl.ImagePixelCount; 67 | classWeights = median(imageFreq) ./ imageFreq; 68 | 69 | lgraph = deeplabv3plusLayers([sz sz 3],numclass,"resnet50"); 70 | pxLayer = pixelClassificationLayer('Name','labels','Classes',tbl.Name,'ClassWeights',classWeights); 71 | lgraph = replaceLayer(lgraph,"classification",pxLayer); 72 | % lgraph=make_CNN_layers_deeplab(sz,numclass,tbl,classWeights); 73 | 74 | % lgraph = deeplabv3plusLayers([sz sz 3], numclass, "resnet101"); 75 | % pxLayer = pixelClassificationLayer('Name','labels','Classes',tbl.Name,'ClassWeights',classWeights); 76 | % lgraph = replaceLayer(lgraph,"ClassificationLayer_predictions",pxLayer); 77 | 78 | % train 79 | [net, info] = trainNetwork(pximdsTrain,lgraph,options); 80 | save([pth,'net.mat'],'net','info'); 81 | 82 | % I = readimage(imdsTrain,35); 83 | % L = readimage(pxdsTrain,35); 84 | % figure, 85 | % subplot(1,2,1),imshow(I) 86 | % subplot(1,2,2),imagesc(double(L));axis equal;axis off 87 | % test on one image 88 | % I = readimage(imdsTest,35); 89 | % C = semanticseg(I, net); 90 | 91 | % cmap=[121 248 252;... % 1 islet 92 | % 0 0 255;... % 2 duct 93 | % 80 237 80;... % 3 blood vessel 94 | % 255 255 0;... % 4 fat 95 | % 149 35 184;... % 5 acinus 96 | % 255 194 245;... % 6 connective tissue 97 | % 255 255 255;... % 7 whitespace 98 | % 255 0 0;... % 8 PanIN 99 | % 255 255 255]/255; % 9 noise 100 | % 101 | % B = labeloverlay(I,C,'Colormap',cmap,'Transparency',0.4); 102 | % figure(651),imshow(B) 103 | % make_pixel_colorbar(cmap,classes); 104 | % 105 | % expectedResult = readimage(pxdsTest,35); 106 | % actual = uint8(C); 107 | % figure(183) 108 | % subplot(1,2,1),imshow(I) 109 | % subplot(1,2,2),imagesc(actual),axis equal;axis off 110 | 111 | 112 | end 113 | 114 | function stop=savetrainingplot(info,pthSave) 115 | stop=false; %prevents this function from ending trainNetwork prematurely 116 | if info.State=='done' %check if all iterations have completed 117 | exportapp(findall(groot, 'Type', 'Figure'),[pthSave,'training_process.png']) 118 | end 119 | 120 | 121 | end 122 | --------------------------------------------------------------------------------