├── MICCAI18_DeepEM (2).pdf ├── NLST ├── calibcentreslice.py ├── calibweaklabelallsmall.csv ├── checkCT.ipynb ├── checkpidsid.py ├── cpfile.py ├── delfile.py ├── filterctim.py ├── mvnegfolder.py ├── pidsidsrsdct.csv ├── queryResultfinal.csv ├── savepidfinal.txt ├── selectcp.py └── studyiddict.csv ├── README.md ├── config_testtchi.py ├── config_trainingweak0.py ├── config_trainingweak1.py ├── config_trainingweak2.py ├── config_trainingweak3.py ├── config_trainingweak4.py ├── config_trainingweak5.py ├── config_trainingweak6.py ├── config_trainingweak7.py ├── config_trainingweak8.py ├── config_trainingweak9.py ├── fnamedct.csv ├── lobepositionpredacc.png ├── prepare.py ├── revfname.py ├── run_testtchi.sh ├── runweaktrainingressamp.sh ├── runweaktrainingresv2.sh ├── weakdetclslos.png ├── weakdetectorsamp ├── data.py ├── data.pyc ├── detect.py ├── dpn3d.py ├── dpn3d26.py ├── dpn3dold.py ├── layers.py ├── layers.pyc ├── main.py ├── prepare.py ├── res18.py ├── res18.pyc ├── res_pool.py ├── revfname.py ├── split_combine.py ├── split_combine.pyc ├── utils.py ├── utils.pyc ├── weakdata.py ├── weakdatav2.py └── weakdatav2.pyc └── weakdetectorv2 ├── data.py ├── data.pyc ├── detect.py ├── dpn3d.py ├── dpn3d26.py ├── dpn3dold.py ├── layers.py ├── layers.pyc ├── main.py ├── res18.py ├── res18.pyc ├── res_pool.py ├── split_combine.py ├── split_combine.pyc ├── utils.py ├── utils.pyc ├── weakdata.py ├── weakdatav2.py └── weakdatav2.pyc /MICCAI18_DeepEM (2).pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/MICCAI18_DeepEM (2).pdf -------------------------------------------------------------------------------- /NLST/calibcentreslice.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | import csv 4 | import os 5 | prep_folder = './preprocessnp/' 6 | pdfrm = pd.read_csv('newweaklabel.csv', names=['fname', 'position', 'centerslice']) 7 | fnmlst = pdfrm['fname'].tolist()[1:] 8 | poslst = pdfrm['position'].tolist()[1:] 9 | ctrlst = pdfrm['centerslice'].tolist()[1:] 10 | fid = open('calibweaklabel.csv', 'w') 11 | csvwriter = csv.writer(fid) 12 | csvwriter.writerow(['fname', 'position', 'centerslice']) 13 | badcount = 0 14 | keepcount = 0 15 | for idx, fnm in enumerate(fnmlst): 16 | data = np.load(os.path.join(prep_folder, fnm+'_clean.npy')) 17 | extendbox = np.load(os.path.join(prep_folder, fnm+'_originbox.npy')) 18 | spacing = np.load(os.path.join(prep_folder, fnm+'_spacing.npy')) 19 | # print(spacing, np.expand_dims(extendbox[:,0],1), spacing.shape, np.expand_dims(extendbox[:,0],1).shape, \ 20 | # np.expand_dims(extendbox[:,0],1)[0,0]) 21 | label = float(ctrlst[idx])*spacing[0] 22 | label = label -float(np.expand_dims(extendbox[:,0],1)[0,0]) 23 | if idx%5000 == 0: 24 | print('process', idx) 25 | if label < 0 or label > data.shape[1]: 26 | badcount += 1 27 | # print(fnm, label, data.shape) 28 | continue 29 | csvwriter.writerow([fnm, poslst[idx], label]) 30 | keepcount += 1 31 | fid.close() 32 | print(badcount, keepcount) -------------------------------------------------------------------------------- /NLST/checkpidsid.py: -------------------------------------------------------------------------------- 1 | ''' check pid and study id is 1 to n''' 2 | import os 3 | import pandas as pd 4 | pdfrm = pd.read_csv('weaklabel.csv', names=['103303/1.2.840.113654.2.55.35659264966224867891971832130700032142', '1', '9']) 5 | pidsid = pdfrm['103303/1.2.840.113654.2.55.35659264966224867891971832130700032142'].tolist() 6 | position = pdfrm['1'].tolist() 7 | centerslice = pdfrm['9'].tolist() 8 | mydct = {} 9 | datadct = {} 10 | for idx, ps in enumerate(pidsid): 11 | pid, sid = ps.split('/') 12 | if sid in mydct: 13 | if mydct[sid] != pid: 14 | print(pid, mydct[sid]) 15 | datadct[sid].append([position[idx], centerslice[idx]]) 16 | else: 17 | datadct[sid] = [[position[idx], centerslice[idx]]] 18 | mydct[sid] = pid 19 | print(len(mydct.keys())) # 11006 20 | ''' we use study id as the file name''' 21 | # save the dictionary 22 | siddct = {} 23 | import csv 24 | fid = open('studyiddict.csv', 'w') 25 | csvwriter = csv.writer(fid) 26 | csvwriter.writerow(['studyid', 'newfname']) 27 | for idx, sid in enumerate(mydct.keys()): 28 | siddct[sid] = idx 29 | csvwriter.writerow([sid, '{num:05d}'.format(num=idx+1)]) 30 | fid.close() 31 | # get the numpy data 32 | npdct = {} 33 | maxsubsrs = 0 34 | for pid in os.listdir('./preprocess/'): 35 | for studyid in os.listdir('./preprocess/'+pid+'/'): 36 | isnp = False 37 | nsubsrs = 0 38 | for srs in os.listdir('./preprocess/'+pid+'/'+studyid+'/'): 39 | if srs.endswith('_clean.npy'): 40 | isnp = True 41 | nsubsrs += 1 42 | if isnp: 43 | if studyid in npdct and npdct[studyid] != pid: 44 | print(pid, npdct[studyid], studyid) 45 | npdct[studyid] = pid 46 | maxsubsrs = max(maxsubsrs, nsubsrs) 47 | print(len(npdct.keys()), maxsubsrs) 48 | # copy to preprocessnp and change file name 49 | import shutil 50 | from shutil import copyfile 51 | pidsidsrsdct = {} 52 | newdatadct = {} 53 | for studyid in npdct.keys(): 54 | if studyid not in siddct: continue 55 | # fpath = siddct[studyid]+'/'+studyid+'/' 56 | subsrsid = 1 57 | for srs in os.listdir('./preprocess/'+npdct[studyid]+'/'+studyid+'/'): 58 | if srs.endswith('_clean.npy'): 59 | pidsidsrsdct['./preprocess/'+npdct[studyid]+'/'+studyid+'/'+srs[:-9]] = \ 60 | '{num:05d}'.format(num=siddct[studyid])+'_'+str(subsrsid) 61 | newdatadct['{num:05d}'.format(num=siddct[studyid])+'_'+str(subsrsid)] = datadct[studyid] 62 | # copyfile('./preprocess/'+npdct[studyid]+'/'+studyid+'/'+srs, \ 63 | # './preprocessnp/'+'{num:05d}'.format(num=siddct[studyid])+'_'+str(subsrsid)+'_clean.npy') 64 | # copyfile('./preprocess/'+npdct[studyid]+'/'+studyid+'/'+srs[:-9]+'originbox.npy', \ 65 | # './preprocessnp/'+'{num:05d}'.format(num=siddct[studyid])+'_'+str(subsrsid)+'_originbox.npy') 66 | # copyfile('./preprocess/'+npdct[studyid]+'/'+studyid+'/'+srs[:-9]+'spacing.npy', \ 67 | # './preprocessnp/'+'{num:05d}'.format(num=siddct[studyid])+'_'+str(subsrsid)+'_spacing.npy') 68 | subsrsid += 1 69 | fid = open('pidsidsrsdct.csv', 'w') 70 | csvwriter = csv.writer(fid) 71 | csvwriter.writerow(['oldpath', 'newfname']) 72 | for k, v in pidsidsrsdct.iteritems(): 73 | csvwriter.writerow([k, v]) 74 | fid.close() 75 | fid = open('newweaklabel.csv', 'w') 76 | csvwriter = csv.writer(fid) 77 | csvwriter.writerow(['fname', 'position', 'centerslice']) 78 | for k, v in newdatadct.iteritems(): 79 | for vv in v: 80 | if len(vv) != 2: print(vv, k) 81 | csvwriter.writerow([k, vv[0], vv[1]]) 82 | fid.close() -------------------------------------------------------------------------------- /NLST/cpfile.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import shutil 3 | import pandas as pd 4 | import os 5 | import os.path 6 | filepath = '/mnt/wentao/NLST/NLST/' 7 | dstpath = '/mnt/media/wentao/NLST/' 8 | sctimageinfofname = '/mnt/wentao/NLST/package-nlst-304-2017.07.05/CT Image Info CSV/sctimageinfo.csv' 9 | sctimageinfocolname = ['softwareversion', 'visit', 'seriesdescription', 'imagetype', 'kvp_raw', \ 10 | 'mas_raw', 'effmas_raw', 'pitch_raw', 'tablerotation_raw', 'reconthickness_raw', 'reconinterval_raw', \ 11 | 'reconfilter', 'reconstruction_diameter_raw', 'manufacturer_raw', 'manufacturers_model_name', 'scannercode', \ 12 | 'seriesinstanceuids', 'studyuid', 'pid', 'study_yr', 'numberimages', 'imagesize_kilobytes', 'imageclass', \ 13 | 'reconinterval', 'reconstruction_diameter', 'reconthickness manufacturer', 'effmas', 'kvp', 'mas', 'pitch', \ 14 | 'tablerotation', 'dataset_version'] 15 | sctiminfoframe = pd.read_csv(sctimageinfofname, names=sctimageinfocolname, dtype=str) 16 | # print sctiminfoframe.softwareversion.tolist()[1], sctiminfoframe.visit.tolist()[1] 17 | uiddict = {} 18 | pidlist = sctiminfoframe.studyuid.tolist()[1:] # pid 19 | studyidlist = sctiminfoframe.seriesinstanceuids.tolist()[1:] # studyuid 20 | seriesinstlist = sctiminfoframe.scannercode.tolist()[1:] # seriesinstanceuids 21 | reconfilterlist = sctiminfoframe.reconinterval_raw.tolist()[1:] # reconfilter 22 | imagetypelist = sctiminfoframe.seriesdescription.tolist()[1:] # imagetype 23 | 24 | for idx in xrange(len(pidlist)): 25 | if idx % 1000 == 0: 26 | print idx, imagetypelist[idx], reconfilterlist[idx], pidlist[idx], studyidlist[idx], seriesinstlist[idx] 27 | if 'local' in imagetypelist[idx].lower(): 28 | continue 29 | if 'lung' not in reconfilterlist[idx].lower(): 30 | continue 31 | if pidlist[idx] not in uiddict: 32 | uiddict[pidlist[idx]] = [[studyidlist[idx], seriesinstlist[idx]]] 33 | else: 34 | uiddict[pidlist[idx]].append([studyidlist[idx], seriesinstlist[idx]]) 35 | nCTs = 0 36 | for pid in os.listdir(filepath): 37 | if nCTs % 500 == 0: 38 | print pid 39 | if pid not in uiddict: 40 | continue 41 | for studyid in os.listdir(filepath+pid+'/'): 42 | for seriesid in os.listdir(filepath+pid+'/'+studyid): 43 | hasseriesid = False 44 | for sidlist in uiddict[pid]: 45 | if studyid == sidlist[0] and seriesid == sidlist[1]: 46 | hasseriesid = True 47 | if not os.path.exists(dstpath+pid): 48 | os.mkdir(dstpath+pid) 49 | if not os.path.exists(dstpath+pid+'/'+studyid): 50 | os.mkdir(dstpath+pid+'/'+studyid) 51 | if not os.path.exists(dstpath+pid+'/'+studyid+'/'+seriesid): 52 | os.mkdir(dstpath+pid+'/'+studyid+'/'+seriesid) 53 | for fname in os.listdir(filepath+pid+'/'+studyid+'/'+seriesid): 54 | shutil.copy(filepath+pid+'/'+studyid+'/'+seriesid+'/'+fname, dstpath+pid+'/'+studyid+'/'+seriesid+'/'+fname) 55 | nCTs += 1 56 | print nCTs -------------------------------------------------------------------------------- /NLST/delfile.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | import zipfile 4 | filepath = './NLST/' 5 | zipcount = 0 6 | count = 0 7 | for pid in os.listdir(filepath): 8 | if pid.endswith('.zip'): 9 | zip_ref = zipfile.ZipFile(filepath+pid, 'r') 10 | if not os.path.exists(filepath+pid[:-4]): 11 | zip_ref.extractall(filepath+pid[:-4]) 12 | zipcount += 1 13 | print(filepath+pid) 14 | zip_ref.close() 15 | os.remove(filepath+pid) 16 | print('del and unzip', zipcount) 17 | for pid in os.listdir(filepath): 18 | for psid in os.listdir(os.path.join(*[filepath, pid])): 19 | for sid in os.listdir(os.path.join(*[filepath, pid, psid])): 20 | if os.path.isdir(os.path.join(*[filepath, pid, psid, sid])): 21 | if len(os.listdir(os.path.join(*[filepath, pid, psid, sid]))) <= 10: 22 | count += 1 23 | shutil.rmtree(os.path.join(*[filepath, pid, psid, sid])) 24 | print('rm '+filepath+'/'+pid+'/'+psid+'/'+sid) 25 | if len(os.listdir(os.path.join(*[filepath, pid, psid]))) == 0: 26 | shutil.rmtree(os.path.join(*[filepath, pid, psid])) 27 | print('rm '+filepath+'/'+pid+'/'+psid) 28 | if len(os.listdir(os.path.join(*[filepath, pid]))) == 0: 29 | shutil.rmtree(os.path.join(*[filepath, pid])) 30 | print('rm '+filepath+'/'+pid) 31 | print('del', count) 32 | # del slice > 2.5, numberimages < 10, 'localizer', 'top' in imagetype 33 | delfoldlst = [] 34 | delcount = 0 35 | names = ['softwareversion', 'visit', 'seriesdescription', 'imagetype', 'kvp_raw', 'mas_raw', 'effmas_raw', \ 36 | 'pitch_raw', 'tablerotation_raw', 'reconthickness_raw','reconinterval_raw', 'reconfilter', 'reconstruction_diameter_raw', \ 37 | 'manufacturer_raw', 'manufacturers_model_name', 'scannercode', 'seriesinstanceuids', 'studyuid', 'pid', 'study_yr', \ 38 | 'numberimages', 'imagesize_kilobytes', 'imageclass', 'reconinterval', 'reconstruction_diameter', 'reconthickness', \ 39 | 'manufacturer', 'effmas', 'kvp', 'mas', 'pitch', 'tablerotation', 'dataset_version'] 40 | import pandas as pd 41 | pdfrm = pd.read_csv('./package-nlst-304-2017.07.05/CT Image Info CSV/sctimageinfo.csv', names=names) 42 | pidlst = pdfrm['pid'].tolist()[1:] 43 | stdidlst = pdfrm['studyuid'].tolist()[1:] 44 | srsidlst = pdfrm['seriesinstanceuids'].tolist()[1:] 45 | thclst = pdfrm['reconthickness'].tolist()[1:] 46 | nimlst = pdfrm['numberimages'].tolist()[1:] 47 | imgtyp = pdfrm['imagetype'].tolist()[1:] 48 | stdyrlst0 = pdfrm['study_yr'].tolist()[1:] 49 | pidstdyr2stdiddct = {} 50 | for idx, thc in enumerate(thclst): 51 | if str(pidlst[idx])+str(stdyrlst0[idx]) not in pidstdyr2stdiddct: 52 | pidstdyr2stdiddct[str(pidlst[idx])+str(stdyrlst0[idx])] = stdidlst[idx] 53 | else: 54 | if pidstdyr2stdiddct[str(pidlst[idx])+str(stdyrlst0[idx])] != stdidlst[idx]: 55 | print(pidlst[idx], pidstdyr2stdiddct[str(pidlst[idx])+str(stdyrlst0[idx])], stdidlst[idx]) 56 | pidstdyr2stdiddct.pop(str(pidlst[idx])+str(stdyrlst0[idx]), None) 57 | # assert(1==0) 58 | if float(thc) > 2.5 or nimlst[idx] <= 10 or 'localizer' in imgtyp[idx] or 'top' in imgtyp[idx]: 59 | # if type(pidlst[idx]) is int or stdidlst[idx] is int or srsidlst[idx] is int: 60 | # print(pidlst[idx], stdidlst[idx], srsidlst[idx]) 61 | if os.path.isdir('./NLST/'+str(pidlst[idx])+'/'+stdidlst[idx]+'/'+srsidlst[idx]): 62 | print('del '+'./NLST/'+str(pidlst[idx])+'/'+stdidlst[idx]+'/'+srsidlst[idx]) 63 | shutil.rmtree('./NLST/'+str(pidlst[idx])+'/'+stdidlst[idx]+'/'+srsidlst[idx]) 64 | if len(os.listdir('./NLST/'+str(pidlst[idx])+'/'+stdidlst[idx])) == 0: 65 | shutil.rmtree('./NLST/'+str(pidlst[idx])+'/'+stdidlst[idx]) 66 | print('rm '+'./NLST/'+str(pidlst[idx])+'/'+stdidlst[idx]) 67 | if len(os.listdir('./NLST/'+str(pidlst[idx]))) == 0: 68 | shutil.rmtree('./NLST/'+str(pidlst[idx])) 69 | print('rm '+'./NLST/'+str(pidlst[idx])) 70 | delcount += 1 71 | # delfoldlst.append('./NLST/'+pidlst[i]+'/'+stdidlst[i]+'/'+srsidlst[i]) 72 | print('del > 2.5', delcount) 73 | # del sct_epi_loc not in [1,2,3,4,5,6] 74 | names = ['STUDY_YR', 'SCT_AB_DESC', 'SCT_PRE_ATT', 'SCT_EPI_LOC', 'SCT_LONG_DIA', 'SCT_PERP_DIA', 'SCT_MARGINS', \ 75 | 'sct_slice_num', 'SCT_AB_NUM', 'sct_found_after_comp', 'pid', 'dataset_version'] 76 | pdfrm = pd.read_csv('./package-nlst-304-2017.07.05/Spiral CT Abnormalities CSV/sctabn.csv', names=names) 77 | stdyrlst = pdfrm['STUDY_YR'].tolist()[1:] 78 | loclst = pdfrm['SCT_EPI_LOC'].tolist()[1:] 79 | lngdimlst = pdfrm['SCT_LONG_DIA'].tolist()[1:] 80 | pidlst1 = pdfrm['pid'].tolist()[1:] 81 | slclst = pdfrm['sct_slice_num'].tolist()[1:] 82 | kplst = [] 83 | kpstdidlst = set([]) 84 | kpdct = {} 85 | for idx, loc in enumerate(loclst): 86 | if str(pidlst1[idx])+str(stdyrlst[idx]) not in pidstdyr2stdiddct: 87 | # print(pidlst1[idx], stdyrlst[idx]) 88 | continue 89 | if loc in [1,2,3,4,5,6, '1', '2', '3', '4', '5', '6'] and float(lngdimlst[idx]) >= 3: 90 | kplst.append([pidlst1[idx], stdyrlst[idx], pidstdyr2stdiddct[str(pidlst1[idx])+str(stdyrlst[idx])], loc, \ 91 | slclst[idx]]) 92 | kpstdidlst.add(str(pidlst1[idx])+'/'+pidstdyr2stdiddct[str(pidlst1[idx])+str(stdyrlst[idx])]) 93 | if str(pidlst1[idx])+'/'+pidstdyr2stdiddct[str(pidlst1[idx])+str(stdyrlst[idx])] in kpdct: 94 | kpdct[str(pidlst1[idx])+'/'+pidstdyr2stdiddct[str(pidlst1[idx])+str(stdyrlst[idx])]].append([loc, slclst[idx]]) 95 | else: 96 | kpdct[str(pidlst1[idx])+'/'+pidstdyr2stdiddct[str(pidlst1[idx])+str(stdyrlst[idx])]] = [[loc, slclst[idx]]] 97 | print('#kp', len(kplst), len(list(kpstdidlst)), len(kpdct.keys())) 98 | kpstdidlst = list(kpstdidlst) 99 | print(len(kpstdidlst)) 100 | ndel = 0 101 | kplst = [] 102 | import csv 103 | fid = open('weaklabel.csv', 'w') 104 | writer = csv.writer(fid) 105 | for pid in os.listdir('./NLST/'): 106 | if len(os.listdir('./NLST/'+pid)) == 0: 107 | print('rm '+'./NLST/'+pid) 108 | shutil.rmtree('./NLST/'+pid) 109 | for stdid in os.listdir('./NLST/'+pid): 110 | if str(pid) + '/' + stdid in kpstdidlst: 111 | kplst.append(str(pid) + '/' + stdid) 112 | for v in kpdct[str(pid) + '/' + stdid]: 113 | writer.writerow([str(pid) + '/' + stdid]+ v) 114 | if str(pid) + '/' + stdid not in kpstdidlst: 115 | print(str(pid)+ '/'+stdid) 116 | ndel += 1 117 | print('rm '+'./NLST/'+pid+'/'+stdid) 118 | shutil.rmtree('./NLST/'+pid+'/'+stdid) 119 | if len(os.listdir('./NLST/'+pid)) == 0: 120 | 1 == 1 121 | print('rm '+'./NLST/'+pid) 122 | shutil.rmtree('./NLST/'+pid) 123 | print('del ', ndel, kpstdidlst[0]) 124 | print('remain ', len(kplst)) 125 | nkp = 0 126 | for pid in os.listdir('./NLST/'): 127 | if len(os.listdir('./NLST/'+pid)) == 0: 128 | print('after rm', './NLST/'+pid) 129 | nkp += len(os.listdir('./NLST/'+pid)) 130 | print('left', nkp) 131 | fid.close() -------------------------------------------------------------------------------- /NLST/filterctim.py: -------------------------------------------------------------------------------- 1 | ''' Wentao Zhu, wentaozhu1991@gmail.com 2 | Put the file into the NLST folder, and run. It will delete 3 | the CT images that do not appear in the csv file 4 | file organization: .../NLST/filterctim.py; .../NLST/NLST/****/****; 5 | .../NLST/package-nlst-304-2017.07.05/Spiral CT Abnormalities CSV/sctabn.csv ''' 6 | import csv 7 | import os 8 | import shutil 9 | 10 | nlstpath = './NLST/NLST/' 11 | csvpath = './package-nlst-304-2017.07.05/Spiral CT Abnormalities CSV/sctabn.csv' 12 | qcsvpath = './package-nlst-304-2017.07.05/queryResultfinal.csv' # query csv 13 | savetxt = './package-nlst-304-2017.07.05/savepidfinal.txt' 14 | f = open(csvpath) 15 | csvf = csv.reader(f) 16 | count = 0 17 | poscount = 0 # number of patient having nodule 18 | lastpid = 0 # record last pid to facilitate poscount 19 | pospidlist = set([]) # record positive pid list 20 | negcount = 0 # subsample 1/3 of CTs having no nodule 21 | negpidlist = set([]) # set for fast remove repulicate items 22 | pididx = 0 # record which column the pid in 23 | lepidx = 0 # record which column the SCT_EPI_LOC in 24 | for row in csvf: 25 | if count == 0: 26 | count += 1 27 | for col in row: 28 | if col == 'pid': 29 | break 30 | pididx += 1 31 | for col in row: 32 | if col == 'SCT_EPI_LOC': 33 | break 34 | lepidx += 1 35 | continue 36 | if row[pididx] != lastpid and row[lepidx] != '': 37 | poscount += 1 38 | pospidlist.add(row[pididx]) 39 | elif row[pididx] != lastpid: 40 | negcount += 1 41 | if negcount % 12 == 0: 42 | negpidlist.add(row[pididx]) 43 | lastpid = row[pididx] 44 | f.close() 45 | assert poscount == len(pospidlist) 46 | # assert(negcount == len(negpidlist)) 47 | print '# pid', poscount+len(negpidlist) 48 | print '# positive pid', poscount 49 | print '# negative pid', len(negpidlist) 50 | 51 | posnum = 0 52 | negnum = 0 53 | txtlist = set([]) # remove repulicated txt pid 54 | txtf = open(savetxt, 'w') 55 | f = open(qcsvpath) 56 | qcsvf = csv.reader(f) 57 | for row in qcsvf: 58 | if row[0] in pospidlist: 59 | if row[0] not in txtlist: 60 | txtf.write(row[0]+'\n') 61 | txtlist.add(row[0]) 62 | posnum += 1 63 | elif row[0] in negpidlist: 64 | if row[0] not in txtlist: 65 | txtf.write(row[0]+'\n') 66 | txtlist.add(row[0]) 67 | negnum += 1 68 | f.close() 69 | txtf.close() 70 | print '# query pid', posnum+negnum 71 | print '# positive pid', posnum 72 | print '# negative pid', negnum 73 | 74 | rmnum = 0 75 | leftnum = 0 76 | for pid in os.listdir(nlstpath): 77 | if pid not in negpidlist and pid not in pospidlist: 78 | print 'remove', pid 79 | shutil.rmtree(nlstpath+pid+'/') 80 | rmnum += 1 81 | else: 82 | leftnum += 1 83 | print 'rm', rmnum 84 | print 'left', leftnum 85 | -------------------------------------------------------------------------------- /NLST/mvnegfolder.py: -------------------------------------------------------------------------------- 1 | ''' Wentao Zhu, wentaozhu1991@gmail.com 2 | mv negative folder into .../data/negNLST/ to reduce memory usage 3 | file organization: .../data/filterctim.py; .../data/NLST/****/****; 4 | .../data/package-nlst-304-2017.07.05/Spiral CT Abnormalities CSV/sctabn.csv ''' 5 | import csv 6 | import os 7 | import shutil 8 | 9 | nlstpath = './data/NLST/' 10 | csvpath = './package-nlst-304-2017.07.05/Spiral CT Abnormalities CSV/sctabn.csv' 11 | qcsvpath = './package-nlst-304-2017.07.05/queryResult.csv' # query csv 12 | savetxt = './package-nlst-304-2017.07.05/savepid.txt' 13 | mvpath = './data/negNLST/' 14 | f = open(csvpath) 15 | csvf = csv.reader(f) 16 | count = 0 17 | poscount = 0 # number of patient having nodule 18 | lastpid = 0 # record last pid to facilitate poscount 19 | pospidlist = set([]) # record positive pid list 20 | negcount = 0 # subsample 1/3 of CTs having no nodule 21 | negpidlist = set([]) # set for fast remove repulicate items 22 | pididx = 0 # record which column the pid in 23 | lepidx = 0 # record which column the SCT_EPI_LOC in 24 | for row in csvf: 25 | if count == 0: 26 | count += 1 27 | for col in row: 28 | if col == 'pid': 29 | break 30 | pididx += 1 31 | for col in row: 32 | if col == 'SCT_EPI_LOC': 33 | break 34 | lepidx += 1 35 | continue 36 | if row[pididx] != lastpid and row[lepidx] != '': 37 | poscount += 1 38 | pospidlist.add(row[pididx]) 39 | elif row[pididx] != lastpid: 40 | negcount += 1 41 | if negcount % 6 == 0: 42 | negpidlist.add(row[pididx]) 43 | lastpid = row[pididx] 44 | f.close() 45 | assert poscount == len(pospidlist) 46 | # assert(negcount == len(negpidlist)) 47 | print '# pid', poscount+len(negpidlist) 48 | print '# positive pid', poscount 49 | print '# negative pid', len(negpidlist) 50 | 51 | posnum = 0 52 | negnum = 0 53 | txtlist = set([]) # remove repulicated txt pid 54 | txtf = open(savetxt, 'w') 55 | f = open(qcsvpath) 56 | qcsvf = csv.reader(f) 57 | for row in qcsvf: 58 | if row[0] in pospidlist: 59 | if row[0] not in txtlist: 60 | txtf.write(row[0]+'\n') 61 | txtlist.add(row[0]) 62 | posnum += 1 63 | elif row[0] in negpidlist: 64 | if row[0] not in txtlist: 65 | txtf.write(row[0]+'\n') 66 | txtlist.add(row[0]) 67 | negnum += 1 68 | f.close() 69 | txtf.close() 70 | print '# query pid', posnum+negnum 71 | print '# positive pid', posnum 72 | print '# negative pid', negnum 73 | 74 | rmnum = 0 75 | leftnum = 0 76 | for pid in os.listdir(nlstpath): 77 | if pid not in negpidlist and pid not in pospidlist: 78 | print 'mv', pid 79 | shutil.move(nlstpath+pid, mvpath) 80 | # shutil.rmtree(nlstpath+pid+'/') 81 | rmnum += 1 82 | else: 83 | leftnum += 1 84 | print 'mv', rmnum 85 | print 'left', leftnum 86 | -------------------------------------------------------------------------------- /NLST/selectcp.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import shutil 3 | import pandas as pd 4 | import os 5 | import os.path 6 | import zipfile 7 | from multiprocessing import Pool 8 | filepath = '/mnt/wentao/NLST/NLST/' 9 | dstpath = '/mnt/media/wentao/NLST/' 10 | sctimageinfofname = '/mnt/wentao/NLST/package-nlst-304-2017.07.05/CT Image Info CSV/sctimageinfo.csv' 11 | sctimageinfocolname = ['softwareversion', 'visit', 'seriesdescription', 'imagetype', 'kvp_raw', \ 12 | 'mas_raw', 'effmas_raw', 'pitch_raw', 'tablerotation_raw', 'reconthickness_raw', 'reconinterval_raw', \ 13 | 'reconfilter', 'reconstruction_diameter_raw', 'manufacturer_raw', 'manufacturers_model_name', 'scannercode', \ 14 | 'seriesinstanceuids', 'studyuid', 'pid', 'study_yr', 'numberimages', 'imagesize_kilobytes', 'imageclass', \ 15 | 'reconinterval', 'reconstruction_diameter', 'reconthickness manufacturer', 'effmas', 'kvp', 'mas', 'pitch', \ 16 | 'tablerotation', 'dataset_version'] 17 | sctiminfoframe = pd.read_csv(sctimageinfofname, names=sctimageinfocolname, dtype=str) 18 | # print sctiminfoframe.softwareversion.tolist()[1], sctiminfoframe.visit.tolist()[1] 19 | uiddict = {} 20 | pidlist = sctiminfoframe.studyuid.tolist()[1:] # pid 21 | studyidlist = sctiminfoframe.seriesinstanceuids.tolist()[1:] # studyuid 22 | seriesinstlist = sctiminfoframe.scannercode.tolist()[1:] # seriesinstanceuids 23 | reconfilterlist = sctiminfoframe.reconinterval_raw.tolist()[1:] # reconfilter 24 | imagetypelist = sctiminfoframe.seriesdescription.tolist()[1:] # imagetype 25 | 26 | for idx in xrange(len(pidlist)): 27 | if idx % 1000 == 0: 28 | print idx, imagetypelist[idx], reconfilterlist[idx], pidlist[idx], studyidlist[idx], seriesinstlist[idx] 29 | if 'local' in imagetypelist[idx].lower(): 30 | continue 31 | if 'lung' not in reconfilterlist[idx].lower(): 32 | continue 33 | if pidlist[idx] not in uiddict: 34 | uiddict[pidlist[idx]] = [[studyidlist[idx], seriesinstlist[idx]]] 35 | else: 36 | uiddict[pidlist[idx]].append([studyidlist[idx], seriesinstlist[idx]]) 37 | # ncp = 0 38 | def cpfunc(pid): 39 | if pid.endswith('.zip') and pid[:-4] in uiddict: 40 | print 'cp', pid 41 | # ncp += 1 42 | # if ncp % 100 == 0: 43 | # print ncp, pid 44 | shutil.copy(filepath+pid, dstpath+pid) 45 | def delfunc(pid): 46 | if pid.endswith('.zip') and pid[:-4] not in uiddict: 47 | print 'del', pid 48 | os.remove(dstpath+pid) 49 | # ndel += 1 50 | def unzipfunc(pid): 51 | if pid.endswith('.zip'): 52 | zip_ref = zipfile.ZipFile(dstpath+pid, 'r') 53 | zip_ref.extractall(dstpath+pid[:-4]+'/') 54 | zip_ref.close() 55 | os.remove(dstpath+pid) 56 | 57 | p = Pool(15) 58 | # p.map(cpfunc, os.listdir(filepath)) 59 | # p.map(delfunc, os.listdir(dstpath)) 60 | p.map(unzipfunc, os.listdir(dstpath)) 61 | p.close() 62 | 63 | # for pid in os.listdir(filepath): 64 | # if pid.endswith('.zip') and pid[:-4] in uiddict: 65 | # ncp += 1 66 | # if ncp % 100 == 0: 67 | # print ncp, pid 68 | # shutil.copy(filepath+pid, dstpath+pid) 69 | 70 | # ndel = 0 71 | # for pid in os.listdir(dstpath): 72 | # if pid.endswith('.zip') and pid[:-4] not in uiddict: 73 | # shutil.rmtree(dstpath+pid) 74 | # ndel += 1 75 | # print '#del', ndel 76 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DeepEM-for-Weakly-Supervised-Detection 2 | MICCAI18 early accepted paper DeepEM: Deep 3D ConvNets with EM for Weakly Supervised Pulmonary Nodule Detection 3 | 4 | Please add paper into reference if the repository is helpful to you. "DeepEM: Deep 3D ConvNets With EM For Weakly Supervised Pulmonary Nodule Detection." MICCAI, 2018" 5 | 6 | Please read our DeepLung paper "Zhu, Wentao, Chaochun Liu, Wei Fan, and Xiaohui Xie. "DeepLung: Deep 3D Dual Path Nets for Automated Pulmonary Nodule Detection and Classification." IEEE WACV, 2018." to get an idea for the baseline method. 7 | 8 | To run the code, first get familiar with it (environments, required packages) from DeepLung code https://github.com/wentaozhu/DeepLung.git 9 | 10 | runweaktrainingressamp.sh is for DeepEM with sampling. 11 | 12 | runweaktrainingresv2.sh is for DeepEM with MAP. 13 | 14 | You can download NLST dataset from https://biometry.nci.nih.gov/cdas/nlst/ and use the scripts in ./NLST/ to clean the dataset. 15 | 16 | The used TianChi hold out test set is in https://drive.google.com/drive/folders/1YEnxCtDplTfTp7OVKUGrXy1N8s2YZ7gP?usp=sharing. 17 | 18 | For any problems, feel free to report issues in the GitHub (preferred) or contact Wentao Zhu (wentaozhu1991@gmail.com) 19 | -------------------------------------------------------------------------------- /config_testtchi.py: -------------------------------------------------------------------------------- 1 | config = {'test_data_path':['/mnt/easy/wentaobaidu/153/tianchi/newtest/'], 2 | 'val_data_path':['/mnt/easy/wentaobaidu/153/tianchi/newtest/'], 3 | 'train_data_path':['/mnt/easy/wentaobaidu/153/tianchi/newtest/'], 4 | 5 | 'train_preprocess_result_path':'/mnt/easy/wentaobaidu/153/tianchi/preprocessing/newtest/', 6 | 'val_preprocess_result_path':'/mnt/easy/wentaobaidu/153/tianchi/preprocessing/newtest/', 7 | 'test_preprocess_result_path':'/mnt/easy/wentaobaidu/153/tianchi/preprocessing/newtest/', 8 | 9 | 'train_annos_path':'/mnt/easy/wentaobaidu/153/tianchi/luna16/CSVFILES/annotations.csv', 10 | 'val_annos_path':'/mnt/easy/wentaobaidu/153/tianchi/csv/newtest/annotations.csv', 11 | 'test_annos_path':'/mnt/easy/wentaobaidu/153/tianchi/csv/newtest/annotations.csv', 12 | 13 | 'black_list':['LKDS-00192', 'LKDS-00319', 'LKDS-00238', 'LKDS-00926', 'LKDS-00504', 14 | 'LKDS-00648', 'LKDS-00829', 'LKDS-00931', 'LKDS-00359', 'LKDS-00379', 15 | 'LKDS-00541', 'LKDS-00353', 'LKDS-00598', 'LKDS-00684', 'LKDS-00065'], 16 | 17 | 'preprocessing_backend':'python', 18 | 19 | 'luna_segment':'/mnt/easy/wentaobaidu/153/tianchi/luna16/seg-lungs-LUNA16/', 20 | 'preprocess_result_path':'/mnt/easy/wentaobaidu/153/tianchi/luna16/preprocess/', 21 | 'luna_data':'/mnt/easy/wentaobaidu/153/tianchi/luna16/', 22 | 'luna_label':'/mnt/easy/wentaobaidu/153/tianchi/luna16/CSVFILES/annotations.csv' 23 | } # LKDS-00648 - end is found by the labelmapping function 24 | # 'LKDS-00192','LKDS-00319','LKDS-00238','LKDS-00926', 'LKDS-00504' is found from preprocessing 25 | # 'LKDS-00504', 26 | -------------------------------------------------------------------------------- /config_trainingweak0.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset3/', 5 | '../../luna16/subset4/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset0/'], 11 | 'test_data_path':['../../luna16/subset0/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak1.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset0/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset3/', 5 | '../../luna16/subset4/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset1/'], 11 | 'test_data_path':['../../luna16/subset1/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } 28 | -------------------------------------------------------------------------------- /config_trainingweak2.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset0/', 4 | '../../luna16/subset3/', 5 | '../../luna16/subset4/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset2/'], 11 | 'test_data_path':['../../luna16/subset2/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak3.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset4/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset3/'], 11 | 'test_data_path':['../../luna16/subset3/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak4.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset3/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset4/'], 11 | 'test_data_path':['../../luna16/subset4/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak5.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset3/', 6 | '../../luna16/subset4/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset5/'], 11 | 'test_data_path':['../../luna16/subset5/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak6.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset3/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset4/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset6/'], 11 | 'test_data_path':['../../luna16/subset6/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak7.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset3/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset4/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset7/'], 11 | 'test_data_path':['../../luna16/subset7/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak8.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset9/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset3/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset4/'], 10 | 'val_data_path':['../../luna16/subset8/'], 11 | 'test_data_path':['../../luna16/subset8/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /config_trainingweak9.py: -------------------------------------------------------------------------------- 1 | config = {'train_data_path':['../../luna16/subset4/', 2 | '../../luna16/subset1/', 3 | '../../luna16/subset2/', 4 | '../../luna16/subset0/', 5 | '../../luna16/subset3/', 6 | '../../luna16/subset5/', 7 | '../../luna16/subset6/', 8 | '../../luna16/subset7/', 9 | '../../luna16/subset8/'], 10 | 'val_data_path':['../../luna16/subset9/'], 11 | 'test_data_path':['../../luna16/subset9/'], 12 | 13 | 'train_preprocess_result_path':'../../luna16/preprocess/', 14 | 'val_preprocess_result_path':'../../luna16/preprocess/', 15 | 'test_preprocess_result_path':'../../luna16/preprocess/', 16 | 17 | 'train_annos_path':'../../luna16/CSVFILES/newannotations.csv', 18 | 'val_annos_path':'../../luna16/CSVFILES/newannotations.csv', 19 | 'test_annos_path':'../../luna16/CSVFILES/newannotations.csv', 20 | 21 | 'weaktrain_data_path':'../../NLST/preprocessnp/', 22 | 'weaktrain_annos_path':'../../NLST/calibweaklabelallsmall.csv', 23 | 24 | 'black_list':[], 25 | 26 | 'preprocessing_backend':'python', 27 | } -------------------------------------------------------------------------------- /lobepositionpredacc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/lobepositionpredacc.png -------------------------------------------------------------------------------- /revfname.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | fid = open('fnamedct.csv', 'w') 4 | writer = csv.writer(fid) 5 | writer.writerow(['fname', 'newname']) 6 | dct = {} 7 | curk = 0 8 | path = '../luna16/subset' 9 | for f in xrange(10): 10 | for fname in os.listdir(path+str(f)): 11 | if fname.endswith('raw'): 12 | dct[fname[:-4]] = '{:05d}'.format(curk) 13 | writer.writerow([fname[:-4], dct[fname[:-4]]]) 14 | curk += 1 15 | fid.close() 16 | print('number of files', curk) 17 | import pandas as pd 18 | # process seriesuid.csv 19 | name = '1.3.6.1.4.1.14519.5.2.1.6279.6001.100225287222365663678666836860' 20 | sdfrm = pd.read_csv('./evaluationScript/annotations/seriesuids.csv', \ 21 | names=[name]) 22 | fnamelst = sdfrm[name].tolist() 23 | print('number of series uid', len(fnamelst)) 24 | fid = open('./evaluationScript/annotations/newseriesuids.csv', 'w') 25 | writer = csv.writer(fid) 26 | for fname in fnamelst: 27 | writer.writerow([dct[fname]]) 28 | fid.close() 29 | # process annotations_excluded.csv 30 | name = ['seriesuid', 'coordX', 'coordY', 'coordZ', 'diameter_mm'] 31 | antexd = pd.read_csv('./evaluationScript/annotations/annotations_excluded.csv', names=name) 32 | srsidlst = antexd[name[0]].tolist()[1:] 33 | crdxlst = antexd[name[1]].tolist()[1:] 34 | crdylst = antexd[name[2]].tolist()[1:] 35 | crdzlst = antexd[name[3]].tolist()[1:] 36 | dimlst = antexd[name[4]].tolist()[1:] 37 | fid = open('./evaluationScript/annotations/newannotations_excluded.csv', 'w') 38 | writer = csv.writer(fid) 39 | writer.writerow(name) 40 | for i in xrange(len(srsidlst)): 41 | writer.writerow([dct[srsidlst[i]], crdxlst[i], crdylst[i], crdzlst[i], dimlst[i]]) 42 | fid.close() 43 | # process annotations.csv 44 | name = ['seriesuid', 'coordX', 'coordY', 'coordZ', 'diameter_mm'] 45 | ant = pd.read_csv('./evaluationScript/annotations/annotations.csv', names=name) 46 | srsidlst = ant[name[0]].tolist()[1:] 47 | crdxlst = ant[name[1]].tolist()[1:] 48 | crdylst = ant[name[2]].tolist()[1:] 49 | crdzlst = ant[name[3]].tolist()[1:] 50 | dimlst = ant[name[4]].tolist()[1:] 51 | fid = open('./evaluationScript/annotations/newannotations.csv', 'w') 52 | writer = csv.writer(fid) 53 | writer.writerow(name) 54 | for i in xrange(len(srsidlst)): 55 | writer.writerow([dct[srsidlst[i]], crdxlst[i], crdylst[i], crdzlst[i], dimlst[i]]) 56 | fid.close() 57 | # process luna16 annotations.csv 58 | name = ['seriesuid', 'coordX', 'coordY', 'coordZ', 'diameter_mm'] 59 | ant = pd.read_csv('../luna16/CSVFILES/annotations.csv', names=name) 60 | srsidlst = ant[name[0]].tolist()[1:] 61 | crdxlst = ant[name[1]].tolist()[1:] 62 | crdylst = ant[name[2]].tolist()[1:] 63 | crdzlst = ant[name[3]].tolist()[1:] 64 | dimlst = ant[name[4]].tolist()[1:] 65 | fid = open('../luna16/CSVFILES/newannotations.csv', 'w') 66 | writer = csv.writer(fid) 67 | writer.writerow(name) 68 | for i in xrange(len(srsidlst)): 69 | writer.writerow([dct[srsidlst[i]], crdxlst[i], crdylst[i], crdzlst[i], dimlst[i]]) 70 | fid.close() 71 | # revise file name 72 | # Do it in the prepare.py to save space. -------------------------------------------------------------------------------- /run_testtchi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | cd weakdetectorv2 4 | f=0 #7 5 | i=47 #4 6 | CUDA_VISIBLE_DEVICES=0,1,2,3 python main.py --model res18 -b 48 --resume results/res18/weakfd$f/0$i.ckpt --test 1 --testthresh -3 --save-dir res18/weakfd$f/ --config config_testtchi 7 | if [ ! -d "results/res18/weakfd$f/traintchi/" ]; then 8 | mkdir results/res18/weakfd$f/traintchi/ 9 | fi 10 | mv results/res18/weakfd$f/bbox/*.npy results/res18/weakfd$f/traintchi/ -------------------------------------------------------------------------------- /runweaktrainingressamp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # python prepare.py 5 | cd weakdetectorsamp 6 | maxeps=80 7 | f=6 #2 8 | # CUDA_VISIBLE_DEVICES=2,3,0,1 python main.py --model res18 -b 32 --resume ../weakdetector/results/res18/weakfd$f/weak018.ckpt --start-epoch 1 --save-dir res18/weakfd$f/ --epochs $maxeps --config config_trainingweak$f 9 | for (( i=11; i<=$maxeps; i+=1)) 10 | do 11 | echo "process $i epoch" 12 | if [ $i -lt 10 ]; then 13 | CUDA_VISIBLE_DEVICES=2,3,0,1 python main.py --model res18 -b 32 --resume results/res18/weakfd$f/00$i.ckpt --test 1 --save-dir res18/weakfd$f/ --config config_trainingweak$f 14 | elif [ $i -lt 100 ]; then 15 | CUDA_VISIBLE_DEVICES=2,3,0,1 python main.py --model res18 -b 32 --resume results/res18/weakfd$f/0$i.ckpt --test 1 --save-dir res18/weakfd$f/ --config config_trainingweak$f 16 | elif [ $i -lt 1000 ]; then 17 | CUDA_VISIBLE_DEVICES=2,3,0,1 python main.py --model res18 -b 32 --resume results/res18/weakfd$f/$i.ckpt --test 1 --save-dir res18/weakfd$f/ --config config_trainingweak$f 18 | else 19 | echo "Unhandled case" 20 | fi 21 | if [ ! -d "results/res18/weakfd$f/val$i/" ]; then 22 | mkdir results/res18/weakfd$f/val$i/ 23 | fi 24 | mv results/res18/weakfd$f/bbox/*.npy results/res18/weakfd$f/val$i/ 25 | done 26 | -------------------------------------------------------------------------------- /runweaktrainingresv2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | # python prepare.py 5 | cd weakdetectorv2 6 | maxeps=50 7 | f=4 #2 ../weakdetector/results/res18/weakfd$f/weak029.ckpt 8 | # Training 9 | # CUDA_VISIBLE_DEVICES=0,2,3,1 python main.py --model res18 -b 32 --resume ../weakdetector/results/res18/weakfd$f/weak024.ckpt --start-epoch 1 --save-dir res18/weakfd$f/ --epochs $maxeps --config config_trainingweak$f 10 | # Inference 11 | for (( i=1; i<=$maxeps; i+=1)) 12 | do 13 | echo "process $i epoch" 14 | if [ $i -lt 10 ]; then 15 | CUDA_VISIBLE_DEVICES=0,1,2,3 python main.py --model res18 -b 32 --resume results/res18/weakfd$f/00$i.ckpt --test 1 --save-dir res18/weakfd$f/ --config config_trainingweak$f 16 | elif [ $i -lt 100 ]; then 17 | CUDA_VISIBLE_DEVICES=0,1,2,3 python main.py --model res18 -b 32 --resume results/res18/weakfd$f/0$i.ckpt --test 1 --save-dir res18/weakfd$f/ --config config_trainingweak$f 18 | elif [ $i -lt 1000 ]; then 19 | CUDA_VISIBLE_DEVICES=0,1,2,3 python main.py --model res18 -b 32 --resume results/res18/weakfd$f/$i.ckpt --test 1 --save-dir res18/weakfd$f/ --config config_trainingweak$f 20 | else 21 | echo "Unhandled case" 22 | fi 23 | if [ ! -d "results/res18/weakfd$f/val$i/" ]; then 24 | mkdir results/res18/weakfd$f/val$i/ 25 | fi 26 | mv results/res18/weakfd$f/bbox/*.npy results/res18/weakfd$f/val$i/ 27 | done -------------------------------------------------------------------------------- /weakdetclslos.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetclslos.png -------------------------------------------------------------------------------- /weakdetectorsamp/data.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorsamp/data.pyc -------------------------------------------------------------------------------- /weakdetectorsamp/detect.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from layers import nms, iou, acc 3 | import time 4 | import multiprocessing as mp 5 | 6 | save_dir = 'results/ma_offset40_res_n6_100-1/' 7 | pbb = np.load(save_dir + 'pbb.npy') 8 | lbb = np.load(save_dir + 'lbb.npy') 9 | 10 | conf_th = [-1, 0, 1] 11 | nms_th = [0.3, 0.5, 0.7] 12 | detect_th = [0.2, 0.3] 13 | def mp_get_pr(conf_th, nms_th, detect_th, num_procs = 64): 14 | start_time = time.time() 15 | 16 | num_samples = len(pbb) 17 | split_size = int(np.ceil(float(num_samples) / num_procs)) 18 | num_procs = int(np.ceil(float(num_samples) / split_size)) 19 | 20 | manager = mp.Manager() 21 | tp = manager.list(range(num_procs)) 22 | fp = manager.list(range(num_procs)) 23 | p = manager.list(range(num_procs)) 24 | procs = [] 25 | for pid in range(num_procs): 26 | proc = mp.Process( 27 | target = get_pr, 28 | args = ( 29 | pbb[pid * split_size:min((pid + 1) * split_size, num_samples)], 30 | lbb[pid * split_size:min((pid + 1) * split_size, num_samples)], 31 | conf_th, nms_th, detect_th, pid, tp, fp, p)) 32 | procs.append(proc) 33 | proc.start() 34 | 35 | for proc in procs: 36 | proc.join() 37 | 38 | tp = np.sum(tp) 39 | fp = np.sum(fp) 40 | p = np.sum(p) 41 | 42 | end_time = time.time() 43 | print('conf_th %1.1f, nms_th %1.1f, detect_th %1.1f, tp %d, fp %d, p %d, recall %f, time %3.2f' % (conf_th, nms_th, detect_th, tp, fp, p, float(tp) / p, end_time - start_time)) 44 | 45 | def get_pr(pbb, lbb, conf_th, nms_th, detect_th, pid, tp_list, fp_list, p_list): 46 | tp, fp, p = 0, 0, 0 47 | for i in range(len(pbb)): 48 | tpi, fpi, pi = acc(pbb[i], lbb[i], conf_th, nms_th, detect_th) 49 | tp += tpi 50 | fp += fpi 51 | p += pi 52 | tp_list[pid] = tp 53 | fp_list[pid] = fp 54 | p_list[pid] = p 55 | 56 | if __name__ == '__main__': 57 | for ct in conf_th: 58 | for nt in nms_th: 59 | for dt in detect_th: 60 | mp_get_pr(ct, nt, dt) 61 | -------------------------------------------------------------------------------- /weakdetectorsamp/dpn3d.py: -------------------------------------------------------------------------------- 1 | '''Dual Path Networks in PyTorch.''' 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.autograd import Variable 6 | from layers import * 7 | config = {} 8 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 9 | config['chanel'] = 1 10 | config['crop_size'] = [96, 96, 96] 11 | config['stride'] = 4 12 | config['max_stride'] = 16 13 | config['num_neg'] = 800 14 | config['th_neg'] = 0.02 15 | config['th_pos_train'] = 0.5 16 | config['th_pos_val'] = 1 17 | config['num_hard'] = 2 18 | config['bound_size'] = 12 19 | config['reso'] = 1 20 | config['sizelim'] = 2.5 #3 #6. #mm 21 | config['sizelim2'] = 10 #30 22 | config['sizelim3'] = 20 #40 23 | config['aug_scale'] = True 24 | config['r_rand_crop'] = 0.3 25 | config['pad_value'] = 170 26 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | debug = False #True#False#True#True #True#False #True 30 | 31 | class Bottleneck(nn.Module): 32 | def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): 33 | super(Bottleneck, self).__init__() 34 | self.out_planes = out_planes 35 | self.dense_depth = dense_depth 36 | self.last_planes = last_planes 37 | self.in_planes = in_planes 38 | 39 | self.conv1 = nn.Conv3d(last_planes, in_planes, kernel_size=1, bias=False) 40 | self.bn1 = nn.BatchNorm3d(in_planes) 41 | self.conv2 = nn.Conv3d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=1, bias=False) 42 | self.bn2 = nn.BatchNorm3d(in_planes) 43 | self.conv3 = nn.Conv3d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) # 1 44 | self.bn3 = nn.BatchNorm3d(out_planes+dense_depth) 45 | 46 | self.shortcut = nn.Sequential() 47 | if first_layer: 48 | self.shortcut = nn.Sequential( 49 | nn.Conv3d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), 50 | nn.BatchNorm3d(out_planes+dense_depth) 51 | ) 52 | 53 | def forward(self, x): 54 | if debug: 55 | print 'bottleneck_0', x.size(), self.last_planes, self.in_planes, 1 56 | out = F.relu(self.bn1(self.conv1(x))) 57 | if debug: 58 | print 'bottleneck_1', out.size(), self.in_planes, self.in_planes, 3 59 | out = F.relu(self.bn2(self.conv2(out))) 60 | if debug: 61 | print 'bottleneck_2', out.size(), self.in_planes, self.out_planes+self.dense_depth, 1 62 | out = self.bn3(self.conv3(out)) 63 | if debug: 64 | print 'bottleneck_3', out.size() 65 | x = self.shortcut(x) 66 | d = self.out_planes 67 | if debug: 68 | print 'bottleneck_4', x.size(), self.last_planes, self.out_planes+self.dense_depth, d 69 | out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) 70 | if debug: 71 | print 'bottleneck_5', out.size() 72 | out = F.relu(out) 73 | return out 74 | 75 | class DPN(nn.Module): 76 | def __init__(self, cfg): 77 | super(DPN, self).__init__() 78 | in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] 79 | num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] 80 | # self.in_planes = in_planes 81 | # self.out_planes = out_planes 82 | # self.num_blocks = num_blocks 83 | # self.dense_depth = dense_depth 84 | self.conv1 = nn.Conv3d(1, 24, kernel_size=3, stride=1, padding=1, bias=False) 85 | self.bn1 = nn.BatchNorm3d(24) 86 | self.last_planes = 24 87 | self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=2)#stride=1) 88 | self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) 89 | self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) 90 | self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) 91 | self.last_planes = 136 92 | self.layer5 = self._make_layer(64, 64, num_blocks[2], dense_depth[2], stride=1) 93 | self.last_planes = 128+3 94 | self.layer6 = self._make_layer(128, 128, num_blocks[1], dense_depth[1], stride=1) 95 | self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 2)#10) 96 | self.last_planes = 72 97 | self.path1 = nn.Sequential( 98 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 99 | nn.BatchNorm3d(self.last_planes), 100 | nn.ReLU(inplace = True)) 101 | self.last_planes = 86 102 | self.path2 = nn.Sequential( 103 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 104 | nn.BatchNorm3d(self.last_planes), 105 | nn.ReLU(inplace = True)) 106 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 107 | self.output = nn.Sequential(nn.Conv3d(138, 64, kernel_size = 1), 108 | nn.ReLU(), 109 | #nn.Dropout3d(p = 0.3), 110 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 111 | def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): 112 | strides = [stride] + [1]*(num_blocks-1) 113 | layers = [] 114 | for i,stride in enumerate(strides): 115 | # if debug: print(i, self.last_planes, in_planes, out_planes, dense_depth) 116 | layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) 117 | self.last_planes = out_planes + (i+2) * dense_depth 118 | return nn.Sequential(*layers) 119 | 120 | def forward(self, x, coord): 121 | if debug: print '0', x.size(), 64#, coord.size 122 | out0 = F.relu(self.bn1(self.conv1(x))) 123 | if debug: print '1', out0.size() 124 | out1 = self.layer1(out0) 125 | if debug: print '2', out1.size() 126 | out2 = self.layer2(out1) 127 | if debug: print '3', out2.size() 128 | out3 = self.layer3(out2) 129 | if debug: print '4', out3.size() 130 | out4 = self.layer4(out3) 131 | if debug: print '5', out4.size() 132 | 133 | out5 = self.path1(out4) 134 | if debug: print '6', out5.size(), torch.cat((out3, out5), 1).size() 135 | out6 = self.layer5(torch.cat((out3, out5), 1)) 136 | if debug: print '7', out6.size() 137 | out7 = self.path2(out6) 138 | if debug: print '8', out7.size(), torch.cat((out2, out7), 1).size() #torch.cat((out2, out7, coord), 1).size() 139 | out8 = self.layer6(torch.cat((out2, out7, coord), 1)) 140 | if debug: print '9', out8.size() 141 | comb2 = self.drop(out8) 142 | out = self.output(comb2) 143 | if debug: print '10', out.size() 144 | size = out.size() 145 | out = out.view(out.size(0), out.size(1), -1) 146 | if debug: print '11', out.size() 147 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 148 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 149 | if debug: print '12', out.size() 150 | return out#, out_1 151 | 152 | def DPN92_3D(): 153 | cfg = { 154 | 'in_planes': (24,32,42,64),#(96,192,384,768), 155 | 'out_planes': (24,32,42,64),#(256,512,1024,2048), 156 | 'num_blocks': (3,4,10,3), 157 | 'dense_depth': (2,2,2,2) 158 | } 159 | return DPN(cfg) 160 | 161 | def get_model(): 162 | net = DPN92_3D() 163 | loss = Loss(config['num_hard']) 164 | get_pbb = GetPBB(config) 165 | return config, net, loss, get_pbb 166 | 167 | 168 | def test(): 169 | debug = True 170 | net = DPN92_3D() 171 | x = Variable(torch.randn(1,1,96,96,96)) 172 | crd = Variable(torch.randn(1,3,24,24,24)) 173 | y = net(x, crd) 174 | # print(y) 175 | 176 | # test() -------------------------------------------------------------------------------- /weakdetectorsamp/dpn3d26.py: -------------------------------------------------------------------------------- 1 | '''Dual Path Networks in PyTorch.''' 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.autograd import Variable 6 | from layers import * 7 | config = {} 8 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 9 | config['chanel'] = 1 10 | config['crop_size'] = [96, 96, 96] 11 | config['stride'] = 4 12 | config['max_stride'] = 16 13 | config['num_neg'] = 800 14 | config['th_neg'] = 0.02 15 | config['th_pos_train'] = 0.5 16 | config['th_pos_val'] = 1 17 | config['num_hard'] = 2 18 | config['bound_size'] = 12 19 | config['reso'] = 1 20 | config['sizelim'] = 2.5 #3 #6. #mm 21 | config['sizelim2'] = 10 #30 22 | config['sizelim3'] = 20 #40 23 | config['aug_scale'] = True 24 | config['r_rand_crop'] = 0.3 25 | config['pad_value'] = 170 26 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | config['ep'] = 1 30 | config['save_dir'] = './' 31 | debug = False #True #True#False #True 32 | 33 | class Bottleneck(nn.Module): 34 | def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): 35 | super(Bottleneck, self).__init__() 36 | self.out_planes = out_planes 37 | self.dense_depth = dense_depth 38 | self.last_planes = last_planes 39 | self.in_planes = in_planes 40 | 41 | self.conv1 = nn.Conv3d(last_planes, in_planes, kernel_size=1, bias=False) 42 | self.bn1 = nn.BatchNorm3d(in_planes) 43 | self.conv2 = nn.Conv3d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=8, bias=False) 44 | self.bn2 = nn.BatchNorm3d(in_planes) 45 | self.conv3 = nn.Conv3d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) 46 | self.bn3 = nn.BatchNorm3d(out_planes+dense_depth) 47 | 48 | self.shortcut = nn.Sequential() 49 | if first_layer: 50 | self.shortcut = nn.Sequential( 51 | nn.Conv3d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), 52 | nn.BatchNorm3d(out_planes+dense_depth) 53 | ) 54 | 55 | def forward(self, x): 56 | if debug: 57 | print 'bottleneck_0', x.size(), self.last_planes, self.in_planes, 1 58 | out = F.relu(self.bn1(self.conv1(x))) 59 | if debug: 60 | print 'bottleneck_1', out.size(), self.in_planes, self.in_planes, 3 61 | out = F.relu(self.bn2(self.conv2(out))) 62 | if debug: 63 | print 'bottleneck_2', out.size(), self.in_planes, self.out_planes+self.dense_depth, 1 64 | out = self.bn3(self.conv3(out)) 65 | if debug: 66 | print 'bottleneck_3', out.size() 67 | x = self.shortcut(x) 68 | d = self.out_planes 69 | if debug: 70 | print 'bottleneck_4', x.size(), self.last_planes, self.out_planes+self.dense_depth, d 71 | out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) 72 | if debug: 73 | print 'bottleneck_5', out.size() 74 | out = F.relu(out) 75 | return out 76 | 77 | class DPN(nn.Module): 78 | def __init__(self, cfg): 79 | super(DPN, self).__init__() 80 | in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] 81 | num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] 82 | 83 | # self.in_planes = in_planes 84 | # self.out_planes = out_planes 85 | # self.num_blocks = num_blocks 86 | # self.dense_depth = dense_depth 87 | 88 | self.conv1 = nn.Conv3d(1, 24, kernel_size=3, stride=1, padding=1, bias=False) 89 | self.bn1 = nn.BatchNorm3d(24) 90 | self.last_planes = 24 91 | self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=2)#stride=1) 92 | self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) 93 | self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) 94 | self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) 95 | self.last_planes = 216 96 | self.layer5 = self._make_layer(128, 128, num_blocks[2], dense_depth[2], stride=1) 97 | self.last_planes = 224+3 98 | self.layer6 = self._make_layer(224, 224, num_blocks[1], dense_depth[1], stride=1) 99 | self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 2)#10) 100 | self.last_planes = 120 101 | self.path1 = nn.Sequential( 102 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 103 | nn.BatchNorm3d(self.last_planes), 104 | nn.ReLU(inplace = True)) 105 | self.last_planes = 152 106 | self.path2 = nn.Sequential( 107 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 108 | nn.BatchNorm3d(self.last_planes), 109 | nn.ReLU(inplace = True)) 110 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 111 | self.output = nn.Sequential(nn.Conv3d(248, 64, kernel_size = 1), 112 | nn.ReLU(), 113 | #nn.Dropout3d(p = 0.3), 114 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 115 | def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): 116 | strides = [stride] + [1]*(num_blocks-1) 117 | layers = [] 118 | for i,stride in enumerate(strides): 119 | if debug: print(i, self.last_planes, in_planes, out_planes, dense_depth) 120 | layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) 121 | self.last_planes = out_planes + (i+2) * dense_depth 122 | return nn.Sequential(*layers) 123 | 124 | def forward(self, x, coord): 125 | if debug: print '0', x.size(), 64#, coord.size 126 | out0 = F.relu(self.bn1(self.conv1(x))) 127 | if debug: print '1', out0.size() 128 | out1 = self.layer1(out0) 129 | if debug: print '2', out1.size() 130 | out2 = self.layer2(out1) 131 | if debug: print '3', out2.size() 132 | out3 = self.layer3(out2) 133 | if debug: print '4', out3.size() 134 | out4 = self.layer4(out3) 135 | if debug: print '5', out4.size() 136 | 137 | out5 = self.path1(out4) 138 | if debug: print '6', out5.size(), torch.cat((out3, out5), 1).size() 139 | out6 = self.layer5(torch.cat((out3, out5), 1)) 140 | if debug: print '7', out6.size() 141 | out7 = self.path2(out6) 142 | if debug: print '8', out7.size(), torch.cat((out2, out7), 1).size() #torch.cat((out2, out7, coord), 1).size() 143 | out8 = self.layer6(torch.cat((out2, out7, coord), 1)) 144 | if debug: print '9', out8.size() 145 | comb2 = self.drop(out8) 146 | out = self.output(comb2) 147 | if debug: print '10', out.size() 148 | size = out.size() 149 | out = out.view(out.size(0), out.size(1), -1) 150 | if debug: print '11', out.size() 151 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 152 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 153 | if debug: print '12', out.size() 154 | return out#, out_1 155 | 156 | def DPN92_3D(): 157 | cfg = { 158 | 'in_planes': (24,48,72,96),#(96,192,384,768), 159 | 'out_planes': (24,48,72,96),#(256,512,1024,2048), 160 | 'num_blocks': (2,2,2,2), 161 | 'dense_depth': (8,8,8,8) 162 | } 163 | return DPN(cfg) 164 | 165 | def get_model(): 166 | net = DPN92_3D() 167 | loss = Loss(config['num_hard']) 168 | get_pbb = GetPBB(config) 169 | return config, net, loss, get_pbb 170 | 171 | 172 | def test(): 173 | debug = True 174 | net = DPN92_3D() 175 | x = Variable(torch.randn(1,1,96,96,96)) 176 | crd = Variable(torch.randn(1,3,24,24,24)) 177 | y = net(x, crd) 178 | # print(y) 179 | 180 | # test() -------------------------------------------------------------------------------- /weakdetectorsamp/dpn3dold.py: -------------------------------------------------------------------------------- 1 | '''Dual Path Networks in PyTorch.''' 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.autograd import Variable 6 | from layers import * 7 | config = {} 8 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 9 | config['chanel'] = 1 10 | config['crop_size'] = [96, 96, 96] 11 | config['stride'] = 4 12 | config['max_stride'] = 16 13 | config['num_neg'] = 800 14 | config['th_neg'] = 0.02 15 | config['th_pos_train'] = 0.5 16 | config['th_pos_val'] = 1 17 | config['num_hard'] = 2 18 | config['bound_size'] = 12 19 | config['reso'] = 1 20 | config['sizelim'] = 2.5 #3 #6. #mm 21 | config['sizelim2'] = 10 #30 22 | config['sizelim3'] = 20 #40 23 | config['aug_scale'] = True 24 | config['r_rand_crop'] = 0.3 25 | config['pad_value'] = 170 26 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | debug = False #True#False #True 30 | 31 | class Bottleneck(nn.Module): 32 | def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): 33 | super(Bottleneck, self).__init__() 34 | self.out_planes = out_planes 35 | self.dense_depth = dense_depth 36 | self.last_planes = last_planes 37 | self.in_planes = in_planes 38 | 39 | self.conv1 = nn.Conv3d(last_planes, in_planes, kernel_size=1, bias=False) 40 | self.bn1 = nn.BatchNorm3d(in_planes) 41 | self.conv2 = nn.Conv3d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=32, bias=False) 42 | self.bn2 = nn.BatchNorm3d(in_planes) 43 | self.conv3 = nn.Conv3d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) 44 | self.bn3 = nn.BatchNorm3d(out_planes+dense_depth) 45 | 46 | self.shortcut = nn.Sequential() 47 | if first_layer: 48 | self.shortcut = nn.Sequential( 49 | nn.Conv3d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), 50 | nn.BatchNorm3d(out_planes+dense_depth) 51 | ) 52 | 53 | def forward(self, x): 54 | # print 'bottleneck_0', x.size(), self.last_planes, self.in_planes, 1 55 | out = F.relu(self.bn1(self.conv1(x))) 56 | # print 'bottleneck_1', out.size(), self.in_planes, self.in_planes, 3 57 | out = F.relu(self.bn2(self.conv2(out))) 58 | # print 'bottleneck_2', out.size(), self.in_planes, self.out_planes+self.dense_depth, 1 59 | out = self.bn3(self.conv3(out)) 60 | # print 'bottleneck_3', out.size() 61 | x = self.shortcut(x) 62 | d = self.out_planes 63 | # print 'bottleneck_4', x.size(), self.last_planes, self.out_planes+self.dense_depth, d 64 | out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) 65 | # print 'bottleneck_5', out.size() 66 | out = F.relu(out) 67 | return out 68 | 69 | class DPN(nn.Module): 70 | def __init__(self, cfg): 71 | super(DPN, self).__init__() 72 | in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] 73 | num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] 74 | 75 | # self.in_planes = in_planes 76 | # self.out_planes = out_planes 77 | # self.num_blocks = num_blocks 78 | # self.dense_depth = dense_depth 79 | 80 | self.conv1 = nn.Conv3d(1, 64, kernel_size=3, stride=1, padding=1, bias=False) 81 | self.bn1 = nn.BatchNorm3d(64) 82 | self.last_planes = 64 83 | self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=2)#stride=1) 84 | self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) 85 | self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) 86 | self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) 87 | self.last_planes = 736 88 | self.layer5 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=1) 89 | self.last_planes = 656+3 90 | self.layer6 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=1) 91 | self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 2)#10) 92 | self.last_planes = 256 93 | self.path1 = nn.Sequential( 94 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 95 | nn.BatchNorm3d(self.last_planes), 96 | nn.ReLU(inplace = True)) 97 | self.last_planes = 480 98 | self.path2 = nn.Sequential( 99 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 100 | nn.BatchNorm3d(self.last_planes), 101 | nn.ReLU(inplace = True)) 102 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 103 | self.output = nn.Sequential(nn.Conv3d(176, 64, kernel_size = 1), 104 | nn.ReLU(), 105 | #nn.Dropout3d(p = 0.3), 106 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 107 | def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): 108 | strides = [stride] + [1]*(num_blocks-1) 109 | layers = [] 110 | for i,stride in enumerate(strides): 111 | layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) 112 | self.last_planes = out_planes + (i+2) * dense_depth 113 | return nn.Sequential(*layers) 114 | 115 | def forward(self, x, coord): 116 | if debug: print '0', x.size(), 64#, coord.size 117 | out0 = F.relu(self.bn1(self.conv1(x))) 118 | if debug: print '1', out0.size() 119 | out1 = self.layer1(out0) 120 | if debug: print '2', out1.size() 121 | out2 = self.layer2(out1) 122 | if debug: print '3', out2.size() 123 | out3 = self.layer3(out2) 124 | if debug: print '4', out3.size() 125 | out4 = self.layer4(out3) 126 | if debug: print '5', out4.size() 127 | 128 | out5 = self.path1(out4) 129 | if debug: print '6', out5.size(), torch.cat((out3, out5), 1).size() 130 | out6 = self.layer5(torch.cat((out3, out5), 1)) 131 | if debug: print '7', out6.size() 132 | out7 = self.path2(out6) 133 | if debug: print '8', out7.size(), torch.cat((out2, out7), 1).size() #torch.cat((out2, out7, coord), 1).size() 134 | out8 = self.layer6(torch.cat((out2, out7, coord), 1)) 135 | if debug: print '9', out8.size() 136 | comb2 = self.drop(out8) 137 | out = self.output(comb2) 138 | if debug: print '10', out.size() 139 | size = out.size() 140 | out = out.view(out.size(0), out.size(1), -1) 141 | if debug: print '11', out.size() 142 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 143 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 144 | if debug: print '12', out.size() 145 | 146 | # out = F.avg_pool3d(out, 4) 147 | # if debug: print '6', out.size() 148 | # out_1 = out.view(out.size(0), -1) 149 | # if debug: print '7', out_1.size() 150 | # out = self.linear(out_1) 151 | # if debug: print '8', out.size() 152 | return out#, out_1 153 | 154 | 155 | def DPN26(): 156 | cfg = { 157 | 'in_planes': (96,192,384,768), 158 | 'out_planes': (256,512,1024,2048), 159 | 'num_blocks': (2,2,2,2), 160 | 'dense_depth': (16,32,24,128) 161 | } 162 | return DPN(cfg) 163 | 164 | def DPN92_3D(): 165 | cfg = { 166 | 'in_planes': (64,64,96,128),#(96,192,384,768), 167 | 'out_planes': (96,96,128,128),#(256,512,1024,2048), 168 | 'num_blocks': (3,4,10,3), 169 | 'dense_depth': (16,16,32,32) 170 | } 171 | return DPN(cfg) 172 | 173 | def get_model(): 174 | net = DPN92_3D() 175 | loss = Loss(config['num_hard']) 176 | get_pbb = GetPBB(config) 177 | return config, net, loss, get_pbb 178 | 179 | 180 | def test(): 181 | debug = True 182 | net = DPN92_3D() 183 | x = Variable(torch.randn(1,1,96,96,96)) 184 | y = net(x) 185 | # print(y) 186 | 187 | # test() -------------------------------------------------------------------------------- /weakdetectorsamp/layers.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorsamp/layers.pyc -------------------------------------------------------------------------------- /weakdetectorsamp/res18.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from layers import * 4 | 5 | config = {} 6 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 7 | config['chanel'] = 1 8 | config['crop_size'] = [96, 96, 96] 9 | config['stride'] = 4 10 | config['max_stride'] = 16 11 | config['num_neg'] = 800 12 | config['th_neg'] = 0.02 13 | config['th_pos_train'] = 0.5 14 | config['th_pos_val'] = 1 15 | config['num_hard'] = 2 16 | config['bound_size'] = 12 17 | config['reso'] = 1 18 | config['sizelim'] = 2.5 #3 #6. #mm 19 | config['sizelim2'] = 10 #30 20 | config['sizelim3'] = 20 #40 21 | config['aug_scale'] = True 22 | config['r_rand_crop'] = 0.3 23 | config['pad_value'] = 170 24 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 25 | 26 | 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | 30 | #config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','d92998a73d4654a442e6d6ba15bbb827','990fbe3f0a1b53878669967b9afd1441','820245d8b211808bd18e78ff5be16fdb','adc3bbc63d40f8761c59be10f1e504c3', 31 | # '417','077','188','876','057','087','130','468'] 32 | 33 | class Net(nn.Module): 34 | def __init__(self): 35 | super(Net, self).__init__() 36 | # The first few layers consumes the most memory, so use simple convolution to save memory. 37 | # Call these layers preBlock, i.e., before the residual blocks of later layers. 38 | self.preBlock = nn.Sequential( 39 | nn.Conv3d(1, 24, kernel_size = 3, padding = 1), 40 | nn.BatchNorm3d(24), 41 | nn.ReLU(inplace = True), 42 | nn.Conv3d(24, 24, kernel_size = 3, padding = 1), 43 | nn.BatchNorm3d(24), 44 | nn.ReLU(inplace = True)) 45 | 46 | # 3 poolings, each pooling downsamples the feature map by a factor 2. 47 | # 3 groups of blocks. The first block of each group has one pooling. 48 | num_blocks_forw = [2,2,3,3] 49 | num_blocks_back = [3,3] 50 | self.featureNum_forw = [24,32,64,64,64] 51 | self.featureNum_back = [128,64,64] 52 | for i in range(len(num_blocks_forw)): 53 | blocks = [] 54 | for j in range(num_blocks_forw[i]): 55 | if j == 0: 56 | blocks.append(PostRes(self.featureNum_forw[i], self.featureNum_forw[i+1])) 57 | else: 58 | blocks.append(PostRes(self.featureNum_forw[i+1], self.featureNum_forw[i+1])) 59 | setattr(self, 'forw' + str(i + 1), nn.Sequential(*blocks)) 60 | 61 | 62 | for i in range(len(num_blocks_back)): 63 | blocks = [] 64 | for j in range(num_blocks_back[i]): 65 | if j == 0: 66 | if i==0: 67 | addition = 3 68 | else: 69 | addition = 0 70 | blocks.append(PostRes(self.featureNum_back[i+1]+self.featureNum_forw[i+2]+addition, self.featureNum_back[i])) 71 | else: 72 | blocks.append(PostRes(self.featureNum_back[i], self.featureNum_back[i])) 73 | setattr(self, 'back' + str(i + 2), nn.Sequential(*blocks)) 74 | 75 | self.maxpool1 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 76 | self.maxpool2 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 77 | self.maxpool3 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 78 | self.maxpool4 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 79 | self.unmaxpool1 = nn.MaxUnpool3d(kernel_size=2,stride=2) 80 | self.unmaxpool2 = nn.MaxUnpool3d(kernel_size=2,stride=2) 81 | 82 | self.path1 = nn.Sequential( 83 | nn.ConvTranspose3d(64, 64, kernel_size = 2, stride = 2), 84 | nn.BatchNorm3d(64), 85 | nn.ReLU(inplace = True)) 86 | self.path2 = nn.Sequential( 87 | nn.ConvTranspose3d(64, 64, kernel_size = 2, stride = 2), 88 | nn.BatchNorm3d(64), 89 | nn.ReLU(inplace = True)) 90 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 91 | self.output = nn.Sequential(nn.Conv3d(self.featureNum_back[0], 64, kernel_size = 1), 92 | nn.ReLU(), 93 | #nn.Dropout3d(p = 0.3), 94 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 95 | 96 | def forward(self, x, coord): 97 | out = self.preBlock(x)#16 98 | out_pool,indices0 = self.maxpool1(out) 99 | out1 = self.forw1(out_pool)#32 100 | out1_pool,indices1 = self.maxpool2(out1) 101 | out2 = self.forw2(out1_pool)#64 102 | #out2 = self.drop(out2) 103 | out2_pool,indices2 = self.maxpool3(out2) 104 | out3 = self.forw3(out2_pool)#96 105 | out3_pool,indices3 = self.maxpool4(out3) 106 | out4 = self.forw4(out3_pool)#96 107 | #out4 = self.drop(out4) 108 | 109 | rev3 = self.path1(out4) 110 | comb3 = self.back3(torch.cat((rev3, out3), 1))#96+96 111 | #comb3 = self.drop(comb3) 112 | rev2 = self.path2(comb3) 113 | 114 | comb2 = self.back2(torch.cat((rev2, out2,coord), 1))#64+64 115 | comb2 = self.drop(comb2) 116 | out = self.output(comb2) 117 | size = out.size() 118 | out = out.view(out.size(0), out.size(1), -1) 119 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 120 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 121 | #out = out.view(-1, 5) 122 | return out 123 | 124 | 125 | def get_model(): 126 | net = Net() 127 | loss = Loss(config['num_hard']) 128 | get_pbb = GetPBB(config) 129 | return config, net, loss, get_pbb 130 | -------------------------------------------------------------------------------- /weakdetectorsamp/res18.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorsamp/res18.pyc -------------------------------------------------------------------------------- /weakdetectorsamp/res_pool.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from layers import * 4 | 5 | config = {} 6 | config['anchors'] = [ 10.0, 25.0, 40.0] 7 | config['chanel'] = 2 8 | config['crop_size'] = [64, 128, 128] 9 | config['stride'] = [2,4,4] 10 | config['max_stride'] = 16 11 | config['num_neg'] = 10 12 | config['th_neg'] = 0.2 13 | config['th_pos'] = 0.5 14 | config['num_hard'] = 1 15 | config['bound_size'] = 12 16 | config['reso'] = [1.5,0.75,0.75] 17 | config['sizelim'] = 6. #mm 18 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','d92998a73d4654a442e6d6ba15bbb827','990fbe3f0a1b53878669967b9afd1441','820245d8b211808bd18e78ff5be16fdb', 19 | '417','077','188','876','057','087','130','468'] 20 | 21 | class Net(nn.Module): 22 | def __init__(self): 23 | super(Net, self).__init__() 24 | # The first few layers consumes the most memory, so use simple convolution to save memory. 25 | # Call these layers preBlock, i.e., before the residual blocks of later layers. 26 | self.preBlock = nn.Sequential( 27 | nn.Conv3d(2, 16, kernel_size = 3, padding = 1), 28 | nn.BatchNorm3d(16), 29 | nn.ReLU(inplace = True), 30 | nn.Conv3d(16, 16, kernel_size = 3, padding = 1), 31 | nn.BatchNorm3d(16), 32 | nn.ReLU(inplace = True)) 33 | 34 | # 3 poolings, each pooling downsamples the feature map by a factor 2. 35 | # 3 groups of blocks. The first block of each group has one pooling. 36 | num_blocks = [6,6,6,6] 37 | n_in = [16, 32, 64,96] 38 | n_out = [32, 64, 96,96] 39 | for i in range(len(num_blocks)): 40 | blocks = [] 41 | for j in range(num_blocks[i]): 42 | if j == 0: 43 | if i ==0: 44 | blocks.append(nn.MaxPool3d(kernel_size=[1,2,2])) 45 | blocks.append(PostRes(n_in[i], n_out[i])) 46 | else: 47 | blocks.append(nn.MaxPool3d(kernel_size=2)) 48 | blocks.append(PostRes(n_out[i], n_out[i])) 49 | else: 50 | blocks.append(PostRes(n_out[i], n_out[i])) 51 | setattr(self, 'group' + str(i + 1), nn.Sequential(*blocks)) 52 | 53 | self.path1 = nn.Sequential( 54 | nn.Conv3d(64, 32, kernel_size = 3, padding = 1), 55 | nn.BatchNorm3d(32), 56 | nn.ReLU(inplace = True)) 57 | 58 | self.path2 = nn.Sequential( 59 | nn.ConvTranspose3d(96, 32, kernel_size = 2, stride = 2), 60 | nn.BatchNorm3d(32), 61 | nn.ReLU(inplace = True)) 62 | 63 | self.path3 = nn.Sequential( 64 | nn.ConvTranspose3d(96, 32, kernel_size = 2, stride = 2), 65 | nn.BatchNorm3d(32), 66 | nn.ReLU(inplace = True), 67 | nn.ConvTranspose3d(32, 32, kernel_size = 2, stride = 2), 68 | nn.BatchNorm3d(32), 69 | nn.ReLU(inplace = True)) 70 | 71 | self.combine = nn.Sequential( 72 | nn.Conv3d(96, 128, kernel_size = 1), 73 | nn.BatchNorm3d(128), 74 | nn.ReLU(inplace = True)) 75 | 76 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 77 | self.output = nn.Conv3d(128, 5 * len(config['anchors']), kernel_size = 1) 78 | 79 | def forward(self, x): 80 | x = x.view(x.size(0), 2,x.size(2), x.size(3), x.size(4)) 81 | out = self.preBlock(x) 82 | 83 | out1 = self.group1(out) 84 | out2 = self.group2(out1) 85 | out3 = self.group3(out2) 86 | out4 = self.group4(out3) 87 | 88 | out2 = self.path1(out2) 89 | out3 = self.path2(out3) 90 | out4 = self.path3(out4) 91 | out = torch.cat((out2, out3, out4), 1) 92 | 93 | out = self.combine(out) 94 | out = self.drop(out) 95 | out = self.output(out) 96 | size = out.size() 97 | out = out.view(out.size(0), out.size(1), -1) 98 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 99 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 100 | #out = out.view(-1, 5) 101 | return out 102 | 103 | def get_model(): 104 | net = Net() 105 | loss = Loss(config['num_hard']) 106 | get_pbb = GetPBB(config) 107 | return config, net, loss, get_pbb 108 | -------------------------------------------------------------------------------- /weakdetectorsamp/revfname.py: -------------------------------------------------------------------------------- 1 | import csv 2 | import os 3 | fid = open('fnamedct.csv', 'w') 4 | writer = csv.writer(fid) 5 | writer.writerow(['fname', 'newname']) 6 | dct = {} 7 | curk = 0 8 | path = '../luna16/subset' 9 | for f in xrange(10): 10 | for fname in os.listdir(path+str(f)): 11 | if fname.endswith('raw'): 12 | dct[fname[:-4]] = '{:05d}'.format(curk) 13 | writer.writerow([fname[:-4], dct[fname[:-4]]]) 14 | curk += 1 15 | fid.close() 16 | print('number of files', curk) 17 | import pandas as pd 18 | # process seriesuid.csv 19 | name = '1.3.6.1.4.1.14519.5.2.1.6279.6001.100225287222365663678666836860' 20 | sdfrm = pd.read_csv('./evaluationScript/annotations/seriesuids.csv', \ 21 | names=[name]) 22 | fnamelst = sdfrm[name].tolist() 23 | print('number of series uid', len(fnamelst)) 24 | fid = open('./evaluationScript/annotations/newseriesuids.csv', 'w') 25 | writer = csv.writer(fid) 26 | for fname in fnamelst: 27 | writer.writerow([dct[fname]]) 28 | fid.close() 29 | # process annotations_excluded.csv 30 | name = ['seriesuid', 'coordX', 'coordY', 'coordZ', 'diameter_mm'] 31 | antexd = pd.read_csv('./evaluationScript/annotations/annotations_excluded.csv', names=name) 32 | srsidlst = antexd[name[0]].tolist()[1:] 33 | crdxlst = antexd[name[1]].tolist()[1:] 34 | crdylst = antexd[name[2]].tolist()[1:] 35 | crdzlst = antexd[name[3]].tolist()[1:] 36 | dimlst = antexd[name[4]].tolist()[1:] 37 | fid = open('./evaluationScript/annotations/newannotations_excluded.csv', 'w') 38 | writer = csv.writer(fid) 39 | writer.writerow(name) 40 | for i in xrange(len(srsidlst)): 41 | writer.writerow([dct[srsidlst[i]], crdxlst[i], crdylst[i], crdzlst[i], dimlst[i]]) 42 | fid.close() 43 | # process annotations.csv 44 | name = ['seriesuid', 'coordX', 'coordY', 'coordZ', 'diameter_mm'] 45 | ant = pd.read_csv('./evaluationScript/annotations/annotations.csv', names=name) 46 | srsidlst = ant[name[0]].tolist()[1:] 47 | crdxlst = ant[name[1]].tolist()[1:] 48 | crdylst = ant[name[2]].tolist()[1:] 49 | crdzlst = ant[name[3]].tolist()[1:] 50 | dimlst = ant[name[4]].tolist()[1:] 51 | fid = open('./evaluationScript/annotations/newannotations.csv', 'w') 52 | writer = csv.writer(fid) 53 | writer.writerow(name) 54 | for i in xrange(len(srsidlst)): 55 | writer.writerow([dct[srsidlst[i]], crdxlst[i], crdylst[i], crdzlst[i], dimlst[i]]) 56 | fid.close() 57 | # process luna16 annotations.csv 58 | name = ['seriesuid', 'coordX', 'coordY', 'coordZ', 'diameter_mm'] 59 | ant = pd.read_csv('../luna16/CSVFILES/annotations.csv', names=name) 60 | srsidlst = ant[name[0]].tolist()[1:] 61 | crdxlst = ant[name[1]].tolist()[1:] 62 | crdylst = ant[name[2]].tolist()[1:] 63 | crdzlst = ant[name[3]].tolist()[1:] 64 | dimlst = ant[name[4]].tolist()[1:] 65 | fid = open('../luna16/CSVFILES/newannotations.csv', 'w') 66 | writer = csv.writer(fid) 67 | writer.writerow(name) 68 | for i in xrange(len(srsidlst)): 69 | writer.writerow([dct[srsidlst[i]], crdxlst[i], crdylst[i], crdzlst[i], dimlst[i]]) 70 | fid.close() 71 | # revise file name 72 | # Do it in the prepare.py to save space. -------------------------------------------------------------------------------- /weakdetectorsamp/split_combine.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | class SplitComb(): 4 | def __init__(self,side_len,max_stride,stride,margin,pad_value): 5 | self.side_len = side_len 6 | self.max_stride = max_stride 7 | self.stride = stride 8 | self.margin = margin 9 | self.pad_value = pad_value 10 | 11 | def split(self, data, side_len = None, max_stride = None, margin = None): 12 | if side_len==None: 13 | side_len = self.side_len 14 | if max_stride == None: 15 | max_stride = self.max_stride 16 | if margin == None: 17 | margin = self.margin 18 | 19 | assert(side_len > margin) 20 | assert(side_len % max_stride == 0) 21 | assert(margin % max_stride == 0) 22 | 23 | splits = [] 24 | _, z, h, w = data.shape 25 | 26 | nz = int(np.ceil(float(z) / side_len)) 27 | nh = int(np.ceil(float(h) / side_len)) 28 | nw = int(np.ceil(float(w) / side_len)) 29 | 30 | nzhw = [nz,nh,nw] 31 | self.nzhw = nzhw 32 | 33 | pad = [ [0, 0], 34 | [margin, nz * side_len - z + margin], 35 | [margin, nh * side_len - h + margin], 36 | [margin, nw * side_len - w + margin]] 37 | data = np.pad(data, pad, 'edge') 38 | 39 | for iz in range(nz): 40 | for ih in range(nh): 41 | for iw in range(nw): 42 | sz = iz * side_len 43 | ez = (iz + 1) * side_len + 2 * margin 44 | sh = ih * side_len 45 | eh = (ih + 1) * side_len + 2 * margin 46 | sw = iw * side_len 47 | ew = (iw + 1) * side_len + 2 * margin 48 | 49 | split = data[np.newaxis, :, sz:ez, sh:eh, sw:ew] 50 | splits.append(split) 51 | 52 | splits = np.concatenate(splits, 0) 53 | return splits,nzhw 54 | 55 | def combine(self, output, nzhw = None, side_len=None, stride=None, margin=None): 56 | 57 | if side_len==None: 58 | side_len = self.side_len 59 | if stride == None: 60 | stride = self.stride 61 | if margin == None: 62 | margin = self.margin 63 | if len(nzhw) != 0: 64 | nz,nh,nw = nzhw 65 | else: 66 | nz = self.nz 67 | nh = self.nh 68 | nw = self.nw 69 | # if nzhw==None: 70 | # nz = self.nz 71 | # nh = self.nh 72 | # nw = self.nw 73 | # else: 74 | # nz,nh,nw = nzhw 75 | assert(side_len % stride == 0) 76 | assert(margin % stride == 0) 77 | side_len /= stride 78 | margin /= stride 79 | 80 | splits = [] 81 | for i in range(len(output)): 82 | splits.append(output[i]) 83 | 84 | output = -1000000 * np.ones(( 85 | nz * side_len, 86 | nh * side_len, 87 | nw * side_len, 88 | splits[0].shape[3], 89 | splits[0].shape[4]), np.float32) 90 | 91 | idx = 0 92 | for iz in range(nz): 93 | for ih in range(nh): 94 | for iw in range(nw): 95 | sz = iz * side_len 96 | ez = (iz + 1) * side_len 97 | sh = ih * side_len 98 | eh = (ih + 1) * side_len 99 | sw = iw * side_len 100 | ew = (iw + 1) * side_len 101 | 102 | split = splits[idx][margin:margin + side_len, margin:margin + side_len, margin:margin + side_len] 103 | output[sz:ez, sh:eh, sw:ew] = split 104 | idx += 1 105 | 106 | return output 107 | -------------------------------------------------------------------------------- /weakdetectorsamp/split_combine.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorsamp/split_combine.pyc -------------------------------------------------------------------------------- /weakdetectorsamp/utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorsamp/utils.pyc -------------------------------------------------------------------------------- /weakdetectorsamp/weakdata.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | from torch.utils.data import Dataset 4 | import os 5 | import time 6 | import collections 7 | import random 8 | from layers import iou 9 | from scipy.ndimage import zoom 10 | import warnings 11 | from scipy.ndimage.interpolation import rotate 12 | import pandas as pd 13 | class DataBowl3Detector(Dataset): 14 | def __init__(self, data_dir, split_path, labelfname, config, phase='train', split_comber=None): 15 | assert(phase == 'train')# or phase == 'val' or phase == 'test') 16 | self.phase = phase 17 | self.max_stride = config['max_stride'] 18 | self.stride = config['stride'] 19 | sizelim = config['sizelim']/config['reso'] 20 | sizelim2 = config['sizelim2']/config['reso'] 21 | sizelim3 = config['sizelim3']/config['reso'] 22 | self.blacklist = config['blacklist'] 23 | self.isScale = config['aug_scale'] 24 | self.r_rand = config['r_rand_crop'] 25 | self.augtype = config['augtype'] 26 | self.pad_value = config['pad_value'] 27 | self.split_comber = split_comber 28 | idcs = split_path # np.load(split_path) 29 | if phase!='test': 30 | idcs = [f for f in idcs if (f not in self.blacklist)] 31 | 32 | self.filenames = [] 33 | for idx in idcs: 34 | if 'tianchi' in data_dir: 35 | self.filenames.append(os.path.join(data_dir, '%s_clean.npy' % idx.split('/')[-1])) 36 | else: 37 | self.filenames.append(os.path.join(data_dir, '%s_clean.npy' % idx)) 38 | self.kagglenames = [f for f in self.filenames]# if len(f.split('/')[-1].split('_')[0])>20] 39 | 40 | pdfrm = pd.read_csv(labelfname, names=['fname', 'position', 'centerslice']) 41 | calibfnamelst = pdfrm['fname'].tolist()[1:] 42 | calibpositionlst = pdfrm['position'].tolist()[1:] 43 | calibcenterslicelst = pdfrm['centerslice'].tolist()[1:] 44 | calibdict = {} 45 | for idx, calibfname in enumerate(calibfnamelst): 46 | if calibfname not in calibdict: 47 | calibdict[calibfname] = [[calibpositionlst[idx], calibcenterslicelst[idx]]] 48 | else: 49 | calibdict[calibfname].append([calibpositionlst[idx], calibcenterslicelst[idx]]) 50 | labels = [] 51 | print len(idcs) 52 | for idx in idcs: 53 | labels.append(calibdict[idx]) 54 | 55 | self.sample_bboxes = labels 56 | if self.phase != 'test': 57 | self.bboxes = [] 58 | for i, l in enumerate(labels): 59 | # print l 60 | if len(l) > 0 : 61 | for t in l: 62 | if len(t) != 2: print(t,i) 63 | self.bboxes.append([np.concatenate([[i],np.array([t[0], t[1]])])]) 64 | 65 | self.bboxes = np.concatenate(self.bboxes,axis = 0) 66 | 67 | self.crop = Crop(config) 68 | self.label_mapping = LabelMapping(config, self.phase) 69 | 70 | def __getitem__(self, idx,split=None): 71 | t = time.time() 72 | np.random.seed(int(str(t%1)[2:7]))#seed according to time 73 | 74 | isRandomImg = False 75 | if self.phase !='test': 76 | if idx>=len(self.bboxes): 77 | isRandom = True 78 | idx = idx%len(self.bboxes) 79 | isRandomImg = np.random.randint(2) 80 | else: 81 | isRandom = False 82 | else: 83 | isRandom = False 84 | 85 | if self.phase != 'test': 86 | if not isRandomImg: 87 | bbox = self.bboxes[idx] 88 | filename = self.filenames[int(bbox[0])] 89 | imgs = np.load(filename) 90 | bboxes = self.sample_bboxes[int(bbox[0])] 91 | isScale = self.augtype['scale'] and (self.phase=='train') 92 | sample, target, bboxes, coord = self.crop(imgs, bbox[1:], bboxes,isScale,isRandom) 93 | if self.phase=='train' and not isRandom: 94 | sample, target, bboxes, coord = augment(sample, target, bboxes, coord, 95 | ifflip = self.augtype['flip'], ifrotate=self.augtype['rotate'], ifswap = self.augtype['swap']) 96 | else: 97 | randimid = np.random.randint(len(self.kagglenames)) 98 | filename = self.kagglenames[randimid] 99 | imgs = np.load(filename) 100 | bboxes = self.sample_bboxes[randimid] 101 | isScale = self.augtype['scale'] and (self.phase=='train') 102 | sample, target, bboxes, coord = self.crop(imgs, [], bboxes,isScale=False,isRand=True) 103 | # print sample.shape, target.shape, bboxes.shape 104 | label = self.label_mapping(sample.shape[1:], target, bboxes, filename) 105 | sample = (sample.astype(np.float32)-128)/128 106 | return torch.from_numpy(sample), torch.from_numpy(label), coord 107 | else: 108 | imgs = np.load(self.filenames[idx]) 109 | bboxes = self.sample_bboxes[idx] 110 | nz, nh, nw = imgs.shape[1:] 111 | pz = int(np.ceil(float(nz) / self.stride)) * self.stride 112 | ph = int(np.ceil(float(nh) / self.stride)) * self.stride 113 | pw = int(np.ceil(float(nw) / self.stride)) * self.stride 114 | imgs = np.pad(imgs, [[0,0],[0, pz - nz], [0, ph - nh], [0, pw - nw]], 'constant',constant_values = self.pad_value) 115 | 116 | xx,yy,zz = np.meshgrid(np.linspace(-0.5,0.5,imgs.shape[1]/self.stride), 117 | np.linspace(-0.5,0.5,imgs.shape[2]/self.stride), 118 | np.linspace(-0.5,0.5,imgs.shape[3]/self.stride),indexing ='ij') 119 | coord = np.concatenate([xx[np.newaxis,...], yy[np.newaxis,...],zz[np.newaxis,:]],0).astype('float32') 120 | imgs, nzhw = self.split_comber.split(imgs) 121 | coord2, nzhw2 = self.split_comber.split(coord, 122 | side_len = self.split_comber.side_len/self.stride, 123 | max_stride = self.split_comber.max_stride/self.stride, 124 | margin = self.split_comber.margin/self.stride) 125 | assert np.all(nzhw==nzhw2) 126 | imgs = (imgs.astype(np.float32)-128)/128 127 | return torch.from_numpy(imgs), bboxes, torch.from_numpy(coord2), np.array(nzhw) 128 | 129 | def __len__(self): 130 | if self.phase == 'train': 131 | return len(self.bboxes)/(1-self.r_rand) 132 | elif self.phase =='val': 133 | return len(self.bboxes) 134 | else: 135 | return len(self.sample_bboxes) 136 | 137 | 138 | def augment(sample, target, bboxes, coord, ifflip = True, ifrotate=True, ifswap = True): 139 | # angle1 = np.random.rand()*180 140 | if ifrotate: 141 | validrot = False 142 | counter = 0 143 | while not validrot: 144 | newtarget = np.copy(target) 145 | angle1 = np.random.rand()*180 146 | size = np.array(sample.shape[2:4]).astype('float') 147 | rotmat = np.array([[np.cos(angle1/180*np.pi),-np.sin(angle1/180*np.pi)],[np.sin(angle1/180*np.pi),np.cos(angle1/180*np.pi)]]) 148 | newtarget[1:3] = np.dot(rotmat,target[1:3]-size/2)+size/2 149 | if np.all(newtarget[:3]>target[3]) and np.all(newtarget[:3]< np.array(sample.shape[1:4])-newtarget[3]): 150 | validrot = True 151 | target = newtarget 152 | sample = rotate(sample,angle1,axes=(2,3),reshape=False) 153 | coord = rotate(coord,angle1,axes=(2,3),reshape=False) 154 | for box in bboxes: 155 | box[1:3] = np.dot(rotmat,box[1:3]-size/2)+size/2 156 | else: 157 | counter += 1 158 | if counter ==3: 159 | break 160 | if ifswap: 161 | if sample.shape[1]==sample.shape[2] and sample.shape[1]==sample.shape[3]: 162 | axisorder = np.random.permutation(3) 163 | sample = np.transpose(sample,np.concatenate([[0],axisorder+1])) 164 | coord = np.transpose(coord,np.concatenate([[0],axisorder+1])) 165 | target[:3] = target[:3][axisorder] 166 | bboxes[:,:3] = bboxes[:,:3][:,axisorder] 167 | 168 | if ifflip: 169 | # flipid = np.array([np.random.randint(2),np.random.randint(2),np.random.randint(2)])*2-1 170 | flipid = np.array([1,np.random.randint(2),np.random.randint(2)])*2-1 171 | sample = np.ascontiguousarray(sample[:,::flipid[0],::flipid[1],::flipid[2]]) 172 | coord = np.ascontiguousarray(coord[:,::flipid[0],::flipid[1],::flipid[2]]) 173 | for ax in range(3): 174 | if flipid[ax]==-1: 175 | target[ax] = np.array(sample.shape[ax+1])-target[ax] 176 | bboxes[:,ax]= np.array(sample.shape[ax+1])-bboxes[:,ax] 177 | return sample, target, bboxes, coord 178 | 179 | class Crop(object): 180 | def __init__(self, config): 181 | self.crop_size = config['crop_size'] 182 | self.bound_size = config['bound_size'] 183 | self.stride = config['stride'] 184 | self.pad_value = config['pad_value'] 185 | def __call__(self, imgs, target, bboxes,isScale=False,isRand=False): 186 | crop_size=self.crop_size 187 | bound_size = self.bound_size 188 | target = np.copy(target) 189 | bboxes = np.copy(bboxes) 190 | 191 | start = [] 192 | for i in range(3): 193 | if not isRand: 194 | r = target[3] / 2 195 | s = np.floor(target[i] - r)+ 1 - bound_size 196 | e = np.ceil(target[i] + r)+ 1 + bound_size - crop_size[i] 197 | else: 198 | s = np.max([imgs.shape[i+1]-crop_size[i]/2,imgs.shape[i+1]/2+bound_size]) 199 | e = np.min([crop_size[i]/2, imgs.shape[i+1]/2-bound_size]) 200 | target = np.array([np.nan,np.nan,np.nan,np.nan]) 201 | if s>e: 202 | start.append(np.random.randint(e,s))#! 203 | else: 204 | start.append(int(target[i])-crop_size[i]/2+np.random.randint(-bound_size/2,bound_size/2)) 205 | 206 | normstart = np.array(start).astype('float32')/np.array(imgs.shape[1:])-0.5 207 | normsize = np.array(crop_size).astype('float32')/np.array(imgs.shape[1:]) 208 | xx,yy,zz = np.meshgrid(np.linspace(normstart[0],normstart[0]+normsize[0],self.crop_size[0]/self.stride), 209 | np.linspace(normstart[1],normstart[1]+normsize[1],self.crop_size[1]/self.stride), 210 | np.linspace(normstart[2],normstart[2]+normsize[2],self.crop_size[2]/self.stride),indexing ='ij') 211 | coord = np.concatenate([xx[np.newaxis,...], yy[np.newaxis,...],zz[np.newaxis,:]],0).astype('float32') 212 | 213 | pad = [] 214 | pad.append([0,0]) 215 | for i in range(3): 216 | leftpad = max(0,-start[i]) 217 | rightpad = max(0,start[i]+crop_size[i]-imgs.shape[i+1]) 218 | pad.append([leftpad,rightpad]) 219 | crop = imgs[:, 220 | max(start[0],0):min(start[0] + crop_size[0],imgs.shape[1]), 221 | max(start[1],0):min(start[1] + crop_size[1],imgs.shape[2]), 222 | max(start[2],0):min(start[2] + crop_size[2],imgs.shape[3])] 223 | crop = np.pad(crop,pad,'constant',constant_values =self.pad_value) 224 | for i in range(3): 225 | target[i] = target[i] - start[i] 226 | for i in range(len(bboxes)): 227 | for j in range(3): 228 | bboxes[i][j] = bboxes[i][j] - start[j] 229 | 230 | if isScale: 231 | with warnings.catch_warnings(): 232 | warnings.simplefilter("ignore") 233 | crop = zoom(crop,[1,scale,scale,scale],order=1) 234 | newpad = self.crop_size[0]-crop.shape[1:][0] 235 | if newpad<0: 236 | crop = crop[:,:-newpad,:-newpad,:-newpad] 237 | elif newpad>0: 238 | pad2 = [[0,0],[0,newpad],[0,newpad],[0,newpad]] 239 | crop = np.pad(crop, pad2, 'constant', constant_values=self.pad_value) 240 | for i in range(4): 241 | target[i] = target[i]*scale 242 | for i in range(len(bboxes)): 243 | for j in range(4): 244 | bboxes[i][j] = bboxes[i][j]*scale 245 | # print(coord.shape) 246 | return crop, target, bboxes, coord 247 | 248 | class LabelMapping(object): 249 | def __init__(self, config, phase): 250 | self.stride = np.array(config['stride']) 251 | self.num_neg = int(config['num_neg']) 252 | self.th_neg = config['th_neg'] 253 | self.anchors = np.asarray(config['anchors']) 254 | self.phase = phase 255 | if phase == 'train': 256 | self.th_pos = config['th_pos_train'] 257 | elif phase == 'val': 258 | self.th_pos = config['th_pos_val'] 259 | 260 | 261 | def __call__(self, input_size, target, bboxes, filename): 262 | stride = self.stride 263 | num_neg = self.num_neg 264 | th_neg = self.th_neg 265 | anchors = self.anchors 266 | th_pos = self.th_pos 267 | 268 | output_size = [] 269 | for i in range(3): 270 | if input_size[i] % stride != 0: 271 | print filename 272 | # assert(input_size[i] % stride == 0) 273 | output_size.append(input_size[i] / stride) 274 | 275 | label = -1 * np.ones(output_size + [len(anchors), 5], np.float32) 276 | offset = ((stride.astype('float')) - 1) / 2 277 | oz = np.arange(offset, offset + stride * (output_size[0] - 1) + 1, stride) 278 | oh = np.arange(offset, offset + stride * (output_size[1] - 1) + 1, stride) 279 | ow = np.arange(offset, offset + stride * (output_size[2] - 1) + 1, stride) 280 | 281 | for bbox in bboxes: 282 | for i, anchor in enumerate(anchors): 283 | iz, ih, iw = select_samples(bbox, anchor, th_neg, oz, oh, ow) 284 | label[iz, ih, iw, i, 0] = 0 285 | 286 | if self.phase == 'train' and self.num_neg > 0: 287 | neg_z, neg_h, neg_w, neg_a = np.where(label[:, :, :, :, 0] == -1) 288 | neg_idcs = random.sample(range(len(neg_z)), min(num_neg, len(neg_z))) 289 | neg_z, neg_h, neg_w, neg_a = neg_z[neg_idcs], neg_h[neg_idcs], neg_w[neg_idcs], neg_a[neg_idcs] 290 | label[:, :, :, :, 0] = 0 291 | label[neg_z, neg_h, neg_w, neg_a, 0] = -1 292 | 293 | if np.isnan(target[0]): 294 | return label 295 | iz, ih, iw, ia = [], [], [], [] 296 | for i, anchor in enumerate(anchors): 297 | iiz, iih, iiw = select_samples(target, anchor, th_pos, oz, oh, ow) 298 | iz.append(iiz) 299 | ih.append(iih) 300 | iw.append(iiw) 301 | ia.append(i * np.ones((len(iiz),), np.int64)) 302 | iz = np.concatenate(iz, 0) 303 | ih = np.concatenate(ih, 0) 304 | iw = np.concatenate(iw, 0) 305 | ia = np.concatenate(ia, 0) 306 | flag = True 307 | if len(iz) == 0: 308 | pos = [] 309 | for i in range(3): 310 | pos.append(max(0, int(np.round((target[i] - offset) / stride)))) 311 | idx = np.argmin(np.abs(np.log(target[3] / anchors))) 312 | pos.append(idx) 313 | flag = False 314 | else: 315 | idx = random.sample(range(len(iz)), 1)[0] 316 | pos = [iz[idx], ih[idx], iw[idx], ia[idx]] 317 | dz = (target[0] - oz[pos[0]]) / anchors[pos[3]] 318 | dh = (target[1] - oh[pos[1]]) / anchors[pos[3]] 319 | dw = (target[2] - ow[pos[2]]) / anchors[pos[3]] 320 | dd = np.log(target[3] / anchors[pos[3]]) 321 | label[pos[0], pos[1], pos[2], pos[3], :] = [1, dz, dh, dw, dd] 322 | return label 323 | 324 | def select_samples(bbox, anchor, th, oz, oh, ow): 325 | z, h, w, d = bbox 326 | max_overlap = min(d, anchor) 327 | min_overlap = np.power(max(d, anchor), 3) * th / max_overlap / max_overlap 328 | if min_overlap > max_overlap: 329 | return np.zeros((0,), np.int64), np.zeros((0,), np.int64), np.zeros((0,), np.int64) 330 | else: 331 | s = z - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 332 | e = z + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 333 | mz = np.logical_and(oz >= s, oz <= e) 334 | iz = np.where(mz)[0] 335 | 336 | s = h - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 337 | e = h + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 338 | mh = np.logical_and(oh >= s, oh <= e) 339 | ih = np.where(mh)[0] 340 | 341 | s = w - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 342 | e = w + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 343 | mw = np.logical_and(ow >= s, ow <= e) 344 | iw = np.where(mw)[0] 345 | 346 | if len(iz) == 0 or len(ih) == 0 or len(iw) == 0: 347 | return np.zeros((0,), np.int64), np.zeros((0,), np.int64), np.zeros((0,), np.int64) 348 | 349 | lz, lh, lw = len(iz), len(ih), len(iw) 350 | iz = iz.reshape((-1, 1, 1)) 351 | ih = ih.reshape((1, -1, 1)) 352 | iw = iw.reshape((1, 1, -1)) 353 | iz = np.tile(iz, (1, lh, lw)).reshape((-1)) 354 | ih = np.tile(ih, (lz, 1, lw)).reshape((-1)) 355 | iw = np.tile(iw, (lz, lh, 1)).reshape((-1)) 356 | centers = np.concatenate([ 357 | oz[iz].reshape((-1, 1)), 358 | oh[ih].reshape((-1, 1)), 359 | ow[iw].reshape((-1, 1))], axis = 1) 360 | 361 | r0 = anchor / 2 362 | s0 = centers - r0 363 | e0 = centers + r0 364 | 365 | r1 = d / 2 366 | s1 = bbox[:3] - r1 367 | s1 = s1.reshape((1, -1)) 368 | e1 = bbox[:3] + r1 369 | e1 = e1.reshape((1, -1)) 370 | 371 | overlap = np.maximum(0, np.minimum(e0, e1) - np.maximum(s0, s1)) 372 | 373 | intersection = overlap[:, 0] * overlap[:, 1] * overlap[:, 2] 374 | union = anchor * anchor * anchor + d * d * d - intersection 375 | 376 | iou = intersection / union 377 | 378 | mask = iou >= th 379 | #if th > 0.4: 380 | # if np.sum(mask) == 0: 381 | # print(['iou not large', iou.max()]) 382 | # else: 383 | # print(['iou large', iou[mask]]) 384 | iz = iz[mask] 385 | ih = ih[mask] 386 | iw = iw[mask] 387 | return iz, ih, iw 388 | 389 | def collate(batch): 390 | if torch.is_tensor(batch[0]): 391 | return [b.unsqueeze(0) for b in batch] 392 | elif isinstance(batch[0], np.ndarray): 393 | return batch 394 | elif isinstance(batch[0], int): 395 | return torch.LongTensor(batch) 396 | elif isinstance(batch[0], collections.Iterable): 397 | transposed = zip(*batch) 398 | return [collate(samples) for samples in transposed] 399 | 400 | -------------------------------------------------------------------------------- /weakdetectorsamp/weakdatav2.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorsamp/weakdatav2.pyc -------------------------------------------------------------------------------- /weakdetectorv2/data.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | from torch.utils.data import Dataset 4 | import os 5 | import time 6 | import collections 7 | import random 8 | from layers import iou 9 | from scipy.ndimage import zoom 10 | import warnings 11 | from scipy.ndimage.interpolation import rotate 12 | class DataBowl3Detector(Dataset): 13 | def __init__(self, data_dir, split_path, config, phase='train', split_comber=None): 14 | assert(phase == 'train' or phase == 'val' or phase == 'test') 15 | self.phase = phase 16 | self.max_stride = config['max_stride'] 17 | self.stride = config['stride'] 18 | sizelim = config['sizelim']/config['reso'] 19 | sizelim2 = config['sizelim2']/config['reso'] 20 | sizelim3 = config['sizelim3']/config['reso'] 21 | self.blacklist = config['blacklist'] 22 | self.isScale = config['aug_scale'] 23 | self.r_rand = config['r_rand_crop'] 24 | self.augtype = config['augtype'] 25 | self.pad_value = config['pad_value'] 26 | self.split_comber = split_comber 27 | idcs = split_path # np.load(split_path) 28 | if phase!='test': 29 | idcs = [f for f in idcs if (f not in self.blacklist)] 30 | 31 | self.filenames = [] 32 | for idx in idcs: 33 | if 'tianchi' in data_dir: 34 | self.filenames.append(os.path.join(data_dir, '%s_clean.npy' % idx.split('/')[-1])) 35 | else: 36 | self.filenames.append(os.path.join(data_dir, '%s_clean.npy' % idx)) 37 | 38 | # self.filenames = [os.path.join(data_dir, '%s_clean.npy' % idx) for idx in idcs] 39 | # print self.filenames 40 | self.kagglenames = [f for f in self.filenames]# if len(f.split('/')[-1].split('_')[0])>20] 41 | # self.lunanames = [f for f in self.filenames if len(f.split('/')[-1].split('_')[0])<20] 42 | 43 | labels = [] 44 | 45 | print len(idcs) 46 | for idx in idcs: 47 | # print data_dir, idx 48 | if 'tianchi' in data_dir: 49 | l = np.load(data_dir+idx.split('/')[-1]+'_label.npy') 50 | else: 51 | l = np.load(data_dir+idx+'_label.npy') 52 | # print l, os.path.join(data_dir, '%s_label.npy' %idx) 53 | if np.all(l==0): 54 | l=np.array([]) 55 | labels.append(l) 56 | 57 | self.sample_bboxes = labels 58 | if self.phase != 'test': 59 | self.bboxes = [] 60 | for i, l in enumerate(labels): 61 | # print l 62 | if len(l) > 0 : 63 | for t in l: 64 | if t[3]>sizelim: 65 | self.bboxes.append([np.concatenate([[i],t])]) 66 | if t[3]>sizelim2: 67 | self.bboxes+=[[np.concatenate([[i],t])]]*2 68 | if t[3]>sizelim3: 69 | self.bboxes+=[[np.concatenate([[i],t])]]*4 70 | self.bboxes = np.concatenate(self.bboxes,axis = 0) 71 | 72 | self.crop = Crop(config) 73 | self.label_mapping = LabelMapping(config, self.phase) 74 | 75 | def __getitem__(self, idx,split=None): 76 | t = time.time() 77 | np.random.seed(int(str(t%1)[2:7]))#seed according to time 78 | 79 | isRandomImg = False 80 | if self.phase !='test': 81 | if idx>=len(self.bboxes): 82 | isRandom = True 83 | idx = idx%len(self.bboxes) 84 | isRandomImg = np.random.randint(2) 85 | else: 86 | isRandom = False 87 | else: 88 | isRandom = False 89 | 90 | if self.phase != 'test': 91 | if not isRandomImg: 92 | bbox = self.bboxes[idx] 93 | filename = self.filenames[int(bbox[0])] 94 | imgs = np.load(filename) 95 | bboxes = self.sample_bboxes[int(bbox[0])] 96 | isScale = self.augtype['scale'] and (self.phase=='train') 97 | sample, target, bboxes, coord = self.crop(imgs, bbox[1:], bboxes,isScale,isRandom) 98 | if self.phase=='train' and not isRandom: 99 | sample, target, bboxes, coord = augment(sample, target, bboxes, coord, 100 | ifflip = self.augtype['flip'], ifrotate=self.augtype['rotate'], ifswap = self.augtype['swap']) 101 | else: 102 | randimid = np.random.randint(len(self.kagglenames)) 103 | filename = self.kagglenames[randimid] 104 | imgs = np.load(filename) 105 | bboxes = self.sample_bboxes[randimid] 106 | isScale = self.augtype['scale'] and (self.phase=='train') 107 | sample, target, bboxes, coord = self.crop(imgs, [], bboxes,isScale=False,isRand=True) 108 | # print sample.shape, target.shape, bboxes.shape 109 | label = self.label_mapping(sample.shape[1:], target, bboxes, filename) 110 | sample = (sample.astype(np.float32)-128)/128 111 | #if filename in self.kagglenames and self.phase=='train': 112 | # label[label==-1]=0 113 | return torch.from_numpy(sample), torch.from_numpy(label), coord 114 | else: 115 | imgs = np.load(self.filenames[idx]) 116 | bboxes = self.sample_bboxes[idx] 117 | nz, nh, nw = imgs.shape[1:] 118 | pz = int(np.ceil(float(nz) / self.stride)) * self.stride 119 | ph = int(np.ceil(float(nh) / self.stride)) * self.stride 120 | pw = int(np.ceil(float(nw) / self.stride)) * self.stride 121 | imgs = np.pad(imgs, [[0,0],[0, pz - nz], [0, ph - nh], [0, pw - nw]], 'constant',constant_values = self.pad_value) 122 | 123 | xx,yy,zz = np.meshgrid(np.linspace(-0.5,0.5,imgs.shape[1]/self.stride), 124 | np.linspace(-0.5,0.5,imgs.shape[2]/self.stride), 125 | np.linspace(-0.5,0.5,imgs.shape[3]/self.stride),indexing ='ij') 126 | coord = np.concatenate([xx[np.newaxis,...], yy[np.newaxis,...],zz[np.newaxis,:]],0).astype('float32') 127 | imgs, nzhw = self.split_comber.split(imgs) 128 | coord2, nzhw2 = self.split_comber.split(coord, 129 | side_len = self.split_comber.side_len/self.stride, 130 | max_stride = self.split_comber.max_stride/self.stride, 131 | margin = self.split_comber.margin/self.stride) 132 | assert np.all(nzhw==nzhw2) 133 | imgs = (imgs.astype(np.float32)-128)/128 134 | return torch.from_numpy(imgs), bboxes, torch.from_numpy(coord2), np.array(nzhw) 135 | 136 | def __len__(self): 137 | if self.phase == 'train': 138 | return len(self.bboxes)/(1-self.r_rand) 139 | elif self.phase =='val': 140 | return len(self.bboxes) 141 | else: 142 | return len(self.sample_bboxes) 143 | 144 | 145 | def augment(sample, target, bboxes, coord, ifflip = True, ifrotate=True, ifswap = True): 146 | # angle1 = np.random.rand()*180 147 | if ifrotate: 148 | validrot = False 149 | counter = 0 150 | while not validrot: 151 | newtarget = np.copy(target) 152 | angle1 = np.random.rand()*180 153 | size = np.array(sample.shape[2:4]).astype('float') 154 | rotmat = np.array([[np.cos(angle1/180*np.pi),-np.sin(angle1/180*np.pi)],[np.sin(angle1/180*np.pi),np.cos(angle1/180*np.pi)]]) 155 | newtarget[1:3] = np.dot(rotmat,target[1:3]-size/2)+size/2 156 | if np.all(newtarget[:3]>target[3]) and np.all(newtarget[:3]< np.array(sample.shape[1:4])-newtarget[3]): 157 | validrot = True 158 | target = newtarget 159 | sample = rotate(sample,angle1,axes=(2,3),reshape=False) 160 | coord = rotate(coord,angle1,axes=(2,3),reshape=False) 161 | for box in bboxes: 162 | box[1:3] = np.dot(rotmat,box[1:3]-size/2)+size/2 163 | else: 164 | counter += 1 165 | if counter ==3: 166 | break 167 | if ifswap: 168 | if sample.shape[1]==sample.shape[2] and sample.shape[1]==sample.shape[3]: 169 | axisorder = np.random.permutation(3) 170 | sample = np.transpose(sample,np.concatenate([[0],axisorder+1])) 171 | coord = np.transpose(coord,np.concatenate([[0],axisorder+1])) 172 | target[:3] = target[:3][axisorder] 173 | bboxes[:,:3] = bboxes[:,:3][:,axisorder] 174 | 175 | if ifflip: 176 | # flipid = np.array([np.random.randint(2),np.random.randint(2),np.random.randint(2)])*2-1 177 | flipid = np.array([1,np.random.randint(2),np.random.randint(2)])*2-1 178 | sample = np.ascontiguousarray(sample[:,::flipid[0],::flipid[1],::flipid[2]]) 179 | coord = np.ascontiguousarray(coord[:,::flipid[0],::flipid[1],::flipid[2]]) 180 | for ax in range(3): 181 | if flipid[ax]==-1: 182 | target[ax] = np.array(sample.shape[ax+1])-target[ax] 183 | bboxes[:,ax]= np.array(sample.shape[ax+1])-bboxes[:,ax] 184 | return sample, target, bboxes, coord 185 | 186 | class Crop(object): 187 | def __init__(self, config): 188 | self.crop_size = config['crop_size'] 189 | self.bound_size = config['bound_size'] 190 | self.stride = config['stride'] 191 | self.pad_value = config['pad_value'] 192 | def __call__(self, imgs, target, bboxes,isScale=False,isRand=False): 193 | if isScale: 194 | radiusLim = [8.,120.] 195 | scaleLim = [0.75,1.25] 196 | scaleRange = [np.min([np.max([(radiusLim[0]/target[3]),scaleLim[0]]),1]) 197 | ,np.max([np.min([(radiusLim[1]/target[3]),scaleLim[1]]),1])] 198 | scale = np.random.rand()*(scaleRange[1]-scaleRange[0])+scaleRange[0] 199 | crop_size = (np.array(self.crop_size).astype('float')/scale).astype('int') 200 | else: 201 | crop_size=self.crop_size 202 | bound_size = self.bound_size 203 | target = np.copy(target) 204 | bboxes = np.copy(bboxes) 205 | 206 | start = [] 207 | for i in range(3): 208 | if not isRand: 209 | r = target[3] / 2 210 | s = np.floor(target[i] - r)+ 1 - bound_size 211 | e = np.ceil (target[i] + r)+ 1 + bound_size - crop_size[i] 212 | else: 213 | s = np.max([imgs.shape[i+1]-crop_size[i]/2,imgs.shape[i+1]/2+bound_size]) 214 | e = np.min([crop_size[i]/2, imgs.shape[i+1]/2-bound_size]) 215 | target = np.array([np.nan,np.nan,np.nan,np.nan]) 216 | if s>e: 217 | start.append(np.random.randint(e,s))#! 218 | else: 219 | start.append(int(target[i])-crop_size[i]/2+np.random.randint(-bound_size/2,bound_size/2)) 220 | 221 | 222 | normstart = np.array(start).astype('float32')/np.array(imgs.shape[1:])-0.5 223 | normsize = np.array(crop_size).astype('float32')/np.array(imgs.shape[1:]) 224 | xx,yy,zz = np.meshgrid(np.linspace(normstart[0],normstart[0]+normsize[0],self.crop_size[0]/self.stride), 225 | np.linspace(normstart[1],normstart[1]+normsize[1],self.crop_size[1]/self.stride), 226 | np.linspace(normstart[2],normstart[2]+normsize[2],self.crop_size[2]/self.stride),indexing ='ij') 227 | coord = np.concatenate([xx[np.newaxis,...], yy[np.newaxis,...],zz[np.newaxis,:]],0).astype('float32') 228 | 229 | pad = [] 230 | pad.append([0,0]) 231 | for i in range(3): 232 | leftpad = max(0,-start[i]) 233 | rightpad = max(0,start[i]+crop_size[i]-imgs.shape[i+1]) 234 | pad.append([leftpad,rightpad]) 235 | crop = imgs[:, 236 | max(start[0],0):min(start[0] + crop_size[0],imgs.shape[1]), 237 | max(start[1],0):min(start[1] + crop_size[1],imgs.shape[2]), 238 | max(start[2],0):min(start[2] + crop_size[2],imgs.shape[3])] 239 | crop = np.pad(crop,pad,'constant',constant_values =self.pad_value) 240 | for i in range(3): 241 | target[i] = target[i] - start[i] 242 | for i in range(len(bboxes)): 243 | for j in range(3): 244 | bboxes[i][j] = bboxes[i][j] - start[j] 245 | 246 | if isScale: 247 | with warnings.catch_warnings(): 248 | warnings.simplefilter("ignore") 249 | crop = zoom(crop,[1,scale,scale,scale],order=1) 250 | newpad = self.crop_size[0]-crop.shape[1:][0] 251 | if newpad<0: 252 | crop = crop[:,:-newpad,:-newpad,:-newpad] 253 | elif newpad>0: 254 | pad2 = [[0,0],[0,newpad],[0,newpad],[0,newpad]] 255 | crop = np.pad(crop, pad2, 'constant', constant_values=self.pad_value) 256 | for i in range(4): 257 | target[i] = target[i]*scale 258 | for i in range(len(bboxes)): 259 | for j in range(4): 260 | bboxes[i][j] = bboxes[i][j]*scale 261 | # print(coord.shape) 262 | return crop, target, bboxes, coord 263 | 264 | class LabelMapping(object): 265 | def __init__(self, config, phase): 266 | self.stride = np.array(config['stride']) 267 | self.num_neg = int(config['num_neg']) 268 | self.th_neg = config['th_neg'] 269 | self.anchors = np.asarray(config['anchors']) 270 | self.phase = phase 271 | if phase == 'train': 272 | self.th_pos = config['th_pos_train'] 273 | elif phase == 'val': 274 | self.th_pos = config['th_pos_val'] 275 | 276 | 277 | def __call__(self, input_size, target, bboxes, filename): 278 | stride = self.stride 279 | num_neg = self.num_neg 280 | th_neg = self.th_neg 281 | anchors = self.anchors 282 | th_pos = self.th_pos 283 | 284 | output_size = [] 285 | for i in range(3): 286 | if input_size[i] % stride != 0: 287 | print filename 288 | # assert(input_size[i] % stride == 0) 289 | output_size.append(input_size[i] / stride) 290 | 291 | label = -1 * np.ones(output_size + [len(anchors), 5], np.float32) 292 | offset = ((stride.astype('float')) - 1) / 2 293 | oz = np.arange(offset, offset + stride * (output_size[0] - 1) + 1, stride) 294 | oh = np.arange(offset, offset + stride * (output_size[1] - 1) + 1, stride) 295 | ow = np.arange(offset, offset + stride * (output_size[2] - 1) + 1, stride) 296 | 297 | for bbox in bboxes: 298 | for i, anchor in enumerate(anchors): 299 | iz, ih, iw = select_samples(bbox, anchor, th_neg, oz, oh, ow) 300 | label[iz, ih, iw, i, 0] = 0 301 | 302 | if self.phase == 'train' and self.num_neg > 0: 303 | neg_z, neg_h, neg_w, neg_a = np.where(label[:, :, :, :, 0] == -1) 304 | neg_idcs = random.sample(range(len(neg_z)), min(num_neg, len(neg_z))) 305 | neg_z, neg_h, neg_w, neg_a = neg_z[neg_idcs], neg_h[neg_idcs], neg_w[neg_idcs], neg_a[neg_idcs] 306 | label[:, :, :, :, 0] = 0 307 | label[neg_z, neg_h, neg_w, neg_a, 0] = -1 308 | 309 | if np.isnan(target[0]): 310 | return label 311 | iz, ih, iw, ia = [], [], [], [] 312 | for i, anchor in enumerate(anchors): 313 | iiz, iih, iiw = select_samples(target, anchor, th_pos, oz, oh, ow) 314 | iz.append(iiz) 315 | ih.append(iih) 316 | iw.append(iiw) 317 | ia.append(i * np.ones((len(iiz),), np.int64)) 318 | iz = np.concatenate(iz, 0) 319 | ih = np.concatenate(ih, 0) 320 | iw = np.concatenate(iw, 0) 321 | ia = np.concatenate(ia, 0) 322 | flag = True 323 | if len(iz) == 0: 324 | pos = [] 325 | for i in range(3): 326 | pos.append(max(0, int(np.round((target[i] - offset) / stride)))) 327 | idx = np.argmin(np.abs(np.log(target[3] / anchors))) 328 | pos.append(idx) 329 | flag = False 330 | else: 331 | idx = random.sample(range(len(iz)), 1)[0] 332 | pos = [iz[idx], ih[idx], iw[idx], ia[idx]] 333 | dz = (target[0] - oz[pos[0]]) / anchors[pos[3]] 334 | dh = (target[1] - oh[pos[1]]) / anchors[pos[3]] 335 | dw = (target[2] - ow[pos[2]]) / anchors[pos[3]] 336 | dd = np.log(target[3] / anchors[pos[3]]) 337 | label[pos[0], pos[1], pos[2], pos[3], :] = [1, dz, dh, dw, dd] 338 | return label 339 | 340 | def select_samples(bbox, anchor, th, oz, oh, ow): 341 | z, h, w, d = bbox 342 | max_overlap = min(d, anchor) 343 | min_overlap = np.power(max(d, anchor), 3) * th / max_overlap / max_overlap 344 | if min_overlap > max_overlap: 345 | return np.zeros((0,), np.int64), np.zeros((0,), np.int64), np.zeros((0,), np.int64) 346 | else: 347 | s = z - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 348 | e = z + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 349 | mz = np.logical_and(oz >= s, oz <= e) 350 | iz = np.where(mz)[0] 351 | 352 | s = h - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 353 | e = h + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 354 | mh = np.logical_and(oh >= s, oh <= e) 355 | ih = np.where(mh)[0] 356 | 357 | s = w - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 358 | e = w + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 359 | mw = np.logical_and(ow >= s, ow <= e) 360 | iw = np.where(mw)[0] 361 | 362 | if len(iz) == 0 or len(ih) == 0 or len(iw) == 0: 363 | return np.zeros((0,), np.int64), np.zeros((0,), np.int64), np.zeros((0,), np.int64) 364 | 365 | lz, lh, lw = len(iz), len(ih), len(iw) 366 | iz = iz.reshape((-1, 1, 1)) 367 | ih = ih.reshape((1, -1, 1)) 368 | iw = iw.reshape((1, 1, -1)) 369 | iz = np.tile(iz, (1, lh, lw)).reshape((-1)) 370 | ih = np.tile(ih, (lz, 1, lw)).reshape((-1)) 371 | iw = np.tile(iw, (lz, lh, 1)).reshape((-1)) 372 | centers = np.concatenate([ 373 | oz[iz].reshape((-1, 1)), 374 | oh[ih].reshape((-1, 1)), 375 | ow[iw].reshape((-1, 1))], axis = 1) 376 | 377 | r0 = anchor / 2 378 | s0 = centers - r0 379 | e0 = centers + r0 380 | 381 | r1 = d / 2 382 | s1 = bbox[:3] - r1 383 | s1 = s1.reshape((1, -1)) 384 | e1 = bbox[:3] + r1 385 | e1 = e1.reshape((1, -1)) 386 | 387 | overlap = np.maximum(0, np.minimum(e0, e1) - np.maximum(s0, s1)) 388 | 389 | intersection = overlap[:, 0] * overlap[:, 1] * overlap[:, 2] 390 | union = anchor * anchor * anchor + d * d * d - intersection 391 | 392 | iou = intersection / union 393 | 394 | mask = iou >= th 395 | #if th > 0.4: 396 | # if np.sum(mask) == 0: 397 | # print(['iou not large', iou.max()]) 398 | # else: 399 | # print(['iou large', iou[mask]]) 400 | iz = iz[mask] 401 | ih = ih[mask] 402 | iw = iw[mask] 403 | return iz, ih, iw 404 | 405 | def collate(batch): 406 | if torch.is_tensor(batch[0]): 407 | return [b.unsqueeze(0) for b in batch] 408 | elif isinstance(batch[0], np.ndarray): 409 | return batch 410 | elif isinstance(batch[0], int): 411 | return torch.LongTensor(batch) 412 | elif isinstance(batch[0], collections.Iterable): 413 | transposed = zip(*batch) 414 | return [collate(samples) for samples in transposed] 415 | 416 | -------------------------------------------------------------------------------- /weakdetectorv2/data.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorv2/data.pyc -------------------------------------------------------------------------------- /weakdetectorv2/detect.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from layers import nms, iou, acc 3 | import time 4 | import multiprocessing as mp 5 | 6 | save_dir = 'results/ma_offset40_res_n6_100-1/' 7 | pbb = np.load(save_dir + 'pbb.npy') 8 | lbb = np.load(save_dir + 'lbb.npy') 9 | 10 | conf_th = [-1, 0, 1] 11 | nms_th = [0.3, 0.5, 0.7] 12 | detect_th = [0.2, 0.3] 13 | def mp_get_pr(conf_th, nms_th, detect_th, num_procs = 64): 14 | start_time = time.time() 15 | 16 | num_samples = len(pbb) 17 | split_size = int(np.ceil(float(num_samples) / num_procs)) 18 | num_procs = int(np.ceil(float(num_samples) / split_size)) 19 | 20 | manager = mp.Manager() 21 | tp = manager.list(range(num_procs)) 22 | fp = manager.list(range(num_procs)) 23 | p = manager.list(range(num_procs)) 24 | procs = [] 25 | for pid in range(num_procs): 26 | proc = mp.Process( 27 | target = get_pr, 28 | args = ( 29 | pbb[pid * split_size:min((pid + 1) * split_size, num_samples)], 30 | lbb[pid * split_size:min((pid + 1) * split_size, num_samples)], 31 | conf_th, nms_th, detect_th, pid, tp, fp, p)) 32 | procs.append(proc) 33 | proc.start() 34 | 35 | for proc in procs: 36 | proc.join() 37 | 38 | tp = np.sum(tp) 39 | fp = np.sum(fp) 40 | p = np.sum(p) 41 | 42 | end_time = time.time() 43 | print('conf_th %1.1f, nms_th %1.1f, detect_th %1.1f, tp %d, fp %d, p %d, recall %f, time %3.2f' % (conf_th, nms_th, detect_th, tp, fp, p, float(tp) / p, end_time - start_time)) 44 | 45 | def get_pr(pbb, lbb, conf_th, nms_th, detect_th, pid, tp_list, fp_list, p_list): 46 | tp, fp, p = 0, 0, 0 47 | for i in range(len(pbb)): 48 | tpi, fpi, pi = acc(pbb[i], lbb[i], conf_th, nms_th, detect_th) 49 | tp += tpi 50 | fp += fpi 51 | p += pi 52 | tp_list[pid] = tp 53 | fp_list[pid] = fp 54 | p_list[pid] = p 55 | 56 | if __name__ == '__main__': 57 | for ct in conf_th: 58 | for nt in nms_th: 59 | for dt in detect_th: 60 | mp_get_pr(ct, nt, dt) 61 | -------------------------------------------------------------------------------- /weakdetectorv2/dpn3d.py: -------------------------------------------------------------------------------- 1 | '''Dual Path Networks in PyTorch.''' 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.autograd import Variable 6 | from layers import * 7 | config = {} 8 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 9 | config['chanel'] = 1 10 | config['crop_size'] = [96, 96, 96] 11 | config['stride'] = 4 12 | config['max_stride'] = 16 13 | config['num_neg'] = 800 14 | config['th_neg'] = 0.02 15 | config['th_pos_train'] = 0.5 16 | config['th_pos_val'] = 1 17 | config['num_hard'] = 2 18 | config['bound_size'] = 12 19 | config['reso'] = 1 20 | config['sizelim'] = 2.5 #3 #6. #mm 21 | config['sizelim2'] = 10 #30 22 | config['sizelim3'] = 20 #40 23 | config['aug_scale'] = True 24 | config['r_rand_crop'] = 0.3 25 | config['pad_value'] = 170 26 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | debug = False #True#False#True#True #True#False #True 30 | 31 | class Bottleneck(nn.Module): 32 | def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): 33 | super(Bottleneck, self).__init__() 34 | self.out_planes = out_planes 35 | self.dense_depth = dense_depth 36 | self.last_planes = last_planes 37 | self.in_planes = in_planes 38 | 39 | self.conv1 = nn.Conv3d(last_planes, in_planes, kernel_size=1, bias=False) 40 | self.bn1 = nn.BatchNorm3d(in_planes) 41 | self.conv2 = nn.Conv3d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=1, bias=False) 42 | self.bn2 = nn.BatchNorm3d(in_planes) 43 | self.conv3 = nn.Conv3d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) # 1 44 | self.bn3 = nn.BatchNorm3d(out_planes+dense_depth) 45 | 46 | self.shortcut = nn.Sequential() 47 | if first_layer: 48 | self.shortcut = nn.Sequential( 49 | nn.Conv3d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), 50 | nn.BatchNorm3d(out_planes+dense_depth) 51 | ) 52 | 53 | def forward(self, x): 54 | if debug: 55 | print 'bottleneck_0', x.size(), self.last_planes, self.in_planes, 1 56 | out = F.relu(self.bn1(self.conv1(x))) 57 | if debug: 58 | print 'bottleneck_1', out.size(), self.in_planes, self.in_planes, 3 59 | out = F.relu(self.bn2(self.conv2(out))) 60 | if debug: 61 | print 'bottleneck_2', out.size(), self.in_planes, self.out_planes+self.dense_depth, 1 62 | out = self.bn3(self.conv3(out)) 63 | if debug: 64 | print 'bottleneck_3', out.size() 65 | x = self.shortcut(x) 66 | d = self.out_planes 67 | if debug: 68 | print 'bottleneck_4', x.size(), self.last_planes, self.out_planes+self.dense_depth, d 69 | out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) 70 | if debug: 71 | print 'bottleneck_5', out.size() 72 | out = F.relu(out) 73 | return out 74 | 75 | class DPN(nn.Module): 76 | def __init__(self, cfg): 77 | super(DPN, self).__init__() 78 | in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] 79 | num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] 80 | # self.in_planes = in_planes 81 | # self.out_planes = out_planes 82 | # self.num_blocks = num_blocks 83 | # self.dense_depth = dense_depth 84 | self.conv1 = nn.Conv3d(1, 24, kernel_size=3, stride=1, padding=1, bias=False) 85 | self.bn1 = nn.BatchNorm3d(24) 86 | self.last_planes = 24 87 | self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=2)#stride=1) 88 | self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) 89 | self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) 90 | self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) 91 | self.last_planes = 136 92 | self.layer5 = self._make_layer(64, 64, num_blocks[2], dense_depth[2], stride=1) 93 | self.last_planes = 128+3 94 | self.layer6 = self._make_layer(128, 128, num_blocks[1], dense_depth[1], stride=1) 95 | self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 2)#10) 96 | self.last_planes = 72 97 | self.path1 = nn.Sequential( 98 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 99 | nn.BatchNorm3d(self.last_planes), 100 | nn.ReLU(inplace = True)) 101 | self.last_planes = 86 102 | self.path2 = nn.Sequential( 103 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 104 | nn.BatchNorm3d(self.last_planes), 105 | nn.ReLU(inplace = True)) 106 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 107 | self.output = nn.Sequential(nn.Conv3d(138, 64, kernel_size = 1), 108 | nn.ReLU(), 109 | #nn.Dropout3d(p = 0.3), 110 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 111 | def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): 112 | strides = [stride] + [1]*(num_blocks-1) 113 | layers = [] 114 | for i,stride in enumerate(strides): 115 | # if debug: print(i, self.last_planes, in_planes, out_planes, dense_depth) 116 | layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) 117 | self.last_planes = out_planes + (i+2) * dense_depth 118 | return nn.Sequential(*layers) 119 | 120 | def forward(self, x, coord): 121 | if debug: print '0', x.size(), 64#, coord.size 122 | out0 = F.relu(self.bn1(self.conv1(x))) 123 | if debug: print '1', out0.size() 124 | out1 = self.layer1(out0) 125 | if debug: print '2', out1.size() 126 | out2 = self.layer2(out1) 127 | if debug: print '3', out2.size() 128 | out3 = self.layer3(out2) 129 | if debug: print '4', out3.size() 130 | out4 = self.layer4(out3) 131 | if debug: print '5', out4.size() 132 | 133 | out5 = self.path1(out4) 134 | if debug: print '6', out5.size(), torch.cat((out3, out5), 1).size() 135 | out6 = self.layer5(torch.cat((out3, out5), 1)) 136 | if debug: print '7', out6.size() 137 | out7 = self.path2(out6) 138 | if debug: print '8', out7.size(), torch.cat((out2, out7), 1).size() #torch.cat((out2, out7, coord), 1).size() 139 | out8 = self.layer6(torch.cat((out2, out7, coord), 1)) 140 | if debug: print '9', out8.size() 141 | comb2 = self.drop(out8) 142 | out = self.output(comb2) 143 | if debug: print '10', out.size() 144 | size = out.size() 145 | out = out.view(out.size(0), out.size(1), -1) 146 | if debug: print '11', out.size() 147 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 148 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 149 | if debug: print '12', out.size() 150 | return out#, out_1 151 | 152 | def DPN92_3D(): 153 | cfg = { 154 | 'in_planes': (24,32,42,64),#(96,192,384,768), 155 | 'out_planes': (24,32,42,64),#(256,512,1024,2048), 156 | 'num_blocks': (3,4,10,3), 157 | 'dense_depth': (2,2,2,2) 158 | } 159 | return DPN(cfg) 160 | 161 | def get_model(): 162 | net = DPN92_3D() 163 | loss = Loss(config['num_hard']) 164 | get_pbb = GetPBB(config) 165 | return config, net, loss, get_pbb 166 | 167 | 168 | def test(): 169 | debug = True 170 | net = DPN92_3D() 171 | x = Variable(torch.randn(1,1,96,96,96)) 172 | crd = Variable(torch.randn(1,3,24,24,24)) 173 | y = net(x, crd) 174 | # print(y) 175 | 176 | # test() -------------------------------------------------------------------------------- /weakdetectorv2/dpn3d26.py: -------------------------------------------------------------------------------- 1 | '''Dual Path Networks in PyTorch.''' 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.autograd import Variable 6 | from layers import * 7 | config = {} 8 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 9 | config['chanel'] = 1 10 | config['crop_size'] = [96, 96, 96] 11 | config['stride'] = 4 12 | config['max_stride'] = 16 13 | config['num_neg'] = 800 14 | config['th_neg'] = 0.02 15 | config['th_pos_train'] = 0.5 16 | config['th_pos_val'] = 1 17 | config['num_hard'] = 2 18 | config['bound_size'] = 12 19 | config['reso'] = 1 20 | config['sizelim'] = 2.5 #3 #6. #mm 21 | config['sizelim2'] = 10 #30 22 | config['sizelim3'] = 20 #40 23 | config['aug_scale'] = True 24 | config['r_rand_crop'] = 0.3 25 | config['pad_value'] = 170 26 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | config['ep'] = 1 30 | config['save_dir'] = './' 31 | debug = False #True #True#False #True 32 | 33 | class Bottleneck(nn.Module): 34 | def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): 35 | super(Bottleneck, self).__init__() 36 | self.out_planes = out_planes 37 | self.dense_depth = dense_depth 38 | self.last_planes = last_planes 39 | self.in_planes = in_planes 40 | 41 | self.conv1 = nn.Conv3d(last_planes, in_planes, kernel_size=1, bias=False) 42 | self.bn1 = nn.BatchNorm3d(in_planes) 43 | self.conv2 = nn.Conv3d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=8, bias=False) 44 | self.bn2 = nn.BatchNorm3d(in_planes) 45 | self.conv3 = nn.Conv3d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) 46 | self.bn3 = nn.BatchNorm3d(out_planes+dense_depth) 47 | 48 | self.shortcut = nn.Sequential() 49 | if first_layer: 50 | self.shortcut = nn.Sequential( 51 | nn.Conv3d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), 52 | nn.BatchNorm3d(out_planes+dense_depth) 53 | ) 54 | 55 | def forward(self, x): 56 | if debug: 57 | print 'bottleneck_0', x.size(), self.last_planes, self.in_planes, 1 58 | out = F.relu(self.bn1(self.conv1(x))) 59 | if debug: 60 | print 'bottleneck_1', out.size(), self.in_planes, self.in_planes, 3 61 | out = F.relu(self.bn2(self.conv2(out))) 62 | if debug: 63 | print 'bottleneck_2', out.size(), self.in_planes, self.out_planes+self.dense_depth, 1 64 | out = self.bn3(self.conv3(out)) 65 | if debug: 66 | print 'bottleneck_3', out.size() 67 | x = self.shortcut(x) 68 | d = self.out_planes 69 | if debug: 70 | print 'bottleneck_4', x.size(), self.last_planes, self.out_planes+self.dense_depth, d 71 | out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) 72 | if debug: 73 | print 'bottleneck_5', out.size() 74 | out = F.relu(out) 75 | return out 76 | 77 | class DPN(nn.Module): 78 | def __init__(self, cfg): 79 | super(DPN, self).__init__() 80 | in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] 81 | num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] 82 | 83 | # self.in_planes = in_planes 84 | # self.out_planes = out_planes 85 | # self.num_blocks = num_blocks 86 | # self.dense_depth = dense_depth 87 | 88 | self.conv1 = nn.Conv3d(1, 24, kernel_size=3, stride=1, padding=1, bias=False) 89 | self.bn1 = nn.BatchNorm3d(24) 90 | self.last_planes = 24 91 | self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=2)#stride=1) 92 | self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) 93 | self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) 94 | self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) 95 | self.last_planes = 216 96 | self.layer5 = self._make_layer(128, 128, num_blocks[2], dense_depth[2], stride=1) 97 | self.last_planes = 224+3 98 | self.layer6 = self._make_layer(224, 224, num_blocks[1], dense_depth[1], stride=1) 99 | self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 2)#10) 100 | self.last_planes = 120 101 | self.path1 = nn.Sequential( 102 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 103 | nn.BatchNorm3d(self.last_planes), 104 | nn.ReLU(inplace = True)) 105 | self.last_planes = 152 106 | self.path2 = nn.Sequential( 107 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 108 | nn.BatchNorm3d(self.last_planes), 109 | nn.ReLU(inplace = True)) 110 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 111 | self.output = nn.Sequential(nn.Conv3d(248, 64, kernel_size = 1), 112 | nn.ReLU(), 113 | #nn.Dropout3d(p = 0.3), 114 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 115 | def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): 116 | strides = [stride] + [1]*(num_blocks-1) 117 | layers = [] 118 | for i,stride in enumerate(strides): 119 | if debug: print(i, self.last_planes, in_planes, out_planes, dense_depth) 120 | layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) 121 | self.last_planes = out_planes + (i+2) * dense_depth 122 | return nn.Sequential(*layers) 123 | 124 | def forward(self, x, coord): 125 | if debug: print '0', x.size(), 64#, coord.size 126 | out0 = F.relu(self.bn1(self.conv1(x))) 127 | if debug: print '1', out0.size() 128 | out1 = self.layer1(out0) 129 | if debug: print '2', out1.size() 130 | out2 = self.layer2(out1) 131 | if debug: print '3', out2.size() 132 | out3 = self.layer3(out2) 133 | if debug: print '4', out3.size() 134 | out4 = self.layer4(out3) 135 | if debug: print '5', out4.size() 136 | 137 | out5 = self.path1(out4) 138 | if debug: print '6', out5.size(), torch.cat((out3, out5), 1).size() 139 | out6 = self.layer5(torch.cat((out3, out5), 1)) 140 | if debug: print '7', out6.size() 141 | out7 = self.path2(out6) 142 | if debug: print '8', out7.size(), torch.cat((out2, out7), 1).size() #torch.cat((out2, out7, coord), 1).size() 143 | out8 = self.layer6(torch.cat((out2, out7, coord), 1)) 144 | if debug: print '9', out8.size() 145 | comb2 = self.drop(out8) 146 | out = self.output(comb2) 147 | if debug: print '10', out.size() 148 | size = out.size() 149 | out = out.view(out.size(0), out.size(1), -1) 150 | if debug: print '11', out.size() 151 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 152 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 153 | if debug: print '12', out.size() 154 | return out#, out_1 155 | 156 | def DPN92_3D(): 157 | cfg = { 158 | 'in_planes': (24,48,72,96),#(96,192,384,768), 159 | 'out_planes': (24,48,72,96),#(256,512,1024,2048), 160 | 'num_blocks': (2,2,2,2), 161 | 'dense_depth': (8,8,8,8) 162 | } 163 | return DPN(cfg) 164 | 165 | def get_model(): 166 | net = DPN92_3D() 167 | loss = Loss(config['num_hard']) 168 | get_pbb = GetPBB(config) 169 | return config, net, loss, get_pbb 170 | 171 | 172 | def test(): 173 | debug = True 174 | net = DPN92_3D() 175 | x = Variable(torch.randn(1,1,96,96,96)) 176 | crd = Variable(torch.randn(1,3,24,24,24)) 177 | y = net(x, crd) 178 | # print(y) 179 | 180 | # test() -------------------------------------------------------------------------------- /weakdetectorv2/dpn3dold.py: -------------------------------------------------------------------------------- 1 | '''Dual Path Networks in PyTorch.''' 2 | import torch 3 | import torch.nn as nn 4 | import torch.nn.functional as F 5 | from torch.autograd import Variable 6 | from layers import * 7 | config = {} 8 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 9 | config['chanel'] = 1 10 | config['crop_size'] = [96, 96, 96] 11 | config['stride'] = 4 12 | config['max_stride'] = 16 13 | config['num_neg'] = 800 14 | config['th_neg'] = 0.02 15 | config['th_pos_train'] = 0.5 16 | config['th_pos_val'] = 1 17 | config['num_hard'] = 2 18 | config['bound_size'] = 12 19 | config['reso'] = 1 20 | config['sizelim'] = 2.5 #3 #6. #mm 21 | config['sizelim2'] = 10 #30 22 | config['sizelim3'] = 20 #40 23 | config['aug_scale'] = True 24 | config['r_rand_crop'] = 0.3 25 | config['pad_value'] = 170 26 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | debug = False #True#False #True 30 | 31 | class Bottleneck(nn.Module): 32 | def __init__(self, last_planes, in_planes, out_planes, dense_depth, stride, first_layer): 33 | super(Bottleneck, self).__init__() 34 | self.out_planes = out_planes 35 | self.dense_depth = dense_depth 36 | self.last_planes = last_planes 37 | self.in_planes = in_planes 38 | 39 | self.conv1 = nn.Conv3d(last_planes, in_planes, kernel_size=1, bias=False) 40 | self.bn1 = nn.BatchNorm3d(in_planes) 41 | self.conv2 = nn.Conv3d(in_planes, in_planes, kernel_size=3, stride=stride, padding=1, groups=32, bias=False) 42 | self.bn2 = nn.BatchNorm3d(in_planes) 43 | self.conv3 = nn.Conv3d(in_planes, out_planes+dense_depth, kernel_size=1, bias=False) 44 | self.bn3 = nn.BatchNorm3d(out_planes+dense_depth) 45 | 46 | self.shortcut = nn.Sequential() 47 | if first_layer: 48 | self.shortcut = nn.Sequential( 49 | nn.Conv3d(last_planes, out_planes+dense_depth, kernel_size=1, stride=stride, bias=False), 50 | nn.BatchNorm3d(out_planes+dense_depth) 51 | ) 52 | 53 | def forward(self, x): 54 | # print 'bottleneck_0', x.size(), self.last_planes, self.in_planes, 1 55 | out = F.relu(self.bn1(self.conv1(x))) 56 | # print 'bottleneck_1', out.size(), self.in_planes, self.in_planes, 3 57 | out = F.relu(self.bn2(self.conv2(out))) 58 | # print 'bottleneck_2', out.size(), self.in_planes, self.out_planes+self.dense_depth, 1 59 | out = self.bn3(self.conv3(out)) 60 | # print 'bottleneck_3', out.size() 61 | x = self.shortcut(x) 62 | d = self.out_planes 63 | # print 'bottleneck_4', x.size(), self.last_planes, self.out_planes+self.dense_depth, d 64 | out = torch.cat([x[:,:d,:,:]+out[:,:d,:,:], x[:,d:,:,:], out[:,d:,:,:]], 1) 65 | # print 'bottleneck_5', out.size() 66 | out = F.relu(out) 67 | return out 68 | 69 | class DPN(nn.Module): 70 | def __init__(self, cfg): 71 | super(DPN, self).__init__() 72 | in_planes, out_planes = cfg['in_planes'], cfg['out_planes'] 73 | num_blocks, dense_depth = cfg['num_blocks'], cfg['dense_depth'] 74 | 75 | # self.in_planes = in_planes 76 | # self.out_planes = out_planes 77 | # self.num_blocks = num_blocks 78 | # self.dense_depth = dense_depth 79 | 80 | self.conv1 = nn.Conv3d(1, 64, kernel_size=3, stride=1, padding=1, bias=False) 81 | self.bn1 = nn.BatchNorm3d(64) 82 | self.last_planes = 64 83 | self.layer1 = self._make_layer(in_planes[0], out_planes[0], num_blocks[0], dense_depth[0], stride=2)#stride=1) 84 | self.layer2 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=2) 85 | self.layer3 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=2) 86 | self.layer4 = self._make_layer(in_planes[3], out_planes[3], num_blocks[3], dense_depth[3], stride=2) 87 | self.last_planes = 736 88 | self.layer5 = self._make_layer(in_planes[2], out_planes[2], num_blocks[2], dense_depth[2], stride=1) 89 | self.last_planes = 656+3 90 | self.layer6 = self._make_layer(in_planes[1], out_planes[1], num_blocks[1], dense_depth[1], stride=1) 91 | self.linear = nn.Linear(out_planes[3]+(num_blocks[3]+1)*dense_depth[3], 2)#10) 92 | self.last_planes = 256 93 | self.path1 = nn.Sequential( 94 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 95 | nn.BatchNorm3d(self.last_planes), 96 | nn.ReLU(inplace = True)) 97 | self.last_planes = 480 98 | self.path2 = nn.Sequential( 99 | nn.ConvTranspose3d(self.last_planes, self.last_planes, kernel_size = 2, stride = 2), 100 | nn.BatchNorm3d(self.last_planes), 101 | nn.ReLU(inplace = True)) 102 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 103 | self.output = nn.Sequential(nn.Conv3d(176, 64, kernel_size = 1), 104 | nn.ReLU(), 105 | #nn.Dropout3d(p = 0.3), 106 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 107 | def _make_layer(self, in_planes, out_planes, num_blocks, dense_depth, stride): 108 | strides = [stride] + [1]*(num_blocks-1) 109 | layers = [] 110 | for i,stride in enumerate(strides): 111 | layers.append(Bottleneck(self.last_planes, in_planes, out_planes, dense_depth, stride, i==0)) 112 | self.last_planes = out_planes + (i+2) * dense_depth 113 | return nn.Sequential(*layers) 114 | 115 | def forward(self, x, coord): 116 | if debug: print '0', x.size(), 64#, coord.size 117 | out0 = F.relu(self.bn1(self.conv1(x))) 118 | if debug: print '1', out0.size() 119 | out1 = self.layer1(out0) 120 | if debug: print '2', out1.size() 121 | out2 = self.layer2(out1) 122 | if debug: print '3', out2.size() 123 | out3 = self.layer3(out2) 124 | if debug: print '4', out3.size() 125 | out4 = self.layer4(out3) 126 | if debug: print '5', out4.size() 127 | 128 | out5 = self.path1(out4) 129 | if debug: print '6', out5.size(), torch.cat((out3, out5), 1).size() 130 | out6 = self.layer5(torch.cat((out3, out5), 1)) 131 | if debug: print '7', out6.size() 132 | out7 = self.path2(out6) 133 | if debug: print '8', out7.size(), torch.cat((out2, out7), 1).size() #torch.cat((out2, out7, coord), 1).size() 134 | out8 = self.layer6(torch.cat((out2, out7, coord), 1)) 135 | if debug: print '9', out8.size() 136 | comb2 = self.drop(out8) 137 | out = self.output(comb2) 138 | if debug: print '10', out.size() 139 | size = out.size() 140 | out = out.view(out.size(0), out.size(1), -1) 141 | if debug: print '11', out.size() 142 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 143 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 144 | if debug: print '12', out.size() 145 | 146 | # out = F.avg_pool3d(out, 4) 147 | # if debug: print '6', out.size() 148 | # out_1 = out.view(out.size(0), -1) 149 | # if debug: print '7', out_1.size() 150 | # out = self.linear(out_1) 151 | # if debug: print '8', out.size() 152 | return out#, out_1 153 | 154 | 155 | def DPN26(): 156 | cfg = { 157 | 'in_planes': (96,192,384,768), 158 | 'out_planes': (256,512,1024,2048), 159 | 'num_blocks': (2,2,2,2), 160 | 'dense_depth': (16,32,24,128) 161 | } 162 | return DPN(cfg) 163 | 164 | def DPN92_3D(): 165 | cfg = { 166 | 'in_planes': (64,64,96,128),#(96,192,384,768), 167 | 'out_planes': (96,96,128,128),#(256,512,1024,2048), 168 | 'num_blocks': (3,4,10,3), 169 | 'dense_depth': (16,16,32,32) 170 | } 171 | return DPN(cfg) 172 | 173 | def get_model(): 174 | net = DPN92_3D() 175 | loss = Loss(config['num_hard']) 176 | get_pbb = GetPBB(config) 177 | return config, net, loss, get_pbb 178 | 179 | 180 | def test(): 181 | debug = True 182 | net = DPN92_3D() 183 | x = Variable(torch.randn(1,1,96,96,96)) 184 | y = net(x) 185 | # print(y) 186 | 187 | # test() -------------------------------------------------------------------------------- /weakdetectorv2/layers.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import torch 4 | from torch import nn 5 | import math 6 | from torch.autograd import Variable 7 | def nms(output, nms_th): 8 | if len(output) == 0: 9 | return output 10 | output = output[np.argsort(-output[:, 0])] 11 | bboxes = [output[0]] 12 | for i in np.arange(1, len(output)): 13 | bbox = output[i] 14 | flag = 1 15 | for j in range(len(bboxes)): 16 | if iou(bbox[1:5], bboxes[j][1:5]) >= nms_th: 17 | flag = -1 18 | break 19 | if flag == 1: 20 | bboxes.append(bbox) 21 | bboxes = np.asarray(bboxes, np.float32) 22 | return bboxes 23 | class PostRes2d(nn.Module): 24 | def __init__(self, n_in, n_out, stride = 1): 25 | super(PostRes2d, self).__init__() 26 | self.conv1 = nn.Conv2d(n_in, n_out, kernel_size = 3, stride = stride, padding = 1) 27 | self.bn1 = nn.BatchNorm2d(n_out) 28 | self.relu = nn.ReLU(inplace = True) 29 | self.conv2 = nn.Conv2d(n_out, n_out, kernel_size = 3, padding = 1) 30 | self.bn2 = nn.BatchNorm2d(n_out) 31 | 32 | if stride != 1 or n_out != n_in: 33 | self.shortcut = nn.Sequential( 34 | nn.Conv2d(n_in, n_out, kernel_size = 1, stride = stride), 35 | nn.BatchNorm2d(n_out)) 36 | else: 37 | self.shortcut = None 38 | 39 | def forward(self, x): 40 | residual = x 41 | if self.shortcut is not None: 42 | residual = self.shortcut(x) 43 | out = self.conv1(x) 44 | out = self.bn1(out) 45 | out = self.relu(out) 46 | out = self.conv2(out) 47 | out = self.bn2(out) 48 | 49 | out += residual 50 | out = self.relu(out) 51 | return out 52 | 53 | class PostRes(nn.Module): 54 | def __init__(self, n_in, n_out, stride = 1): 55 | super(PostRes, self).__init__() 56 | self.conv1 = nn.Conv3d(n_in, n_out, kernel_size = 3, stride = stride, padding = 1) 57 | self.bn1 = nn.BatchNorm3d(n_out) 58 | self.relu = nn.ReLU(inplace = True) 59 | self.conv2 = nn.Conv3d(n_out, n_out, kernel_size = 3, padding = 1) 60 | self.bn2 = nn.BatchNorm3d(n_out) 61 | 62 | if stride != 1 or n_out != n_in: 63 | self.shortcut = nn.Sequential( 64 | nn.Conv3d(n_in, n_out, kernel_size = 1, stride = stride), 65 | nn.BatchNorm3d(n_out)) 66 | else: 67 | self.shortcut = None 68 | 69 | def forward(self, x): 70 | residual = x 71 | if self.shortcut is not None: 72 | residual = self.shortcut(x) 73 | out = self.conv1(x) 74 | out = self.bn1(out) 75 | out = self.relu(out) 76 | out = self.conv2(out) 77 | out = self.bn2(out) 78 | 79 | out += residual 80 | out = self.relu(out) 81 | return out 82 | 83 | class Rec3(nn.Module): 84 | def __init__(self, n0, n1, n2, n3, p = 0.0, integrate = True): 85 | super(Rec3, self).__init__() 86 | 87 | self.block01 = nn.Sequential( 88 | nn.Conv3d(n0, n1, kernel_size = 3, stride = 2, padding = 1), 89 | nn.BatchNorm3d(n1), 90 | nn.ReLU(inplace = True), 91 | nn.Conv3d(n1, n1, kernel_size = 3, padding = 1), 92 | nn.BatchNorm3d(n1)) 93 | 94 | self.block11 = nn.Sequential( 95 | nn.Conv3d(n1, n1, kernel_size = 3, padding = 1), 96 | nn.BatchNorm3d(n1), 97 | nn.ReLU(inplace = True), 98 | nn.Conv3d(n1, n1, kernel_size = 3, padding = 1), 99 | nn.BatchNorm3d(n1)) 100 | 101 | self.block21 = nn.Sequential( 102 | nn.ConvTranspose3d(n2, n1, kernel_size = 2, stride = 2), 103 | nn.BatchNorm3d(n1), 104 | nn.ReLU(inplace = True), 105 | nn.Conv3d(n1, n1, kernel_size = 3, padding = 1), 106 | nn.BatchNorm3d(n1)) 107 | 108 | self.block12 = nn.Sequential( 109 | nn.Conv3d(n1, n2, kernel_size = 3, stride = 2, padding = 1), 110 | nn.BatchNorm3d(n2), 111 | nn.ReLU(inplace = True), 112 | nn.Conv3d(n2, n2, kernel_size = 3, padding = 1), 113 | nn.BatchNorm3d(n2)) 114 | 115 | self.block22 = nn.Sequential( 116 | nn.Conv3d(n2, n2, kernel_size = 3, padding = 1), 117 | nn.BatchNorm3d(n2), 118 | nn.ReLU(inplace = True), 119 | nn.Conv3d(n2, n2, kernel_size = 3, padding = 1), 120 | nn.BatchNorm3d(n2)) 121 | 122 | self.block32 = nn.Sequential( 123 | nn.ConvTranspose3d(n3, n2, kernel_size = 2, stride = 2), 124 | nn.BatchNorm3d(n2), 125 | nn.ReLU(inplace = True), 126 | nn.Conv3d(n2, n2, kernel_size = 3, padding = 1), 127 | nn.BatchNorm3d(n2)) 128 | 129 | self.block23 = nn.Sequential( 130 | nn.Conv3d(n2, n3, kernel_size = 3, stride = 2, padding = 1), 131 | nn.BatchNorm3d(n3), 132 | nn.ReLU(inplace = True), 133 | nn.Conv3d(n3, n3, kernel_size = 3, padding = 1), 134 | nn.BatchNorm3d(n3)) 135 | 136 | self.block33 = nn.Sequential( 137 | nn.Conv3d(n3, n3, kernel_size = 3, padding = 1), 138 | nn.BatchNorm3d(n3), 139 | nn.ReLU(inplace = True), 140 | nn.Conv3d(n3, n3, kernel_size = 3, padding = 1), 141 | nn.BatchNorm3d(n3)) 142 | 143 | self.relu = nn.ReLU(inplace = True) 144 | self.p = p 145 | self.integrate = integrate 146 | 147 | def forward(self, x0, x1, x2, x3): 148 | if self.p > 0 and self.training: 149 | coef = torch.bernoulli((1.0 - self.p) * torch.ones(8)) 150 | out1 = coef[0] * self.block01(x0) + coef[1] * self.block11(x1) + coef[2] * self.block21(x2) 151 | out2 = coef[3] * self.block12(x1) + coef[4] * self.block22(x2) + coef[5] * self.block32(x3) 152 | out3 = coef[6] * self.block23(x2) + coef[7] * self.block33(x3) 153 | else: 154 | out1 = (1 - self.p) * (self.block01(x0) + self.block11(x1) + self.block21(x2)) 155 | out2 = (1 - self.p) * (self.block12(x1) + self.block22(x2) + self.block32(x3)) 156 | out3 = (1 - self.p) * (self.block23(x2) + self.block33(x3)) 157 | 158 | if self.integrate: 159 | out1 += x1 160 | out2 += x2 161 | out3 += x3 162 | 163 | return x0, self.relu(out1), self.relu(out2), self.relu(out3) 164 | 165 | def hard_mining(neg_output, neg_labels, num_hard): 166 | _, idcs = torch.topk(neg_output, min(num_hard, len(neg_output))) 167 | neg_output = torch.index_select(neg_output, 0, idcs) 168 | neg_labels = torch.index_select(neg_labels, 0, idcs) 169 | return neg_output, neg_labels 170 | 171 | class Fprclsloss(nn.Module): 172 | def __init__(self, num_hard=2): 173 | super(Fprclsloss, self).__init__() 174 | self.sigmoid = nn.Sigmoid() 175 | self.classify_loss = nn.BCELoss() 176 | self.num_hard = num_hard 177 | 178 | def forward(self, output, labels, train=True): 179 | batch_size = labels.size(0) 180 | pos_idcs = labels[:, 0] > 0.5 181 | pos_idcs = pos_idcs.unsqueeze(1).expand(pos_idcs.size(0), 5) 182 | pos_output = output[pos_idcs].view(-1, 1) 183 | pos_labels = labels[pos_idcs].view(-1, 1) 184 | neg_idcs = labels[:, 0] < 0.5 185 | neg_output = output[:, 0][neg_idcs] 186 | neg_labels = labels[:, 0][neg_idcs] 187 | 188 | if self.num_hard > 0 and train: 189 | neg_output, neg_labels = hard_mining(neg_output, neg_labels, self.num_hard * batch_size) 190 | neg_prob = self.sigmoid(neg_output) 191 | if len(pos_output)>0: 192 | pos_prob = self.sigmoid(pos_output[:, 0]) 193 | classify_loss = 0.5 * self.classify_loss(pos_prob, pos_labels[:, 0]) + 0.5 * self.classify_loss(neg_prob, neg_labels + 1) 194 | pos_correct = (pos_prob.data >= 0.5).sum() 195 | pos_total = len(pos_prob) 196 | else: 197 | classify_loss = 0.5 * self.classify_loss(neg_prob, neg_labels + 1) 198 | pos_correct = 0 199 | pos_total = 0 200 | classify_loss_data = classify_loss.data[0] 201 | loss = classify_loss 202 | neg_correct = (neg_prob.data < 0.5).sum() 203 | neg_total = len(neg_prob) 204 | return [loss] + [pos_correct, pos_total, neg_correct, neg_total] 205 | 206 | class Loss(nn.Module): 207 | def __init__(self, num_hard = 0): 208 | super(Loss, self).__init__() 209 | self.sigmoid = nn.Sigmoid() 210 | self.classify_loss = nn.BCELoss() 211 | self.regress_loss = nn.SmoothL1Loss() 212 | self.num_hard = num_hard 213 | 214 | def forward(self, output, labels, train = True, prob=None, isp=False): 215 | batch_size = labels.size(0) 216 | output = output.view(-1, 5) 217 | labels = labels.view(-1, 5) 218 | 219 | pos_idcs = labels[:, 0] > 0.5 220 | pos_idcs = pos_idcs.unsqueeze(1).expand(pos_idcs.size(0), 5) 221 | pos_output = output[pos_idcs].view(-1, 5) 222 | pos_labels = labels[pos_idcs].view(-1, 5) 223 | 224 | neg_idcs = labels[:, 0] < -0.5 225 | neg_output = output[:, 0][neg_idcs] 226 | neg_labels = labels[:, 0][neg_idcs] 227 | 228 | if self.num_hard > 0 and train: 229 | neg_output, neg_labels = hard_mining(neg_output, neg_labels, self.num_hard * batch_size) 230 | 231 | neg_prob = self.sigmoid(neg_output) 232 | 233 | #classify_loss = self.classify_loss( 234 | # torch.cat((pos_prob, neg_prob), 0), 235 | # torch.cat((pos_labels[:, 0], neg_labels + 1), 0)) 236 | if len(pos_output)>0: 237 | pos_prob = self.sigmoid(pos_output[:, 0]) 238 | pz, ph, pw, pd = pos_output[:, 1], pos_output[:, 2], pos_output[:, 3], pos_output[:, 4] 239 | lz, lh, lw, ld = pos_labels[:, 1], pos_labels[:, 2], pos_labels[:, 3], pos_labels[:, 4] 240 | 241 | regress_losses = [ 242 | self.regress_loss(pz, lz), 243 | self.regress_loss(ph, lh), 244 | self.regress_loss(pw, lw), 245 | self.regress_loss(pd, ld)] 246 | regress_losses_data = [l.data[0] for l in regress_losses] 247 | classify_loss = 0.5 * self.classify_loss( 248 | pos_prob, pos_labels[:, 0]) + 0.5 * self.classify_loss( 249 | neg_prob, neg_labels + 1) 250 | pos_correct = (pos_prob.data >= 0.5).sum() 251 | pos_total = len(pos_prob) 252 | 253 | else: 254 | regress_losses = [0,0,0,0] 255 | classify_loss = 0.5 * self.classify_loss( 256 | neg_prob, neg_labels + 1) 257 | pos_correct = 0 258 | pos_total = 0 259 | regress_losses_data = [0,0,0,0] 260 | classify_loss_data = classify_loss.data[0] 261 | 262 | loss = classify_loss 263 | for regress_loss in regress_losses: 264 | loss += regress_loss 265 | 266 | neg_correct = (neg_prob.data < 0.5).sum() 267 | neg_total = len(neg_prob) 268 | 269 | if isp: 270 | # print(prob.numpy()[0,0]) 271 | return [loss*(prob.numpy()[0,0]), classify_loss_data] + regress_losses_data + [pos_correct, pos_total, neg_correct, neg_total] 272 | 273 | return [loss, classify_loss_data] + regress_losses_data + [pos_correct, pos_total, neg_correct, neg_total] 274 | 275 | class GetPBB(object): 276 | def __init__(self, config): 277 | self.stride = config['stride'] 278 | self.anchors = np.asarray(config['anchors']) 279 | self.softmax = torch.nn.Softmax()#dim=-1) 280 | 281 | def __call__(self, output,thresh = -3, ismask=False, weaklst=None, model2=None): 282 | stride = self.stride 283 | anchors = self.anchors 284 | output = np.copy(output) 285 | offset = (float(stride) - 1) / 2 286 | output_size = output.shape 287 | oz = np.arange(offset, offset + stride * (output_size[0] - 1) + 1, stride) 288 | oh = np.arange(offset, offset + stride * (output_size[1] - 1) + 1, stride) 289 | ow = np.arange(offset, offset + stride * (output_size[2] - 1) + 1, stride) 290 | 291 | output[:, :, :, :, 1] = oz.reshape((-1, 1, 1, 1)) + output[:, :, :, :, 1] * anchors.reshape((1, 1, 1, -1)) # z 292 | output[:, :, :, :, 2] = oh.reshape((1, -1, 1, 1)) + output[:, :, :, :, 2] * anchors.reshape((1, 1, 1, -1)) 293 | output[:, :, :, :, 3] = ow.reshape((1, 1, -1, 1)) + output[:, :, :, :, 3] * anchors.reshape((1, 1, 1, -1)) 294 | output[:, :, :, :, 4] = np.exp(output[:, :, :, :, 4]) * anchors.reshape((1, 1, 1, -1)) 295 | if weaklst and model2: 296 | # mask = (output[..., 1] >= 0) 297 | ret = [] 298 | for wl in weaklst: 299 | # print(type(output[..., 1])) 300 | # print(output[..., 1].shape) 301 | mask = (output[..., 1] >= wl[2]) & (output[..., 1] <= wl[3]) 302 | if mask.shape[0] == 0 or len(np.where(mask))==1: 303 | ret.append(np.array([])) 304 | continue 305 | # print(mask.shape, len(np.where(mask))) 306 | xx, yy, zz, aa = np.where(mask) 307 | output = output[xx, yy, zz, aa] # prob, z, x, y, d 308 | feat = np.hstack([output[:,1].reshape((-1,1)) / wl[4], output[:,2].reshape((-1,1)) / wl[5], output[:,3].reshape((-1,1)) / wl[6]]) 309 | if feat.shape[0] == 0: 310 | ret.append(np.array([])) 311 | continue 312 | # print(output.shape, type(output), feat.shape, wl[0], xx.shape, type(xx)) 313 | pred = model2.forward(Variable(torch.from_numpy(feat).float(), requires_grad=False)).data.numpy() 314 | # print(pred.shape, type(pred.argmax(axis=1)==wl[0]), np.sum(pred.argmax(axis=1)==wl[0]), np.min(pred.argmax(axis=1)), np.max(pred.argmax(axis=1))) 315 | xxlst = np.where(pred.argmax(axis=1)==wl[0]) 316 | if not xxlst or xxlst[0].shape[0]==0: 317 | ret.append(np.array([])) 318 | continue 319 | # print(len(xxlst), xxlst[0].shape, type(xxlst[0]), xxlst[0][0]) 320 | output = np.array(output[(pred.argmax(axis=1)==wl[0])!=0, :]) 321 | # print(output.shape) 322 | output = np.array(output[output[:,0] > thresh, :]) 323 | # print(output.shape) 324 | pbb = nms(output, 0.1) # prob, z, x, y, d 325 | if pbb.shape[0] == 0: 326 | ret.append(pbb) 327 | continue 328 | # print(pbb.shape) 329 | feat = np.hstack([pbb[:,1].reshape((-1,1)) / wl[4], pbb[:,2].reshape((-1,1)) / wl[5], pbb[:,3].reshape((-1,1)) / wl[6]]) 330 | # print(feat.shape) 331 | linearout = model2.forward(Variable(torch.from_numpy(feat).float(), requires_grad=False)) 332 | # print(linearout.size()) 333 | linearprob = self.softmax.forward(linearout) 334 | # print(linearprob.size()) 335 | prob = 1/(1+np.exp(-pbb[:,0])) * 2*np.exp(-np.square(np.maximum(abs(wl[1]-pbb[:,1])-3.253443196/2, 0))/(2*(4.76/2)*(4.76/2)))/(np.sqrt(2*np.pi*(4.76/2)*(4.76/2))) * \ 336 | linearprob.data.numpy()[:,wl[0]] 337 | argmaxidx = np.argmax(prob) 338 | pbb[argmaxidx, 0] = np.max(prob) 339 | ret.append(pbb[np.argmax(prob), :]) 340 | return ret 341 | # output = -np.sort(-output, axis=0) 342 | else: 343 | mask = output[..., 0] > thresh 344 | xx,yy,zz,aa = np.where(mask) 345 | 346 | output = output[xx,yy,zz,aa] 347 | 348 | if ismask: 349 | return output,[xx,yy,zz,aa] 350 | else: 351 | return output 352 | 353 | #output = output[output[:, 0] >= self.conf_th] 354 | #bboxes = nms(output, self.nms_th) 355 | def nms(output, nms_th): 356 | if len(output) == 0: 357 | return output 358 | 359 | output = output[np.argsort(-output[:, 0])] 360 | bboxes = [output[0]] 361 | 362 | for i in np.arange(1, len(output)): 363 | bbox = output[i] 364 | flag = 1 365 | for j in range(len(bboxes)): 366 | if iou(bbox[1:5], bboxes[j][1:5]) >= nms_th: 367 | flag = -1 368 | break 369 | if flag == 1: 370 | bboxes.append(bbox) 371 | 372 | bboxes = np.asarray(bboxes, np.float32) 373 | return bboxes 374 | 375 | def iou(box0, box1): 376 | 377 | r0 = box0[3] / 2 378 | s0 = box0[:3] - r0 379 | e0 = box0[:3] + r0 380 | 381 | r1 = box1[3] / 2 382 | s1 = box1[:3] - r1 383 | e1 = box1[:3] + r1 384 | 385 | overlap = [] 386 | for i in range(len(s0)): 387 | overlap.append(max(0, min(e0[i], e1[i]) - max(s0[i], s1[i]))) 388 | 389 | intersection = overlap[0] * overlap[1] * overlap[2] 390 | union = box0[3] * box0[3] * box0[3] + box1[3] * box1[3] * box1[3] - intersection 391 | return intersection / union 392 | 393 | def acc(pbb, lbb, conf_th, nms_th, detect_th): 394 | pbb = pbb[pbb[:, 0] >= conf_th] 395 | pbb = nms(pbb, nms_th) 396 | 397 | tp = [] 398 | fp = [] 399 | fn = [] 400 | l_flag = np.zeros((len(lbb),), np.int32) 401 | for p in pbb: 402 | flag = 0 403 | bestscore = 0 404 | for i, l in enumerate(lbb): 405 | score = iou(p[1:5], l) 406 | if score>bestscore: 407 | bestscore = score 408 | besti = i 409 | if bestscore > detect_th: 410 | flag = 1 411 | if l_flag[besti] == 0: 412 | l_flag[besti] = 1 413 | tp.append(np.concatenate([p,[bestscore]],0)) 414 | else: 415 | fp.append(np.concatenate([p,[bestscore]],0)) 416 | if flag == 0: 417 | fp.append(np.concatenate([p,[bestscore]],0)) 418 | for i,l in enumerate(lbb): 419 | if l_flag[i]==0: 420 | score = [] 421 | for p in pbb: 422 | score.append(iou(p[1:5],l)) 423 | if len(score)!=0: 424 | bestscore = np.max(score) 425 | else: 426 | bestscore = 0 427 | if bestscore0: 454 | fn = np.concatenate([fn,tp[fn_i,:5]]) 455 | else: 456 | fn = fn 457 | if len(tp_in_topk)>0: 458 | tp = tp[tp_in_topk] 459 | else: 460 | tp = [] 461 | if len(fp_in_topk)>0: 462 | fp = newallp[fp_in_topk] 463 | else: 464 | fp = [] 465 | return tp, fp , fn 466 | -------------------------------------------------------------------------------- /weakdetectorv2/layers.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorv2/layers.pyc -------------------------------------------------------------------------------- /weakdetectorv2/res18.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from layers import * 4 | 5 | config = {} 6 | config['anchors'] = [5., 10., 20.] #[ 10.0, 30.0, 60.] 7 | config['chanel'] = 1 8 | config['crop_size'] = [96, 96, 96] 9 | config['stride'] = 4 10 | config['max_stride'] = 16 11 | config['num_neg'] = 800 12 | config['th_neg'] = 0.02 13 | config['th_pos_train'] = 0.5 14 | config['th_pos_val'] = 1 15 | config['num_hard'] = 2 16 | config['bound_size'] = 12 17 | config['reso'] = 1 18 | config['sizelim'] = 2.5 #3 #6. #mm 19 | config['sizelim2'] = 10 #30 20 | config['sizelim3'] = 20 #40 21 | config['aug_scale'] = True 22 | config['r_rand_crop'] = 0.3 23 | config['pad_value'] = 170 24 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 25 | 26 | 27 | config['augtype'] = {'flip':True,'swap':False,'scale':True,'rotate':False} 28 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','990fbe3f0a1b53878669967b9afd1441','adc3bbc63d40f8761c59be10f1e504c3'] 29 | 30 | #config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','d92998a73d4654a442e6d6ba15bbb827','990fbe3f0a1b53878669967b9afd1441','820245d8b211808bd18e78ff5be16fdb','adc3bbc63d40f8761c59be10f1e504c3', 31 | # '417','077','188','876','057','087','130','468'] 32 | 33 | class Net(nn.Module): 34 | def __init__(self): 35 | super(Net, self).__init__() 36 | # The first few layers consumes the most memory, so use simple convolution to save memory. 37 | # Call these layers preBlock, i.e., before the residual blocks of later layers. 38 | self.preBlock = nn.Sequential( 39 | nn.Conv3d(1, 24, kernel_size = 3, padding = 1), 40 | nn.BatchNorm3d(24), 41 | nn.ReLU(inplace = True), 42 | nn.Conv3d(24, 24, kernel_size = 3, padding = 1), 43 | nn.BatchNorm3d(24), 44 | nn.ReLU(inplace = True)) 45 | 46 | # 3 poolings, each pooling downsamples the feature map by a factor 2. 47 | # 3 groups of blocks. The first block of each group has one pooling. 48 | num_blocks_forw = [2,2,3,3] 49 | num_blocks_back = [3,3] 50 | self.featureNum_forw = [24,32,64,64,64] 51 | self.featureNum_back = [128,64,64] 52 | for i in range(len(num_blocks_forw)): 53 | blocks = [] 54 | for j in range(num_blocks_forw[i]): 55 | if j == 0: 56 | blocks.append(PostRes(self.featureNum_forw[i], self.featureNum_forw[i+1])) 57 | else: 58 | blocks.append(PostRes(self.featureNum_forw[i+1], self.featureNum_forw[i+1])) 59 | setattr(self, 'forw' + str(i + 1), nn.Sequential(*blocks)) 60 | 61 | 62 | for i in range(len(num_blocks_back)): 63 | blocks = [] 64 | for j in range(num_blocks_back[i]): 65 | if j == 0: 66 | if i==0: 67 | addition = 3 68 | else: 69 | addition = 0 70 | blocks.append(PostRes(self.featureNum_back[i+1]+self.featureNum_forw[i+2]+addition, self.featureNum_back[i])) 71 | else: 72 | blocks.append(PostRes(self.featureNum_back[i], self.featureNum_back[i])) 73 | setattr(self, 'back' + str(i + 2), nn.Sequential(*blocks)) 74 | 75 | self.maxpool1 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 76 | self.maxpool2 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 77 | self.maxpool3 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 78 | self.maxpool4 = nn.MaxPool3d(kernel_size=2,stride=2,return_indices =True) 79 | self.unmaxpool1 = nn.MaxUnpool3d(kernel_size=2,stride=2) 80 | self.unmaxpool2 = nn.MaxUnpool3d(kernel_size=2,stride=2) 81 | 82 | self.path1 = nn.Sequential( 83 | nn.ConvTranspose3d(64, 64, kernel_size = 2, stride = 2), 84 | nn.BatchNorm3d(64), 85 | nn.ReLU(inplace = True)) 86 | self.path2 = nn.Sequential( 87 | nn.ConvTranspose3d(64, 64, kernel_size = 2, stride = 2), 88 | nn.BatchNorm3d(64), 89 | nn.ReLU(inplace = True)) 90 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 91 | self.output = nn.Sequential(nn.Conv3d(self.featureNum_back[0], 64, kernel_size = 1), 92 | nn.ReLU(), 93 | #nn.Dropout3d(p = 0.3), 94 | nn.Conv3d(64, 5 * len(config['anchors']), kernel_size = 1)) 95 | 96 | def forward(self, x, coord): 97 | out = self.preBlock(x)#16 98 | out_pool,indices0 = self.maxpool1(out) 99 | out1 = self.forw1(out_pool)#32 100 | out1_pool,indices1 = self.maxpool2(out1) 101 | out2 = self.forw2(out1_pool)#64 102 | #out2 = self.drop(out2) 103 | out2_pool,indices2 = self.maxpool3(out2) 104 | out3 = self.forw3(out2_pool)#96 105 | out3_pool,indices3 = self.maxpool4(out3) 106 | out4 = self.forw4(out3_pool)#96 107 | #out4 = self.drop(out4) 108 | 109 | rev3 = self.path1(out4) 110 | comb3 = self.back3(torch.cat((rev3, out3), 1))#96+96 111 | #comb3 = self.drop(comb3) 112 | rev2 = self.path2(comb3) 113 | 114 | comb2 = self.back2(torch.cat((rev2, out2,coord), 1))#64+64 115 | comb2 = self.drop(comb2) 116 | out = self.output(comb2) 117 | size = out.size() 118 | out = out.view(out.size(0), out.size(1), -1) 119 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 120 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 121 | #out = out.view(-1, 5) 122 | return out 123 | 124 | 125 | def get_model(): 126 | net = Net() 127 | loss = Loss(config['num_hard']) 128 | get_pbb = GetPBB(config) 129 | return config, net, loss, get_pbb 130 | -------------------------------------------------------------------------------- /weakdetectorv2/res18.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorv2/res18.pyc -------------------------------------------------------------------------------- /weakdetectorv2/res_pool.py: -------------------------------------------------------------------------------- 1 | import torch 2 | from torch import nn 3 | from layers import * 4 | 5 | config = {} 6 | config['anchors'] = [ 10.0, 25.0, 40.0] 7 | config['chanel'] = 2 8 | config['crop_size'] = [64, 128, 128] 9 | config['stride'] = [2,4,4] 10 | config['max_stride'] = 16 11 | config['num_neg'] = 10 12 | config['th_neg'] = 0.2 13 | config['th_pos'] = 0.5 14 | config['num_hard'] = 1 15 | config['bound_size'] = 12 16 | config['reso'] = [1.5,0.75,0.75] 17 | config['sizelim'] = 6. #mm 18 | config['blacklist'] = ['868b024d9fa388b7ddab12ec1c06af38','d92998a73d4654a442e6d6ba15bbb827','990fbe3f0a1b53878669967b9afd1441','820245d8b211808bd18e78ff5be16fdb', 19 | '417','077','188','876','057','087','130','468'] 20 | 21 | class Net(nn.Module): 22 | def __init__(self): 23 | super(Net, self).__init__() 24 | # The first few layers consumes the most memory, so use simple convolution to save memory. 25 | # Call these layers preBlock, i.e., before the residual blocks of later layers. 26 | self.preBlock = nn.Sequential( 27 | nn.Conv3d(2, 16, kernel_size = 3, padding = 1), 28 | nn.BatchNorm3d(16), 29 | nn.ReLU(inplace = True), 30 | nn.Conv3d(16, 16, kernel_size = 3, padding = 1), 31 | nn.BatchNorm3d(16), 32 | nn.ReLU(inplace = True)) 33 | 34 | # 3 poolings, each pooling downsamples the feature map by a factor 2. 35 | # 3 groups of blocks. The first block of each group has one pooling. 36 | num_blocks = [6,6,6,6] 37 | n_in = [16, 32, 64,96] 38 | n_out = [32, 64, 96,96] 39 | for i in range(len(num_blocks)): 40 | blocks = [] 41 | for j in range(num_blocks[i]): 42 | if j == 0: 43 | if i ==0: 44 | blocks.append(nn.MaxPool3d(kernel_size=[1,2,2])) 45 | blocks.append(PostRes(n_in[i], n_out[i])) 46 | else: 47 | blocks.append(nn.MaxPool3d(kernel_size=2)) 48 | blocks.append(PostRes(n_out[i], n_out[i])) 49 | else: 50 | blocks.append(PostRes(n_out[i], n_out[i])) 51 | setattr(self, 'group' + str(i + 1), nn.Sequential(*blocks)) 52 | 53 | self.path1 = nn.Sequential( 54 | nn.Conv3d(64, 32, kernel_size = 3, padding = 1), 55 | nn.BatchNorm3d(32), 56 | nn.ReLU(inplace = True)) 57 | 58 | self.path2 = nn.Sequential( 59 | nn.ConvTranspose3d(96, 32, kernel_size = 2, stride = 2), 60 | nn.BatchNorm3d(32), 61 | nn.ReLU(inplace = True)) 62 | 63 | self.path3 = nn.Sequential( 64 | nn.ConvTranspose3d(96, 32, kernel_size = 2, stride = 2), 65 | nn.BatchNorm3d(32), 66 | nn.ReLU(inplace = True), 67 | nn.ConvTranspose3d(32, 32, kernel_size = 2, stride = 2), 68 | nn.BatchNorm3d(32), 69 | nn.ReLU(inplace = True)) 70 | 71 | self.combine = nn.Sequential( 72 | nn.Conv3d(96, 128, kernel_size = 1), 73 | nn.BatchNorm3d(128), 74 | nn.ReLU(inplace = True)) 75 | 76 | self.drop = nn.Dropout3d(p = 0.5, inplace = False) 77 | self.output = nn.Conv3d(128, 5 * len(config['anchors']), kernel_size = 1) 78 | 79 | def forward(self, x): 80 | x = x.view(x.size(0), 2,x.size(2), x.size(3), x.size(4)) 81 | out = self.preBlock(x) 82 | 83 | out1 = self.group1(out) 84 | out2 = self.group2(out1) 85 | out3 = self.group3(out2) 86 | out4 = self.group4(out3) 87 | 88 | out2 = self.path1(out2) 89 | out3 = self.path2(out3) 90 | out4 = self.path3(out4) 91 | out = torch.cat((out2, out3, out4), 1) 92 | 93 | out = self.combine(out) 94 | out = self.drop(out) 95 | out = self.output(out) 96 | size = out.size() 97 | out = out.view(out.size(0), out.size(1), -1) 98 | #out = out.transpose(1, 4).transpose(1, 2).transpose(2, 3).contiguous() 99 | out = out.transpose(1, 2).contiguous().view(size[0], size[2], size[3], size[4], len(config['anchors']), 5) 100 | #out = out.view(-1, 5) 101 | return out 102 | 103 | def get_model(): 104 | net = Net() 105 | loss = Loss(config['num_hard']) 106 | get_pbb = GetPBB(config) 107 | return config, net, loss, get_pbb 108 | -------------------------------------------------------------------------------- /weakdetectorv2/split_combine.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import numpy as np 3 | class SplitComb(): 4 | def __init__(self,side_len,max_stride,stride,margin,pad_value): 5 | self.side_len = side_len 6 | self.max_stride = max_stride 7 | self.stride = stride 8 | self.margin = margin 9 | self.pad_value = pad_value 10 | 11 | def split(self, data, side_len = None, max_stride = None, margin = None): 12 | if side_len==None: 13 | side_len = self.side_len 14 | if max_stride == None: 15 | max_stride = self.max_stride 16 | if margin == None: 17 | margin = self.margin 18 | 19 | assert(side_len > margin) 20 | assert(side_len % max_stride == 0) 21 | assert(margin % max_stride == 0) 22 | 23 | splits = [] 24 | _, z, h, w = data.shape 25 | 26 | nz = int(np.ceil(float(z) / side_len)) 27 | nh = int(np.ceil(float(h) / side_len)) 28 | nw = int(np.ceil(float(w) / side_len)) 29 | 30 | nzhw = [nz,nh,nw] 31 | self.nzhw = nzhw 32 | 33 | pad = [ [0, 0], 34 | [margin, nz * side_len - z + margin], 35 | [margin, nh * side_len - h + margin], 36 | [margin, nw * side_len - w + margin]] 37 | data = np.pad(data, pad, 'edge') 38 | 39 | for iz in range(nz): 40 | for ih in range(nh): 41 | for iw in range(nw): 42 | sz = iz * side_len 43 | ez = (iz + 1) * side_len + 2 * margin 44 | sh = ih * side_len 45 | eh = (ih + 1) * side_len + 2 * margin 46 | sw = iw * side_len 47 | ew = (iw + 1) * side_len + 2 * margin 48 | 49 | split = data[np.newaxis, :, sz:ez, sh:eh, sw:ew] 50 | splits.append(split) 51 | 52 | splits = np.concatenate(splits, 0) 53 | return splits,nzhw 54 | 55 | def combine(self, output, nzhw = None, side_len=None, stride=None, margin=None): 56 | 57 | if side_len==None: 58 | side_len = self.side_len 59 | if stride == None: 60 | stride = self.stride 61 | if margin == None: 62 | margin = self.margin 63 | if len(nzhw) != 0: 64 | nz,nh,nw = nzhw 65 | else: 66 | nz = self.nz 67 | nh = self.nh 68 | nw = self.nw 69 | # if nzhw==None: 70 | # nz = self.nz 71 | # nh = self.nh 72 | # nw = self.nw 73 | # else: 74 | # nz,nh,nw = nzhw 75 | assert(side_len % stride == 0) 76 | assert(margin % stride == 0) 77 | side_len /= stride 78 | margin /= stride 79 | 80 | splits = [] 81 | for i in range(len(output)): 82 | splits.append(output[i]) 83 | 84 | output = -1000000 * np.ones(( 85 | nz * side_len, 86 | nh * side_len, 87 | nw * side_len, 88 | splits[0].shape[3], 89 | splits[0].shape[4]), np.float32) 90 | 91 | idx = 0 92 | for iz in range(nz): 93 | for ih in range(nh): 94 | for iw in range(nw): 95 | sz = iz * side_len 96 | ez = (iz + 1) * side_len 97 | sh = ih * side_len 98 | eh = (ih + 1) * side_len 99 | sw = iw * side_len 100 | ew = (iw + 1) * side_len 101 | 102 | split = splits[idx][margin:margin + side_len, margin:margin + side_len, margin:margin + side_len] 103 | output[sz:ez, sh:eh, sw:ew] = split 104 | idx += 1 105 | 106 | return output 107 | -------------------------------------------------------------------------------- /weakdetectorv2/split_combine.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorv2/split_combine.pyc -------------------------------------------------------------------------------- /weakdetectorv2/utils.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorv2/utils.pyc -------------------------------------------------------------------------------- /weakdetectorv2/weakdata.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import torch 3 | from torch.utils.data import Dataset 4 | import os 5 | import time 6 | import collections 7 | import random 8 | from layers import iou 9 | from scipy.ndimage import zoom 10 | import warnings 11 | from scipy.ndimage.interpolation import rotate 12 | import pandas as pd 13 | class DataBowl3Detector(Dataset): 14 | def __init__(self, data_dir, split_path, labelfname, config, phase='train', split_comber=None): 15 | assert(phase == 'train')# or phase == 'val' or phase == 'test') 16 | self.phase = phase 17 | self.max_stride = config['max_stride'] 18 | self.stride = config['stride'] 19 | sizelim = config['sizelim']/config['reso'] 20 | sizelim2 = config['sizelim2']/config['reso'] 21 | sizelim3 = config['sizelim3']/config['reso'] 22 | self.blacklist = config['blacklist'] 23 | self.isScale = config['aug_scale'] 24 | self.r_rand = config['r_rand_crop'] 25 | self.augtype = config['augtype'] 26 | self.pad_value = config['pad_value'] 27 | self.split_comber = split_comber 28 | idcs = split_path # np.load(split_path) 29 | if phase!='test': 30 | idcs = [f for f in idcs if (f not in self.blacklist)] 31 | 32 | self.filenames = [] 33 | for idx in idcs: 34 | if 'tianchi' in data_dir: 35 | self.filenames.append(os.path.join(data_dir, '%s_clean.npy' % idx.split('/')[-1])) 36 | else: 37 | self.filenames.append(os.path.join(data_dir, '%s_clean.npy' % idx)) 38 | self.kagglenames = [f for f in self.filenames]# if len(f.split('/')[-1].split('_')[0])>20] 39 | 40 | pdfrm = pd.read_csv(labelfname, names=['fname', 'position', 'centerslice']) 41 | calibfnamelst = pdfrm['fname'].tolist()[1:] 42 | calibpositionlst = pdfrm['position'].tolist()[1:] 43 | calibcenterslicelst = pdfrm['centerslice'].tolist()[1:] 44 | calibdict = {} 45 | for idx, calibfname in enumerate(calibfnamelst): 46 | if calibfname not in calibdict: 47 | calibdict[calibfname] = [[calibpositionlst[idx], calibcenterslicelst[idx]]] 48 | else: 49 | calibdict[calibfname].append([calibpositionlst[idx], calibcenterslicelst[idx]]) 50 | labels = [] 51 | print len(idcs) 52 | for idx in idcs: 53 | labels.append(calibdict[idx]) 54 | 55 | self.sample_bboxes = labels 56 | if self.phase != 'test': 57 | self.bboxes = [] 58 | for i, l in enumerate(labels): 59 | # print l 60 | if len(l) > 0 : 61 | for t in l: 62 | if len(t) != 2: print(t,i) 63 | self.bboxes.append([np.concatenate([[i],np.array([t[0], t[1]])])]) 64 | 65 | self.bboxes = np.concatenate(self.bboxes,axis = 0) 66 | 67 | self.crop = Crop(config) 68 | self.label_mapping = LabelMapping(config, self.phase) 69 | 70 | def __getitem__(self, idx,split=None): 71 | t = time.time() 72 | np.random.seed(int(str(t%1)[2:7]))#seed according to time 73 | 74 | isRandomImg = False 75 | if self.phase !='test': 76 | if idx>=len(self.bboxes): 77 | isRandom = True 78 | idx = idx%len(self.bboxes) 79 | isRandomImg = np.random.randint(2) 80 | else: 81 | isRandom = False 82 | else: 83 | isRandom = False 84 | 85 | if self.phase != 'test': 86 | if not isRandomImg: 87 | bbox = self.bboxes[idx] 88 | filename = self.filenames[int(bbox[0])] 89 | imgs = np.load(filename) 90 | bboxes = self.sample_bboxes[int(bbox[0])] 91 | isScale = self.augtype['scale'] and (self.phase=='train') 92 | sample, target, bboxes, coord = self.crop(imgs, bbox[1:], bboxes,isScale,isRandom) 93 | if self.phase=='train' and not isRandom: 94 | sample, target, bboxes, coord = augment(sample, target, bboxes, coord, 95 | ifflip = self.augtype['flip'], ifrotate=self.augtype['rotate'], ifswap = self.augtype['swap']) 96 | else: 97 | randimid = np.random.randint(len(self.kagglenames)) 98 | filename = self.kagglenames[randimid] 99 | imgs = np.load(filename) 100 | bboxes = self.sample_bboxes[randimid] 101 | isScale = self.augtype['scale'] and (self.phase=='train') 102 | sample, target, bboxes, coord = self.crop(imgs, [], bboxes,isScale=False,isRand=True) 103 | # print sample.shape, target.shape, bboxes.shape 104 | label = self.label_mapping(sample.shape[1:], target, bboxes, filename) 105 | sample = (sample.astype(np.float32)-128)/128 106 | return torch.from_numpy(sample), torch.from_numpy(label), coord 107 | else: 108 | imgs = np.load(self.filenames[idx]) 109 | bboxes = self.sample_bboxes[idx] 110 | nz, nh, nw = imgs.shape[1:] 111 | pz = int(np.ceil(float(nz) / self.stride)) * self.stride 112 | ph = int(np.ceil(float(nh) / self.stride)) * self.stride 113 | pw = int(np.ceil(float(nw) / self.stride)) * self.stride 114 | imgs = np.pad(imgs, [[0,0],[0, pz - nz], [0, ph - nh], [0, pw - nw]], 'constant',constant_values = self.pad_value) 115 | 116 | xx,yy,zz = np.meshgrid(np.linspace(-0.5,0.5,imgs.shape[1]/self.stride), 117 | np.linspace(-0.5,0.5,imgs.shape[2]/self.stride), 118 | np.linspace(-0.5,0.5,imgs.shape[3]/self.stride),indexing ='ij') 119 | coord = np.concatenate([xx[np.newaxis,...], yy[np.newaxis,...],zz[np.newaxis,:]],0).astype('float32') 120 | imgs, nzhw = self.split_comber.split(imgs) 121 | coord2, nzhw2 = self.split_comber.split(coord, 122 | side_len = self.split_comber.side_len/self.stride, 123 | max_stride = self.split_comber.max_stride/self.stride, 124 | margin = self.split_comber.margin/self.stride) 125 | assert np.all(nzhw==nzhw2) 126 | imgs = (imgs.astype(np.float32)-128)/128 127 | return torch.from_numpy(imgs), bboxes, torch.from_numpy(coord2), np.array(nzhw) 128 | 129 | def __len__(self): 130 | if self.phase == 'train': 131 | return len(self.bboxes)/(1-self.r_rand) 132 | elif self.phase =='val': 133 | return len(self.bboxes) 134 | else: 135 | return len(self.sample_bboxes) 136 | 137 | 138 | def augment(sample, target, bboxes, coord, ifflip = True, ifrotate=True, ifswap = True): 139 | # angle1 = np.random.rand()*180 140 | if ifrotate: 141 | validrot = False 142 | counter = 0 143 | while not validrot: 144 | newtarget = np.copy(target) 145 | angle1 = np.random.rand()*180 146 | size = np.array(sample.shape[2:4]).astype('float') 147 | rotmat = np.array([[np.cos(angle1/180*np.pi),-np.sin(angle1/180*np.pi)],[np.sin(angle1/180*np.pi),np.cos(angle1/180*np.pi)]]) 148 | newtarget[1:3] = np.dot(rotmat,target[1:3]-size/2)+size/2 149 | if np.all(newtarget[:3]>target[3]) and np.all(newtarget[:3]< np.array(sample.shape[1:4])-newtarget[3]): 150 | validrot = True 151 | target = newtarget 152 | sample = rotate(sample,angle1,axes=(2,3),reshape=False) 153 | coord = rotate(coord,angle1,axes=(2,3),reshape=False) 154 | for box in bboxes: 155 | box[1:3] = np.dot(rotmat,box[1:3]-size/2)+size/2 156 | else: 157 | counter += 1 158 | if counter ==3: 159 | break 160 | if ifswap: 161 | if sample.shape[1]==sample.shape[2] and sample.shape[1]==sample.shape[3]: 162 | axisorder = np.random.permutation(3) 163 | sample = np.transpose(sample,np.concatenate([[0],axisorder+1])) 164 | coord = np.transpose(coord,np.concatenate([[0],axisorder+1])) 165 | target[:3] = target[:3][axisorder] 166 | bboxes[:,:3] = bboxes[:,:3][:,axisorder] 167 | 168 | if ifflip: 169 | # flipid = np.array([np.random.randint(2),np.random.randint(2),np.random.randint(2)])*2-1 170 | flipid = np.array([1,np.random.randint(2),np.random.randint(2)])*2-1 171 | sample = np.ascontiguousarray(sample[:,::flipid[0],::flipid[1],::flipid[2]]) 172 | coord = np.ascontiguousarray(coord[:,::flipid[0],::flipid[1],::flipid[2]]) 173 | for ax in range(3): 174 | if flipid[ax]==-1: 175 | target[ax] = np.array(sample.shape[ax+1])-target[ax] 176 | bboxes[:,ax]= np.array(sample.shape[ax+1])-bboxes[:,ax] 177 | return sample, target, bboxes, coord 178 | 179 | class Crop(object): 180 | def __init__(self, config): 181 | self.crop_size = config['crop_size'] 182 | self.bound_size = config['bound_size'] 183 | self.stride = config['stride'] 184 | self.pad_value = config['pad_value'] 185 | def __call__(self, imgs, target, bboxes,isScale=False,isRand=False): 186 | crop_size=self.crop_size 187 | bound_size = self.bound_size 188 | target = np.copy(target) 189 | bboxes = np.copy(bboxes) 190 | 191 | start = [] 192 | for i in range(3): 193 | if not isRand: 194 | r = target[3] / 2 195 | s = np.floor(target[i] - r)+ 1 - bound_size 196 | e = np.ceil(target[i] + r)+ 1 + bound_size - crop_size[i] 197 | else: 198 | s = np.max([imgs.shape[i+1]-crop_size[i]/2,imgs.shape[i+1]/2+bound_size]) 199 | e = np.min([crop_size[i]/2, imgs.shape[i+1]/2-bound_size]) 200 | target = np.array([np.nan,np.nan,np.nan,np.nan]) 201 | if s>e: 202 | start.append(np.random.randint(e,s))#! 203 | else: 204 | start.append(int(target[i])-crop_size[i]/2+np.random.randint(-bound_size/2,bound_size/2)) 205 | 206 | normstart = np.array(start).astype('float32')/np.array(imgs.shape[1:])-0.5 207 | normsize = np.array(crop_size).astype('float32')/np.array(imgs.shape[1:]) 208 | xx,yy,zz = np.meshgrid(np.linspace(normstart[0],normstart[0]+normsize[0],self.crop_size[0]/self.stride), 209 | np.linspace(normstart[1],normstart[1]+normsize[1],self.crop_size[1]/self.stride), 210 | np.linspace(normstart[2],normstart[2]+normsize[2],self.crop_size[2]/self.stride),indexing ='ij') 211 | coord = np.concatenate([xx[np.newaxis,...], yy[np.newaxis,...],zz[np.newaxis,:]],0).astype('float32') 212 | 213 | pad = [] 214 | pad.append([0,0]) 215 | for i in range(3): 216 | leftpad = max(0,-start[i]) 217 | rightpad = max(0,start[i]+crop_size[i]-imgs.shape[i+1]) 218 | pad.append([leftpad,rightpad]) 219 | crop = imgs[:, 220 | max(start[0],0):min(start[0] + crop_size[0],imgs.shape[1]), 221 | max(start[1],0):min(start[1] + crop_size[1],imgs.shape[2]), 222 | max(start[2],0):min(start[2] + crop_size[2],imgs.shape[3])] 223 | crop = np.pad(crop,pad,'constant',constant_values =self.pad_value) 224 | for i in range(3): 225 | target[i] = target[i] - start[i] 226 | for i in range(len(bboxes)): 227 | for j in range(3): 228 | bboxes[i][j] = bboxes[i][j] - start[j] 229 | 230 | if isScale: 231 | with warnings.catch_warnings(): 232 | warnings.simplefilter("ignore") 233 | crop = zoom(crop,[1,scale,scale,scale],order=1) 234 | newpad = self.crop_size[0]-crop.shape[1:][0] 235 | if newpad<0: 236 | crop = crop[:,:-newpad,:-newpad,:-newpad] 237 | elif newpad>0: 238 | pad2 = [[0,0],[0,newpad],[0,newpad],[0,newpad]] 239 | crop = np.pad(crop, pad2, 'constant', constant_values=self.pad_value) 240 | for i in range(4): 241 | target[i] = target[i]*scale 242 | for i in range(len(bboxes)): 243 | for j in range(4): 244 | bboxes[i][j] = bboxes[i][j]*scale 245 | # print(coord.shape) 246 | return crop, target, bboxes, coord 247 | 248 | class LabelMapping(object): 249 | def __init__(self, config, phase): 250 | self.stride = np.array(config['stride']) 251 | self.num_neg = int(config['num_neg']) 252 | self.th_neg = config['th_neg'] 253 | self.anchors = np.asarray(config['anchors']) 254 | self.phase = phase 255 | if phase == 'train': 256 | self.th_pos = config['th_pos_train'] 257 | elif phase == 'val': 258 | self.th_pos = config['th_pos_val'] 259 | 260 | 261 | def __call__(self, input_size, target, bboxes, filename): 262 | stride = self.stride 263 | num_neg = self.num_neg 264 | th_neg = self.th_neg 265 | anchors = self.anchors 266 | th_pos = self.th_pos 267 | 268 | output_size = [] 269 | for i in range(3): 270 | if input_size[i] % stride != 0: 271 | print filename 272 | # assert(input_size[i] % stride == 0) 273 | output_size.append(input_size[i] / stride) 274 | 275 | label = -1 * np.ones(output_size + [len(anchors), 5], np.float32) 276 | offset = ((stride.astype('float')) - 1) / 2 277 | oz = np.arange(offset, offset + stride * (output_size[0] - 1) + 1, stride) 278 | oh = np.arange(offset, offset + stride * (output_size[1] - 1) + 1, stride) 279 | ow = np.arange(offset, offset + stride * (output_size[2] - 1) + 1, stride) 280 | 281 | for bbox in bboxes: 282 | for i, anchor in enumerate(anchors): 283 | iz, ih, iw = select_samples(bbox, anchor, th_neg, oz, oh, ow) 284 | label[iz, ih, iw, i, 0] = 0 285 | 286 | if self.phase == 'train' and self.num_neg > 0: 287 | neg_z, neg_h, neg_w, neg_a = np.where(label[:, :, :, :, 0] == -1) 288 | neg_idcs = random.sample(range(len(neg_z)), min(num_neg, len(neg_z))) 289 | neg_z, neg_h, neg_w, neg_a = neg_z[neg_idcs], neg_h[neg_idcs], neg_w[neg_idcs], neg_a[neg_idcs] 290 | label[:, :, :, :, 0] = 0 291 | label[neg_z, neg_h, neg_w, neg_a, 0] = -1 292 | 293 | if np.isnan(target[0]): 294 | return label 295 | iz, ih, iw, ia = [], [], [], [] 296 | for i, anchor in enumerate(anchors): 297 | iiz, iih, iiw = select_samples(target, anchor, th_pos, oz, oh, ow) 298 | iz.append(iiz) 299 | ih.append(iih) 300 | iw.append(iiw) 301 | ia.append(i * np.ones((len(iiz),), np.int64)) 302 | iz = np.concatenate(iz, 0) 303 | ih = np.concatenate(ih, 0) 304 | iw = np.concatenate(iw, 0) 305 | ia = np.concatenate(ia, 0) 306 | flag = True 307 | if len(iz) == 0: 308 | pos = [] 309 | for i in range(3): 310 | pos.append(max(0, int(np.round((target[i] - offset) / stride)))) 311 | idx = np.argmin(np.abs(np.log(target[3] / anchors))) 312 | pos.append(idx) 313 | flag = False 314 | else: 315 | idx = random.sample(range(len(iz)), 1)[0] 316 | pos = [iz[idx], ih[idx], iw[idx], ia[idx]] 317 | dz = (target[0] - oz[pos[0]]) / anchors[pos[3]] 318 | dh = (target[1] - oh[pos[1]]) / anchors[pos[3]] 319 | dw = (target[2] - ow[pos[2]]) / anchors[pos[3]] 320 | dd = np.log(target[3] / anchors[pos[3]]) 321 | label[pos[0], pos[1], pos[2], pos[3], :] = [1, dz, dh, dw, dd] 322 | return label 323 | 324 | def select_samples(bbox, anchor, th, oz, oh, ow): 325 | z, h, w, d = bbox 326 | max_overlap = min(d, anchor) 327 | min_overlap = np.power(max(d, anchor), 3) * th / max_overlap / max_overlap 328 | if min_overlap > max_overlap: 329 | return np.zeros((0,), np.int64), np.zeros((0,), np.int64), np.zeros((0,), np.int64) 330 | else: 331 | s = z - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 332 | e = z + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 333 | mz = np.logical_and(oz >= s, oz <= e) 334 | iz = np.where(mz)[0] 335 | 336 | s = h - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 337 | e = h + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 338 | mh = np.logical_and(oh >= s, oh <= e) 339 | ih = np.where(mh)[0] 340 | 341 | s = w - 0.5 * np.abs(d - anchor) - (max_overlap - min_overlap) 342 | e = w + 0.5 * np.abs(d - anchor) + (max_overlap - min_overlap) 343 | mw = np.logical_and(ow >= s, ow <= e) 344 | iw = np.where(mw)[0] 345 | 346 | if len(iz) == 0 or len(ih) == 0 or len(iw) == 0: 347 | return np.zeros((0,), np.int64), np.zeros((0,), np.int64), np.zeros((0,), np.int64) 348 | 349 | lz, lh, lw = len(iz), len(ih), len(iw) 350 | iz = iz.reshape((-1, 1, 1)) 351 | ih = ih.reshape((1, -1, 1)) 352 | iw = iw.reshape((1, 1, -1)) 353 | iz = np.tile(iz, (1, lh, lw)).reshape((-1)) 354 | ih = np.tile(ih, (lz, 1, lw)).reshape((-1)) 355 | iw = np.tile(iw, (lz, lh, 1)).reshape((-1)) 356 | centers = np.concatenate([ 357 | oz[iz].reshape((-1, 1)), 358 | oh[ih].reshape((-1, 1)), 359 | ow[iw].reshape((-1, 1))], axis = 1) 360 | 361 | r0 = anchor / 2 362 | s0 = centers - r0 363 | e0 = centers + r0 364 | 365 | r1 = d / 2 366 | s1 = bbox[:3] - r1 367 | s1 = s1.reshape((1, -1)) 368 | e1 = bbox[:3] + r1 369 | e1 = e1.reshape((1, -1)) 370 | 371 | overlap = np.maximum(0, np.minimum(e0, e1) - np.maximum(s0, s1)) 372 | 373 | intersection = overlap[:, 0] * overlap[:, 1] * overlap[:, 2] 374 | union = anchor * anchor * anchor + d * d * d - intersection 375 | 376 | iou = intersection / union 377 | 378 | mask = iou >= th 379 | #if th > 0.4: 380 | # if np.sum(mask) == 0: 381 | # print(['iou not large', iou.max()]) 382 | # else: 383 | # print(['iou large', iou[mask]]) 384 | iz = iz[mask] 385 | ih = ih[mask] 386 | iw = iw[mask] 387 | return iz, ih, iw 388 | 389 | def collate(batch): 390 | if torch.is_tensor(batch[0]): 391 | return [b.unsqueeze(0) for b in batch] 392 | elif isinstance(batch[0], np.ndarray): 393 | return batch 394 | elif isinstance(batch[0], int): 395 | return torch.LongTensor(batch) 396 | elif isinstance(batch[0], collections.Iterable): 397 | transposed = zip(*batch) 398 | return [collate(samples) for samples in transposed] 399 | 400 | -------------------------------------------------------------------------------- /weakdetectorv2/weakdatav2.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/uci-cbcl/DeepEM-for-Weakly-Supervised-Detection-1/89f6f72a7d5ccb558c4659fbb893bf3d93ef6f2e/weakdetectorv2/weakdatav2.pyc --------------------------------------------------------------------------------