init + inference.py патч
This commit is contained in:
464
utils.py
Normal file
464
utils.py
Normal file
@ -0,0 +1,464 @@
|
||||
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))
|
||||
Reference in New Issue
Block a user