Spaces:
				
			
			
	
			
			
		Running
		
			on 
			
			Zero
	
	
	
			
			
	
	
	
	
		
		
		Running
		
			on 
			
			Zero
	| from collections import OrderedDict | |
| import os | |
| import numpy as np | |
| import torch | |
| import torch.nn.functional as F | |
| import os | |
| from skimage.filters import threshold_sauvola | |
| import cv2 | |
| def second2hours(seconds): | |
| h = seconds//3600 | |
| seconds %= 3600 | |
| m = seconds//60 | |
| seconds %= 60 | |
| hms = '{:d} H : {:d} Min'.format(int(h),int(m)) | |
| return hms | |
| def dict2string(loss_dict): | |
| loss_string = '' | |
| for key, value in loss_dict.items(): | |
| loss_string += key+' {:.4f}, '.format(value) | |
| return loss_string[:-2] | |
| def mkdir(dir): | |
| if not os.path.exists(dir): | |
| os.makedirs(dir) | |
| def convert_state_dict(state_dict): | |
| """Converts a state dict saved from a dataParallel module to normal | |
| module state_dict inplace | |
| :param state_dict is the loaded DataParallel model_state | |
| """ | |
| new_state_dict = OrderedDict() | |
| for k, v in state_dict.items(): | |
| name = k[7:] # remove `module.` | |
| new_state_dict[name] = v | |
| return new_state_dict | |
| def get_lr(optimizer): | |
| for param_group in optimizer.param_groups: | |
| return float(param_group['lr']) | |
| def torch2cvimg(tensor,min=0,max=1): | |
| ''' | |
| input: | |
| tensor -> torch.tensor BxCxHxW C can be 1,3 | |
| return | |
| im -> ndarray uint8 HxWxC | |
| ''' | |
| im_list = [] | |
| for i in range(tensor.shape[0]): | |
| im = tensor.detach().cpu().data.numpy()[i] | |
| im = im.transpose(1,2,0) | |
| im = np.clip(im,min,max) | |
| im = ((im-min)/(max-min)*255).astype(np.uint8) | |
| im_list.append(im) | |
| return im_list | |
| def cvimg2torch(img,min=0,max=1): | |
| ''' | |
| input: | |
| im -> ndarray uint8 HxWxC | |
| return | |
| tensor -> torch.tensor BxCxHxW | |
| ''' | |
| img = img.astype(float) / 255.0 | |
| img = img.transpose(2, 0, 1) # NHWC -> NCHW | |
| img = np.expand_dims(img, 0) | |
| img = torch.from_numpy(img).float() | |
| return img | |
| def setup_seed(seed): | |
| # np.random.seed(seed) | |
| # random.seed(seed) | |
| # torch.manual_seed(seed) #cpu | |
| # torch.cuda.manual_seed_all(seed) #并行gpu | |
| torch.backends.cudnn.deterministic = True #cpu/gpu结果一致 | |
| # torch.backends.cudnn.benchmark = False #训练集变化不大时使训练加速 | |
| def SauvolaModBinarization(image,n1=51,n2=51,k1=0.3,k2=0.3,default=True): | |
| ''' | |
| Binarization using Sauvola's algorithm | |
| @name : SauvolaModBinarization | |
| parameters | |
| @param image (numpy array of shape (3/1) of type np.uint8): color or gray scale image | |
| optional parameters | |
| @param n1 (int) : window size for running sauvola during the first pass | |
| @param n2 (int): window size for running sauvola during the second pass | |
| @param k1 (float): k value corresponding to sauvola during the first pass | |
| @param k2 (float): k value corresponding to sauvola during the second pass | |
| @param default (bool) : bollean variable to set the above parameter as default. | |
| @param default is set to True : thus default values of the above optional parameters (n1,n2,k1,k2) are set to | |
| n1 = 5 % of min(image height, image width) | |
| n2 = 10 % of min(image height, image width) | |
| k1 = 0.5 | |
| k2 = 0.5 | |
| Returns | |
| @return A binary image of same size as @param image | |
| @cite https://drive.google.com/file/d/1D3CyI5vtodPJeZaD2UV5wdcaIMtkBbdZ/view?usp=sharing | |
| ''' | |
| if(default): | |
| n1 = int(0.05*min(image.shape[0],image.shape[1])) | |
| if (n1%2==0): | |
| n1 = n1+1 | |
| n2 = int(0.1*min(image.shape[0],image.shape[1])) | |
| if (n2%2==0): | |
| n2 = n2+1 | |
| k1 = 0.5 | |
| k2 = 0.5 | |
| if(image.ndim==3): | |
| gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) | |
| else: | |
| gray = np.copy(image) | |
| T1 = threshold_sauvola(gray, window_size=n1,k=k1) | |
| max_val = np.amax(gray) | |
| min_val = np.amin(gray) | |
| C = np.copy(T1) | |
| C = C.astype(np.float32) | |
| C[gray > T1] = (gray[gray > T1] - T1[gray > T1])/(max_val - T1[gray > T1]) | |
| C[gray <= T1] = 0 | |
| C = C * 255.0 | |
| new_in = np.copy(C.astype(np.uint8)) | |
| T2 = threshold_sauvola(new_in, window_size=n2,k=k2) | |
| binary = np.copy(gray) | |
| binary[new_in <= T2] = 0 | |
| binary[new_in > T2] = 255 | |
| return binary,T2 | |
| def getBasecoord(h,w): | |
| base_coord0 = np.tile(np.arange(h).reshape(h,1),(1,w)).astype(np.float32) | |
| base_coord1 = np.tile(np.arange(w).reshape(1,w),(h,1)).astype(np.float32) | |
| base_coord = np.concatenate((np.expand_dims(base_coord1,-1),np.expand_dims(base_coord0,-1)),-1) | |
| return base_coord | |
| import numpy as np | |
| from scipy import ndimage as ndi | |
| # lookup tables for bwmorph_thin | |
| G123_LUT = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, | |
| 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, | |
| 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, | |
| 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, | |
| 0, 0, 0], dtype=np.bool_) | |
| G123P_LUT = np.array([0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, | |
| 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, | |
| 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, | |
| 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, | |
| 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
| 0, 0, 0], dtype=np.bool_) | |
| def bwmorph(image, n_iter=None): | |
| """ | |
| Perform morphological thinning of a binary image | |
| Parameters | |
| ---------- | |
| image : binary (M, N) ndarray | |
| The image to be thinned. | |
| n_iter : int, number of iterations, optional | |
| Regardless of the value of this parameter, the thinned image | |
| is returned immediately if an iteration produces no change. | |
| If this parameter is specified it thus sets an upper bound on | |
| the number of iterations performed. | |
| Returns | |
| ------- | |
| out : ndarray of bools | |
| Thinned image. | |
| See also | |
| -------- | |
| skeletonize | |
| Notes | |
| ----- | |
| This algorithm [1]_ works by making multiple passes over the image, | |
| removing pixels matching a set of criteria designed to thin | |
| connected regions while preserving eight-connected components and | |
| 2 x 2 squares [2]_. In each of the two sub-iterations the algorithm | |
| correlates the intermediate skeleton image with a neighborhood mask, | |
| then looks up each neighborhood in a lookup table indicating whether | |
| the central pixel should be deleted in that sub-iteration. | |
| References | |
| ---------- | |
| .. [1] Z. Guo and R. W. Hall, "Parallel thinning with | |
| two-subiteration algorithms," Comm. ACM, vol. 32, no. 3, | |
| pp. 359-373, 1989. | |
| .. [2] Lam, L., Seong-Whan Lee, and Ching Y. Suen, "Thinning | |
| Methodologies-A Comprehensive Survey," IEEE Transactions on | |
| Pattern Analysis and Machine Intelligence, Vol 14, No. 9, | |
| September 1992, p. 879 | |
| Examples | |
| -------- | |
| >>> square = np.zeros((7, 7), dtype=np.uint8) | |
| >>> square[1:-1, 2:-2] = 1 | |
| >>> square[0,1] = 1 | |
| >>> square | |
| array([[0, 1, 0, 0, 0, 0, 0], | |
| [0, 0, 1, 1, 1, 0, 0], | |
| [0, 0, 1, 1, 1, 0, 0], | |
| [0, 0, 1, 1, 1, 0, 0], | |
| [0, 0, 1, 1, 1, 0, 0], | |
| [0, 0, 1, 1, 1, 0, 0], | |
| [0, 0, 0, 0, 0, 0, 0]], dtype=uint8) | |
| >>> skel = bwmorph_thin(square) | |
| >>> skel.astype(np.uint8) | |
| array([[0, 1, 0, 0, 0, 0, 0], | |
| [0, 0, 1, 0, 0, 0, 0], | |
| [0, 0, 0, 1, 0, 0, 0], | |
| [0, 0, 0, 1, 0, 0, 0], | |
| [0, 0, 0, 1, 0, 0, 0], | |
| [0, 0, 0, 0, 0, 0, 0], | |
| [0, 0, 0, 0, 0, 0, 0]], dtype=uint8) | |
| """ | |
| # check parameters | |
| if n_iter is None: | |
| n = -1 | |
| elif n_iter <= 0: | |
| raise ValueError('n_iter must be > 0') | |
| else: | |
| n = n_iter | |
| # check that we have a 2d binary image, and convert it | |
| # to uint8 | |
| skel = np.array(image).astype(np.uint8) | |
| if skel.ndim != 2: | |
| raise ValueError('2D array required') | |
| if not np.all(np.in1d(image.flat,(0,1))): | |
| raise ValueError('Image contains values other than 0 and 1') | |
| # neighborhood mask | |
| mask = np.array([[ 8, 4, 2], | |
| [16, 0, 1], | |
| [32, 64,128]],dtype=np.uint8) | |
| # iterate either 1) indefinitely or 2) up to iteration limit | |
| while n != 0: | |
| before = np.sum(skel) # count points before thinning | |
| # for each subiteration | |
| for lut in [G123_LUT, G123P_LUT]: | |
| # correlate image with neighborhood mask | |
| N = ndi.correlate(skel, mask, mode='constant') | |
| # take deletion decision from this subiteration's LUT | |
| D = np.take(lut, N) | |
| # perform deletion | |
| skel[D] = 0 | |
| after = np.sum(skel) # coint points after thinning | |
| if before == after: | |
| # iteration had no effect: finish | |
| break | |
| # count down to iteration limit (or endlessly negative) | |
| n -= 1 | |
| return skel.astype(np.bool_) | |
| """ | |
| # here's how to make the LUTs | |
| def nabe(n): | |
| return np.array([n>>i&1 for i in range(0,9)]).astype(np.bool_) | |
| def hood(n): | |
| return np.take(nabe(n), np.array([[3, 2, 1], | |
| [4, 8, 0], | |
| [5, 6, 7]])) | |
| def G1(n): | |
| s = 0 | |
| bits = nabe(n) | |
| for i in (0,2,4,6): | |
| if not(bits[i]) and (bits[i+1] or bits[(i+2) % 8]): | |
| s += 1 | |
| return s==1 | |
| g1_lut = np.array([G1(n) for n in range(256)]) | |
| def G2(n): | |
| n1, n2 = 0, 0 | |
| bits = nabe(n) | |
| for k in (1,3,5,7): | |
| if bits[k] or bits[k-1]: | |
| n1 += 1 | |
| if bits[k] or bits[(k+1) % 8]: | |
| n2 += 1 | |
| return min(n1,n2) in [2,3] | |
| g2_lut = np.array([G2(n) for n in range(256)]) | |
| g12_lut = g1_lut & g2_lut | |
| def G3(n): | |
| bits = nabe(n) | |
| return not((bits[1] or bits[2] or not(bits[7])) and bits[0]) | |
| def G3p(n): | |
| bits = nabe(n) | |
| return not((bits[5] or bits[6] or not(bits[3])) and bits[4]) | |
| g3_lut = np.array([G3(n) for n in range(256)]) | |
| g3p_lut = np.array([G3p(n) for n in range(256)]) | |
| g123_lut = g12_lut & g3_lut | |
| g123p_lut = g12_lut & g3p_lut | |
| """ | |
| """ | |
| author : Peb Ruswono Aryan | |
| metric for evaluating binarization algorithms | |
| implemented : | |
| * F-Measure | |
| * pseudo F-Measure (as in H-DIBCO 2010 & 2012) | |
| * Peak Signal to Noise Ratio (PSNR) | |
| * Negative Rate Measure (NRM) | |
| * Misclassification Penaltiy Measure (MPM) | |
| * Distance Reciprocal Distortion (DRD) | |
| usage: | |
| python metric.py test-image.png ground-truth-image.png | |
| """ | |
| def drd_fn(im, im_gt): | |
| height, width = im.shape | |
| neg = np.zeros(im.shape) | |
| neg[im_gt!=im] = 1 | |
| y, x = np.unravel_index(np.flatnonzero(neg), im.shape) | |
| n = 2 | |
| m = n*2+1 | |
| W = np.zeros((m,m), dtype=np.uint8) | |
| W[n,n] = 1. | |
| W = cv2.distanceTransform(1-W, cv2.DIST_L2, cv2.DIST_MASK_PRECISE) | |
| W[n,n] = 1. | |
| W = 1./W | |
| W[n,n] = 0. | |
| W /= W.sum() | |
| nubn = 0. | |
| block_size = 8 | |
| for y1 in range(0, height, block_size): | |
| for x1 in range(0, width, block_size): | |
| y2 = min(y1+block_size-1,height-1) | |
| x2 = min(x1+block_size-1,width-1) | |
| block_dim = (x2-x1+1)*(y1-y1+1) | |
| block = 1-im_gt[y1:y2, x1:x2] | |
| block_sum = np.sum(block) | |
| if block_sum>0 and block_sum<block_dim: | |
| nubn += 1 | |
| drd_sum= 0. | |
| tmp = np.zeros(W.shape) | |
| for i in range(min(1,len(y))): | |
| tmp[:,:] = 0 | |
| x1 = max(0, x[i]-n) | |
| y1 = max(0, y[i]-n) | |
| x2 = min(width-1, x[i]+n) | |
| y2 = min(height-1, y[i]+n) | |
| yy1 = y1-y[i]+n | |
| yy2 = y2-y[i]+n | |
| xx1 = x1-x[i]+n | |
| xx2 = x2-x[i]+n | |
| tmp[yy1:yy2+1,xx1:xx2+1] = np.abs(im[y[i],x[i]]-im_gt[y1:y2+1,x1:x2+1]) | |
| tmp *= W | |
| drd_sum += np.sum(tmp) | |
| return drd_sum/nubn | |
| def bin_metric(im,im_gt): | |
| height, width = im.shape | |
| npixel = height*width | |
| im[im>0] = 1 | |
| gt_mask = im_gt==0 | |
| im_gt[im_gt>0] = 1 | |
| sk = bwmorph(1-im_gt) | |
| im_sk = np.ones(im_gt.shape) | |
| im_sk[sk] = 0 | |
| kernel = np.ones((3,3), dtype=np.uint8) | |
| im_dil = cv2.erode(im_gt, kernel) | |
| im_gtb = im_gt-im_dil | |
| im_gtbd = cv2.distanceTransform(1-im_gtb, cv2.DIST_L2, 3) | |
| nd = im_gtbd.sum() | |
| ptp = np.zeros(im_gt.shape) | |
| ptp[(im==0) & (im_sk==0)] = 1 | |
| numptp = ptp.sum() | |
| tp = np.zeros(im_gt.shape) | |
| tp[(im==0) & (im_gt==0)] = 1 | |
| numtp = tp.sum() | |
| tn = np.zeros(im_gt.shape) | |
| tn[(im==1) & (im_gt==1)] = 1 | |
| numtn = tn.sum() | |
| fp = np.zeros(im_gt.shape) | |
| fp[(im==0) & (im_gt==1)] = 1 | |
| numfp = fp.sum() | |
| fn = np.zeros(im_gt.shape) | |
| fn[(im==1) & (im_gt==0)] = 1 | |
| numfn = fn.sum() | |
| precision = numtp / (numtp + numfp) | |
| recall = numtp / (numtp + numfn) | |
| precall = numptp / np.sum(1-im_sk) | |
| fmeasure = (2*recall*precision)/(recall+precision) | |
| pfmeasure = (2*precall*precision)/(precall+precision) | |
| mse = (numfp+numfn)/npixel | |
| psnr = 10.*np.log10(1./mse) | |
| nrfn = numfn / (numfn + numtp) | |
| nrfp = numfp / (numfp + numtn) | |
| nrm = (nrfn + nrfp)/2 | |
| im_dn = im_gtbd.copy() | |
| im_dn[fn==0] = 0 | |
| dn = np.sum(im_dn) | |
| mpfn = dn / nd | |
| im_dp = im_gtbd.copy() | |
| im_dp[fp==0] = 0 | |
| dp = np.sum(im_dp) | |
| mpfp = dp / nd | |
| mpm = (mpfp + mpfn) / 2 | |
| drd = drd_fn(im, im_gt) | |
| return fmeasure, pfmeasure,psnr,nrm, mpm,drd | |
| # print("F-measure\t: {0}\npF-measure\t: {1}\nPSNR\t\t: {2}\nNRM\t\t: {3}\nMPM\t\t: {4}\nDRD\t\t: {5}".format(fmeasure, pfmeasure, psnr, nrm, mpm, drd)) | 
