├── .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 |
4 |
5 |
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 |
--------------------------------------------------------------------------------