import base64 import colorsys import re import socket import traceback from enum import Enum from functools import reduce import requests from PIL import ImageDraw, ImageFont, Image from keras import backend as K import numpy as np import cv2 def compose(*funcs): """Compose arbitrarily many functions, evaluated left to right. Reference: https://mathieularose.com/function-composition-in-python/ """ if funcs: return reduce(lambda f, g: lambda *a, **kw: g(f(*a, **kw)), funcs) else: raise ValueError('Composition of empty sequence not supported.') def box_iou(b1, b2): """Return iou tensor Parameters ---------- b1: tensor, shape=(i1,...,iN, 4), xywh b2: tensor, shape=(j, 4), xywh Returns ------- iou: tensor, shape=(i1,...,iN, j) """ # Expand dim to apply broadcasting. b1 = K.expand_dims(b1, -2) b1_xy = b1[..., :2] b1_wh = b1[..., 2:4] b1_wh_half = b1_wh/2. b1_mins = b1_xy - b1_wh_half b1_maxes = b1_xy + b1_wh_half # Expand dim to apply broadcasting. b2 = K.expand_dims(b2, 0) b2_xy = b2[..., :2] b2_wh = b2[..., 2:4] b2_wh_half = b2_wh/2. b2_mins = b2_xy - b2_wh_half b2_maxes = b2_xy + b2_wh_half intersect_mins = K.maximum(b1_mins, b2_mins) intersect_maxes = K.minimum(b1_maxes, b2_maxes) intersect_wh = K.maximum(intersect_maxes - intersect_mins, 0.) intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1] b1_area = b1_wh[..., 0] * b1_wh[..., 1] b2_area = b2_wh[..., 0] * b2_wh[..., 1] iou = intersect_area / (b1_area + b2_area - intersect_area) return iou def get_classes(classes_path): """loads the classes""" with open(classes_path) as f: class_names = f.readlines() class_names = [c.strip() for c in class_names] return class_names def get_anchors(anchors_path): """loads the anchors from a file""" with open(anchors_path) as f: anchors = f.readline() anchors = [float(x) for x in anchors.split(',')] return np.array(anchors).reshape(-1, 2) def get_colors(number, bright=True): """ Generate random colors for drawing bounding boxes. To get visually distinct colors, generate them in HSV space then convert to RGB. """ if number <= 0: return [] brightness = 1.0 if bright else 0.7 hsv_tuples = [(x / number, 1., brightness) for x in range(number)] colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples)) colors = list( map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), colors)) np.random.seed(10101) # Fixed seed for consistent colors across runs. np.random.shuffle(colors) # Shuffle colors to decorrelate adjacent classes. np.random.seed(None) # Reset seed to default. return colors labelType = Enum('labelType', ('LABEL_TOP_OUTSIDE', 'LABEL_BOTTOM_OUTSIDE', 'LABEL_TOP_INSIDE', 'LABEL_BOTTOM_INSIDE',)) def draw_boxes(image, out_boxes, out_classes, out_scores, class_names, colors): font = ImageFont.truetype(font='yolo_data/FiraMono-Medium.otf', size=np.floor(3e-2 * image.size[1] + 0.5).astype('int32')) thickness = (image.size[0] + image.size[1]) // 300 box_list = [] for i, c in reversed(list(enumerate(out_classes))): predicted_class = class_names[c] box = out_boxes[i] score = out_scores[i] label = '{} {:.2f}'.format(predicted_class, score) draw = ImageDraw.Draw(image) label_size = draw.textsize(label, font) top, left, bottom, right = box top = max(0, np.floor(top + 0.5).astype('int32')) left = max(0, np.floor(left + 0.5).astype('int32')) bottom = min(image.size[1], np.floor(bottom + 0.5).astype('int32')) right = min(image.size[0], np.floor(right + 0.5).astype('int32')) # print(label, (left, top), (right, bottom)) box_list.append([(left, top), (right, bottom)]) if top - label_size[1] >= 0: text_origin = np.array([left, top - label_size[1]]) else: text_origin = np.array([left, top + 1]) # My kingdom for a good redistributable image drawing library. for i in range(thickness): draw.rectangle( [left + i, top + i, right - i, bottom - i], outline=colors[c]) draw.rectangle( [tuple(text_origin), tuple(text_origin + label_size)], fill=colors[c]) draw.text(text_origin, label, fill=(0, 0, 0), font=font) del draw return image, box_list def draw_label(image, text, color, coords, label_type=labelType.LABEL_TOP_OUTSIDE): font = cv2.FONT_HERSHEY_PLAIN font_scale = 1. (text_width, text_height) = cv2.getTextSize(text, font, fontScale=font_scale, thickness=1)[0] padding = 5 rect_height = text_height + padding * 2 rect_width = text_width + padding * 2 (x, y) = coords if label_type == labelType.LABEL_TOP_OUTSIDE or label_type == labelType.LABEL_BOTTOM_INSIDE: cv2.rectangle(image, (x, y), (x + rect_width, y - rect_height), color, cv2.FILLED) cv2.putText(image, text, (x + padding, y - text_height + padding), font, fontScale=font_scale, color=(255, 255, 255), lineType=cv2.LINE_AA) else: # LABEL_BOTTOM_OUTSIDE or LABEL_TOP_INSIDE cv2.rectangle(image, (x, y), (x + rect_width, y + rect_height), color, cv2.FILLED) cv2.putText(image, text, (x + padding, y + text_height + padding), font, fontScale=font_scale, color=(255, 255, 255), lineType=cv2.LINE_AA) return image def pil_resize(image_np, height, width): image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)) image_pil = image_pil.resize((int(width), int(height)), Image.BICUBIC) image_np = cv2.cvtColor(np.asarray(image_pil), cv2.COLOR_RGB2BGR) return image_np def pil_resize_a(image_np, height, width): image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGRA2RGBA)) image_pil = image_pil.resize((int(width), int(height)), Image.BICUBIC) image_np = cv2.cvtColor(np.asarray(image_pil), cv2.COLOR_RGBA2BGRA) return image_np def np2pil(image_np): image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)) return image_pil def pil2np(image_pil): image_np = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGB2BGR) return image_np def np2pil_a(image_np): image_pil = Image.fromarray(cv2.cvtColor(image_np, cv2.COLOR_BGRA2RGBA)) return image_pil def pil2np_a(image_pil): image_np = cv2.cvtColor(np.array(image_pil), cv2.COLOR_RGBA2BGRA) return image_np def pil_rotate(image_np, angle, fill_color=(255, 255, 255)): image_pil = np2pil(image_np) image_rotate = image_pil.rotate(angle, expand=False, fillcolor=fill_color) image_np = pil2np(image_rotate) return image_np def pil_rotate_a(image_np, angle): image_pil = np2pil_a(image_np) image_rotate = image_pil.rotate(angle, expand=False, fillcolor=(255, 255, 255)) image_np = pil2np_a(image_rotate) return image_np def request_post(url, param, time_out=1000, use_zlib=False): fails = 0 text = None while True: try: if fails >= 1: break headers = {'content-type': 'application/json'} # result = requests.post(url, data=param, timeout=time_out) session = requests.Session() result = session.post(url, data=param, timeout=time_out) if result.status_code == 200: text = result.text break else: print('result.status_code', result.status_code) print('result.text', result.text) fails += 1 continue except socket.timeout: fails += 1 print('timeout! fail times:', fails) except: fails += 1 print('fail! fail times:', fails) traceback.print_exc() return text def np2bytes(image_np): # numpy转为可序列化的string success, img_encode = cv2.imencode(".jpg", image_np) # numpy -> bytes img_bytes = img_encode.tobytes() return img_bytes def bytes2np(_b): try: # 二进制数据流转np.ndarray [np.uint8: 8位像素] image_np = cv2.imdecode(np.frombuffer(_b, np.uint8), cv2.IMREAD_COLOR) # 将rgb转为bgr # image_np = cv2.cvtColor(image_np, cv2.COLOR_RGB2BGR) return image_np except cv2.error as e: if "src.empty()" in str(e): print("bytes2np image is empty!") return None except: traceback.print_exc() return None def base64_decode(b64, is_test=False): # b64 = re.sub(r"\r\n", "\n", b64) missing_padding = 4 - len(b64) % 4 if missing_padding: b64 += '=' * missing_padding if is_test: return base64.b64decode(b64), b64 return base64.b64decode(b64)