# Copyright (c) <2015-Present> Tzutalin # Copyright (C) 2013 MIT, Computer Science and Artificial Intelligence Laboratory. Bryan Russell, Antonio Torralba, # William T. Freeman. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and # associated documentation files (the "Software"), to deal in the Software without restriction, including without # limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the # Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all copies or substantial portions of # the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT # NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT # SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import hashlib import os import re import sys from math import sqrt import cv2 import numpy as np from PyQt5.QtCore import QRegExp, QT_VERSION_STR from PyQt5.QtGui import QIcon, QRegExpValidator, QColor from PyQt5.QtWidgets import QPushButton, QAction, QMenu from libs.ustr import ustr __dir__ = os.path.dirname(os.path.abspath(__file__)) # 获取本程序文件路径 __iconpath__ = os.path.abspath(os.path.join(__dir__, '../resources/icons')) def newIcon(icon, iconSize=None): if iconSize is not None: return QIcon(QIcon(__iconpath__ + "/" + icon + ".png").pixmap(iconSize, iconSize)) else: return QIcon(__iconpath__ + "/" + icon + ".png") def newButton(text, icon=None, slot=None): b = QPushButton(text) if icon is not None: b.setIcon(newIcon(icon)) if slot is not None: b.clicked.connect(slot) return b def newAction(parent, text, slot=None, shortcut=None, icon=None, tip=None, checkable=False, enabled=True, iconSize=None): """Create a new action and assign callbacks, shortcuts, etc.""" a = QAction(text, parent) if icon is not None: if iconSize is not None: a.setIcon(newIcon(icon, iconSize)) else: a.setIcon(newIcon(icon)) if shortcut is not None: if isinstance(shortcut, (list, tuple)): a.setShortcuts(shortcut) else: a.setShortcut(shortcut) if tip is not None: a.setToolTip(tip) a.setStatusTip(tip) if slot is not None: a.triggered.connect(slot) if checkable: a.setCheckable(True) a.setEnabled(enabled) return a def addActions(widget, actions): for action in actions: if action is None: widget.addSeparator() elif isinstance(action, QMenu): widget.addMenu(action) else: widget.addAction(action) def labelValidator(): return QRegExpValidator(QRegExp(r'^[^ \t].+'), None) class struct(object): def __init__(self, **kwargs): self.__dict__.update(kwargs) def distance(p): return sqrt(p.x() * p.x() + p.y() * p.y()) def fmtShortcut(text): mod, key = text.split('+', 1) return '%s+%s' % (mod, key) def generateColorByText(text): s = ustr(text) hashCode = int(hashlib.sha256(s.encode('utf-8')).hexdigest(), 16) r = int((hashCode / 255) % 255) g = int((hashCode / 65025) % 255) b = int((hashCode / 16581375) % 255) return QColor(r, g, b, 100) def have_qstring(): '''p3/qt5 get rid of QString wrapper as py3 has native unicode str type''' return not (sys.version_info.major >= 3 or QT_VERSION_STR.startswith('5.')) def natural_sort(list, key=lambda s: s): """ Sort the list into natural alphanumeric order. """ def get_alphanum_key_func(key): convert = lambda text: int(text) if text.isdigit() else text return lambda s: [convert(c) for c in re.split('([0-9]+)', key(s))] sort_key = get_alphanum_key_func(key) list.sort(key=sort_key) def get_rotate_crop_image(img, points): # Use Green's theory to judge clockwise or counterclockwise # author: biyanhua d = 0.0 for index in range(-1, 3): d += -0.5 * (points[index + 1][1] + points[index][1]) * ( points[index + 1][0] - points[index][0]) if d < 0: # counterclockwise tmp = np.array(points) points[1], points[3] = tmp[3], tmp[1] try: img_crop_width = int( max( np.linalg.norm(points[0] - points[1]), np.linalg.norm(points[2] - points[3]))) img_crop_height = int( max( np.linalg.norm(points[0] - points[3]), np.linalg.norm(points[1] - points[2]))) pts_std = np.float32([[0, 0], [img_crop_width, 0], [img_crop_width, img_crop_height], [0, img_crop_height]]) M = cv2.getPerspectiveTransform(points, pts_std) dst_img = cv2.warpPerspective( img, M, (img_crop_width, img_crop_height), borderMode=cv2.BORDER_REPLICATE, flags=cv2.INTER_CUBIC) dst_img_height, dst_img_width = dst_img.shape[0:2] if dst_img_height * 1.0 / dst_img_width >= 1.5: dst_img = np.rot90(dst_img) return dst_img except Exception as e: print(e) def boxPad(box, imgShape, pad : int) -> np.array: """ Pad a box with [pad] pixels on each side. """ box = np.array(box, dtype=np.int32) box[0][0], box[0][1] = box[0][0] - pad, box[0][1] - pad box[1][0], box[1][1] = box[1][0] + pad, box[1][1] - pad box[2][0], box[2][1] = box[2][0] + pad, box[2][1] + pad box[3][0], box[3][1] = box[3][0] - pad, box[3][1] + pad h, w, _ = imgShape box[:,0] = np.clip(box[:,0], 0, w) box[:,1] = np.clip(box[:,1], 0, h) return box def OBB2HBB(obb) -> np.array: """ Convert Oriented Bounding Box to Horizontal Bounding Box. """ hbb = np.zeros(4, dtype=np.int32) hbb[0] = min(obb[:, 0]) hbb[1] = min(obb[:, 1]) hbb[2] = max(obb[:, 0]) hbb[3] = max(obb[:, 1]) return hbb def expand_list(merged, html_list): ''' Fill blanks according to merged cells ''' sr, er, sc, ec = merged for i in range(sr, er): for j in range(sc, ec): html_list[i][j] = None html_list[sr][sc] = '' if ec - sc > 1: html_list[sr][sc] += " colspan={}".format(ec - sc) if er - sr > 1: html_list[sr][sc] += " rowspan={}".format(er - sr) return html_list def convert_token(html_list): ''' Convert raw html to label format ''' token_list = ["
"] # final html list: for row in html_list: token_list.append("