text: #!/usr/bin/env python3
  # -*- coding: utf-8 -*-
  """
  Created on Thu Sep 9 23:11:51 2020
  utils
  @author: chineseocr
  """
  import cv2
  import numpy as np
  
  def nms_box(boxes, scores, score_threshold=0.5, nms_threshold=0.3):
      ##nms box
      boxes = np.array(boxes)
      scores = np.array(scores)
      ind = scores>score_threshold
      boxes = boxes[ind]
      scores =scores[ind]
      def box_to_center(box):
          xmin,ymin,xmax,ymax = [round(float(x),4) for x in box]
          w = xmax-xmin
          h = ymax-ymin
          return [round(xmin,4),round(ymin,4),round(w,4),round(h,4)]
      
      newBoxes = [ box_to_center(box) for box in boxes]
      newscores = [ round(float(x),6) for x in scores]
      
      index = cv2.dnn.NMSBoxes(newBoxes, newscores, score_threshold=score_threshold, nms_threshold=nms_threshold)
      if len(index)>0:
         index = index.reshape((-1,))
         return boxes[index],scores[index]
      else:
          return np.array([]),np.array([])
      
  from scipy.ndimage import filters,interpolation
  from numpy import amin, amax
  def resize_im(im, scale, max_scale=None):
      f=float(scale)/min(im.shape[0], im.shape[1])
      if max_scale!=None and f*max(im.shape[0], im.shape[1])>max_scale:
          f=float(max_scale)/max(im.shape[0], im.shape[1])
      return cv2.resize(im, (0, 0), fx=f, fy=f)
  
  def estimate_skew_angle(raw,angleRange=[-15,15]):
      """
      估计图像文字偏转角度,
      angleRange:角度估计区间
      """
      raw = resize_im(raw, scale=600, max_scale=900)
      image = raw-amin(raw)
      image = image/amax(image)
      m = interpolation.zoom(image,0.5)
      m = filters.percentile_filter(m,80,size=(20,2))
      m = filters.percentile_filter(m,80,size=(2,20))
      m = interpolation.zoom(m,1.0/0.5)
      #w,h = image.shape[1],image.shape[0]
      w,h = min(image.shape[1],m.shape[1]),min(image.shape[0],m.shape[0])
      flat = np.clip(image[:h,:w]-m[:h,:w]+1,0,1)
      d0,d1 = flat.shape
      o0,o1 = int(0.1*d0),int(0.1*d1)
      flat = amax(flat)-flat
      flat -= amin(flat)
      est = flat[o0:d0-o0,o1:d1-o1]
      angles = range(angleRange[0],angleRange[1])
      estimates = []
      for a in angles:
          
          roest =interpolation.rotate(est,a,order=0,mode='constant')
          v = np.mean(roest,axis=1)
          v = np.var(v)
          estimates.append((v,a))
      
      _,a = max(estimates)
      return a
  
  
  def eval_angle(img,angleRange=[-5,5]):
      """
      估计图片文字的偏移角度
      """
      im = Image.fromarray(img)
      degree = estimate_skew_angle(np.array(im.convert('L')),angleRange=angleRange)
      im = im.rotate(degree,center=(im.size[0]/2,im.size[1]/2),expand=1,fillcolor=(255,255,255))
      img = np.array(im)
      return img,degree
  
      
  def letterbox_image(image, size,fillValue=[128,128,128]):
      '''
      resize image with unchanged aspect ratio using padding
      '''
      image_h, image_w = image.shape[:2]
      w, h = size
      new_w = int(image_w * min(w*1.0/image_w, h*1.0/image_h))
      new_h = int(image_h * min(w*1.0/image_w, h*1.0/image_h))
      
      resized_image = cv2.resize(image,(new_w,new_h),interpolation=cv2.INTER_CUBIC)
      #cv2.imwrite('tmp/test.png', resized_image[...,::-1])
      if fillValue is None:
         fillValue = [int(x.mean()) for x in cv2.split(np.array(image))]
      boxed_image = np.zeros((size[1],size[0],3),dtype=np.uint8)
      boxed_image[:] = fillValue
      boxed_image[:new_h,:new_w,:] =resized_image 
  
      return boxed_image,new_w/image_w,new_h/image_h
  
  
  from skimage import measure
  def get_table_line(binimg,axis=0,lineW=10):
      ##获取表格线
      ##axis=0 横线
      ##axis=1 竖线
      labels=measure.label(binimg>0,connectivity=2)  #8连通区域标记
      regions = measure.regionprops(labels)
      if axis==1:
         lineboxes = [ minAreaRect(line.coords) for line in regions if line.bbox[2]-line.bbox[0]>lineW ]
      else:
          lineboxes = [ minAreaRect(line.coords) for line in regions if line.bbox[3]-line.bbox[1]>lineW ]
      return lineboxes
  
  
  def sqrt(p1,p2):
          return np.sqrt((p1[0]-p2[0])**2+(p1[1]-p2[1])**2)
      
  def adjust_lines(RowsLines,ColsLines,alph=50):
      ##调整line
  
      
      nrow = len(RowsLines)
      ncol = len(ColsLines)
      newRowsLines =[]
      newColsLines =[]
      for i in range(nrow):
          
          x1,y1,x2,y2 = RowsLines[i]
          cx1,cy1 = (x1+x2)/2,(y1+y2)/2
          for j in range(nrow):
              if i!=j:
                  x3,y3,x4,y4 = RowsLines[j]
                  cx2,cy2 = (x3+x4)/2,(y3+y4)/2
                  if  (x3<cx1<x4 or y3<cy1<y4 ) or ( x1<cx2<x2 or y1<cy2<y2):
                      continue
                  else:
                      r = sqrt((x1,y1),(x3,y3))
                      if r<alph:
                          newRowsLines.append([x1,y1,x3,y3])
                      r = sqrt((x1,y1),(x4,y4))
                      if r<alph:
                          newRowsLines.append([x1,y1,x4,y4])
                      
                      r = sqrt((x2,y2),(x3,y3))
                      if r<alph:
                          newRowsLines.append([x2,y2,x3,y3])
                      r = sqrt((x2,y2),(x4,y4))
                      if r<alph:
                          newRowsLines.append([x2,y2,x4,y4])
                          
                          
      for i in range(ncol):
          x1,y1,x2,y2 = ColsLines[i]
          cx1,cy1 = (x1+x2)/2,(y1+y2)/2
          for j in range(ncol):
              if i!=j:
                  x3,y3,x4,y4 = ColsLines[j]
                  cx2,cy2 = (x3+x4)/2,(y3+y4)/2
                  if  (x3<cx1<x4 or y3<cy1<y4 ) or ( x1<cx2<x2 or y1<cy2<y2):
                      continue
                  else:
                      r = sqrt((x1,y1),(x3,y3))
                      if r<alph:
                          newColsLines.append([x1,y1,x3,y3])
                      r = sqrt((x1,y1),(x4,y4))
                      if r<alph:
                          newColsLines.append([x1,y1,x4,y4])
                      
                      r = sqrt((x2,y2),(x3,y3))
                      if r<alph:
                          newColsLines.append([x2,y2,x3,y3])
                      r = sqrt((x2,y2),(x4,y4))
                      if r<alph:
                          newColsLines.append([x2,y2,x4,y4])
                          
      return newRowsLines,newColsLines
  
  
  def minAreaRect(coords):
      """
      多边形外接矩形
      """
      rect=cv2.minAreaRect(coords[:,::-1])
      box = cv2.boxPoints(rect)
      box = box.reshape((8,)).tolist()
  
      box = image_location_sort_box(box)
      
      x1,y1,x2,y2,x3,y3,x4,y4=box
      degree,w,h,cx,cy = solve(box)
      if w<h:
          xmin =(x1+x2)/2
          xmax = (x3+x4)/2
          ymin = (y1+y2)/2
          ymax = (y3+y4)/2
          
      else:
          xmin =(x1+x4)/2
          xmax = (x2+x3)/2
          ymin = (y1+y4)/2
          ymax = (y2+y3)/2
      #degree,w,h,cx,cy = solve(box)
      #x1,y1,x2,y2,x3,y3,x4,y4 = box
      #return {'degree':degree,'w':w,'h':h,'cx':cx,'cy':cy}
      return [xmin,ymin,xmax,ymax]
  
  
  def fit_line(p1,p2):
      """A = Y2 - Y1
         B = X1 - X2
         C = X2*Y1 - X1*Y2
         AX+BY+C=0
      直线一般方程
      """
      x1,y1 = p1
      x2,y2 = p2
      A = y2-y1
      B = x1-x2
      C = x2*y1-x1*y2
      return A,B,C
  
  def point_line_cor(p,A,B,C):
      ##判断点与之间的位置关系
      #一般式直线方程(Ax+By+c)=0
      x,y = p
      r = A*x+B*y+C
      return r
  
  def line_to_line(points1,points2,alpha=10):
      """
      线段之间的距离
      """
      x1,y1,x2,y2 = points1
      ox1,oy1,ox2,oy2 = points2
      A1,B1,C1 = fit_line((x1,y1),(x2,y2))
      A2,B2,C2 = fit_line((ox1,oy1),(ox2,oy2))
      flag1 = point_line_cor([x1,y1],A2,B2,C2)
      flag2 = point_line_cor([x2,y2],A2,B2,C2)
      
      if (flag1>0 and flag2>0) or (flag1<0 and flag2<0):
          
          x =  (B1*C2-B2*C1)/(A1*B2-A2*B1)
          y =  (A2*C1-A1*C2)/(A1*B2-A2*B1)
          p =  (x,y)
          r0 = sqrt(p,(x1,y1))
          r1 = sqrt(p,(x2,y2))
          
          if min(r0,r1)<alpha:
              
              if r0<r1:
                   points1 = [p[0],p[1],x2,y2]
              else:
                   points1 = [x1,y1,p[0],p[1]]
                   
      return points1
  
  from scipy.spatial import distance as dist
  def _order_points(pts):
      # 根据x坐标对点进行排序
      """
      ---------------------
      作者：Tong_T
      来源：CSDN
      原文：https://blog.csdn.net/Tong_T/article/details/81907132
      版权声明：本文为博主原创文章，转载请附上博文链接！
      """
      x_sorted = pts[np.argsort(pts[:, 0]), :]
  
      left_most = x_sorted[:2, :]
      right_most = x_sorted[2:, :]
      left_most = left_most[np.argsort(left_most[:, 1]), :]
      (tl, bl) = left_most
  
      distance = dist.cdist(tl[np.newaxis], right_most, "euclidean")[0]
      (br, tr) = right_most[np.argsort(distance)[::-1], :]
  
      return np.array([tl, tr, br, bl], dtype="float32")
  
  def image_location_sort_box(box):
      x1,y1,x2,y2,x3,y3,x4,y4 = box[:8]
      pts = (x1,y1),(x2,y2),(x3,y3),(x4,y4)
      pts = np.array(pts, dtype="float32")
      (x1,y1),(x2,y2),(x3,y3),(x4,y4) = _order_points(pts)
      return [x1,y1,x2,y2,x3,y3,x4,y4]
  
  def solve(box):
       """
       绕 cx,cy点 w,h 旋转 angle 的坐标
       x = cx-w/2
       y = cy-h/2
       x1-cx = -w/2*cos(angle) +h/2*sin(angle)
       y1 -cy= -w/2*sin(angle) -h/2*cos(angle)
       
       h(x1-cx) = -wh/2*cos(angle) +hh/2*sin(angle)
       w(y1 -cy)= -ww/2*sin(angle) -hw/2*cos(angle)
       (hh+ww)/2sin(angle) = h(x1-cx)-w(y1 -cy)
  
       """
       x1,y1,x2,y2,x3,y3,x4,y4= box[:8]
       cx = (x1+x3+x2+x4)/4.0
       cy = (y1+y3+y4+y2)/4.0
       w = (np.sqrt((x2-x1)**2+(y2-y1)**2)+np.sqrt((x3-x4)**2+(y3-y4)**2))/2
       h = (np.sqrt((x2-x3)**2+(y2-y3)**2)+np.sqrt((x1-x4)**2+(y1-y4)**2))/2
       #x = cx-w/2
       #y = cy-h/2
       sinA = (h*(x1-cx)-w*(y1 -cy))*1.0/(h*h+w*w)*2
       angle = np.arcsin(sinA)
       return angle,w,h,cx,cy
  
  def xy_rotate_box(cx,cy,w,h,angle=0,degree=None,**args):
      """
      绕 cx,cy点 w,h 旋转 angle 的坐标
      x_new = (x-cx)*cos(angle) - (y-cy)*sin(angle)+cx
      y_new = (x-cx)*sin(angle) + (y-cy)*sin(angle)+cy
      """
      if degree is not None:
          angle = degree
      cx    = float(cx)
      cy    = float(cy)
      w     = float(w)
      h     = float(h)
      angle = float(angle)
      x1,y1 = rotate(cx-w/2,cy-h/2,angle,cx,cy)
      x2,y2 = rotate(cx+w/2,cy-h/2,angle,cx,cy)
      x3,y3 = rotate(cx+w/2,cy+h/2,angle,cx,cy)
      x4,y4 = rotate(cx-w/2,cy+h/2,angle,cx,cy)
      return x1,y1,x2,y2,x3,y3,x4,y4
   
  from numpy import cos,sin
  def rotate(x,y,angle,cx,cy):
      angle = angle#*pi/180
      x_new = (x-cx)*cos(angle) - (y-cy)*sin(angle)+cx
      y_new = (x-cx)*sin(angle) + (y-cy)*cos(angle)+cy
      return x_new,y_new
  
  def minAreaRectbox(regions,flag=True,W=0,H=0,filtersmall=False,adjustBox=False):
      """
      多边形外接矩形
      """
      boxes = []
      for region in regions:
          rect=cv2.minAreaRect(region.coords[:,::-1])
  
          box = cv2.boxPoints(rect)
          box = box.reshape((8,)).tolist()
          box = image_location_sort_box(box)
          x1,y1,x2,y2,x3,y3,x4,y4=box
          angle,w,h,cx,cy = solve(box)
          if adjustBox:
             x1,y1,x2,y2,x3,y3,x4,y4 =xy_rotate_box(cx,cy,w+5,h+5,angle=0,degree=None)
          
          if w>32 and h>32 and flag :
              if  abs(angle/np.pi*180)<20:
                 if filtersmall and w<10 or h<10:
                     continue
                 boxes.append([x1,y1,x2,y2,x3,y3,x4,y4])
          else:
               if w*h<0.5*W*H:
                  if filtersmall and w<8 or h<8:
                     continue
                  boxes.append([x1,y1,x2,y2,x3,y3,x4,y4])
      return boxes
  
  
  from PIL import Image
  def rectangle(img,boxes):
      tmp = np.copy(img)
      for box in boxes:
          xmin,ymin,xmax,ymax = box[:4]
          cv2.rectangle(tmp,(int(xmin),int(ymin)),(int(xmax),int(ymax)),(0,255,0),1,lineType=cv2.LINE_AA)
      return Image.fromarray(tmp)
  
  
  def draw_lines(im, bboxes,color=(0,0,0),lineW=3):
      """
          boxes: bounding boxes
      """
      tmp = np.copy(im)
      c = color
      h, w = im.shape[:2]
     
      for box in bboxes:
          x1,y1,x2,y2= box[:4]
          cv2.line(tmp,(int(x1),int(y1)),(int(x2),int(y2)),c,lineW,lineType=cv2.LINE_AA)
   
  
      return tmp
  
  def draw_boxes(im, bboxes,color=(0,0,0)):
      """
          boxes: bounding boxes
      """
      tmp = np.copy(im)
      c = color
      h, w, _ = im.shape
  
      for box in bboxes:
          if type(box) is dict:
              x1,y1,x2,y2,x3,y3,x4,y4 = xy_rotate_box(**box)
          else:
             x1,y1,x2,y2,x3,y3,x4,y4 = box[:8]
  
          cv2.line(tmp,(int(x1),int(y1)),(int(x2),int(y2)),c,1,lineType=cv2.LINE_AA)
          cv2.line(tmp,(int(x2),int(y2)),(int(x3),int(y3)),c,1,lineType=cv2.LINE_AA)
          cv2.line(tmp,(int(x3),int(y3)),(int(x4),int(y4)),c,1,lineType=cv2.LINE_AA)
          cv2.line(tmp,(int(x4),int(y4)),(int(x1),int(y1)),c,1,lineType=cv2.LINE_AA)
  
      return tmp