def update_labelattr(cls, lmdict, *, points=False, inplace=True): """ :param points: 是否更新labelattr中的points、bbox等几何信息 并且在无任何几何信息的情况下,增设points """ if not inplace: lmdict = copy.deepcopy(lmdict) for shape in lmdict['shapes']: # 1 属性字典,至少先初始化一个label属性 labelattr = DictTool.json_loads(shape['label'], 'label') # 2 填充其他扩展属性值 keys = set(shape.keys()) stdkeys = set('label,points,group_id,shape_type,flags'.split(',')) for k in (keys - stdkeys): labelattr[k] = shape[k] del shape[k] # 要删除原有的扩展字段值 # 3 处理points等几何信息 if points: if 'bbox' in labelattr: labelattr['bbox'] = ltrb2xywh(rect_bounds(shape['points'])) else: labelattr['points'] = shape['points'] # + 写回shape shape['label'] = json.dumps(labelattr, ensure_ascii=False) return lmdict
def gen_annotation(cls, **kwargs): """ 智能地生成一个annotation字典 这个略微有点过度封装了 但没事,先放着,可以不拿出来用~~ :param points: 必须是n*2的结构 """ a = kwargs.copy() # a = {'id': 0, 'area': 0, 'bbox': [0, 0, 0, 0], # 'category_id': 1, 'image_id': 0, 'iscrowd': 0, 'segmentation': []} if 'points' in a: # points是一个特殊参数,使用“一个”多边形来标注(注意区别segmentation是多个多边形) if 'segmentation' not in a: a['segmentation'] = [cls.points2segmentation(a['points'])] del a['points'] if 'bbox' not in a: pts = [] for seg in a['segmentation']: pts += seg a['bbox'] = ltrb2xywh(rect_bounds(pts)) if 'area' not in a: # 自动计算面积 a['area'] = int(a['bbox'][2] * a['bbox'][3]) for k in ['id', 'image_id']: if k not in a: a[k] = 0 if 'category_id' not in a: a['category_id'] = 1 return a
def _get_subrect_image(im, pts, fill=0): """ :return: dst_img 按外接四边形截取的子图 new_pts 新的变换后的点坐标 """ # 1 计算需要pad的宽度 x1, y1, x2, y2 = [round_int(v) for v in rect_bounds(pts)] h, w = im.shape[:2] pad = [-y1, y2 - h, -x1, x2 - w] # 各个维度要补充的宽度 pad = [max(0, v) for v in pad] # 负数宽度不用补充,改为0 # 2 pad并定位rect局部图 tmp_img = xlcv.pad(im, pad, fill) if max(pad) > 0 else im dst_img = tmp_img[y1 + pad[0]:y2, x1 + pad[2]:x2] # 这里越界不会报错,只是越界的那个维度shape为0 new_pts = [(pt[0] - x1, pt[1] - y1) for pt in pts] return dst_img, new_pts
def gen_quad_annotations(cls, file, *, image_id, start_box_id, category_id=1, **kwargs): """ 解析一张图片对应的txt标注文件 :param file: 标注文件,有多行标注 每行是x1,y1,x2,y2,x3,y3,x4,y4[,label] (label可以不存在) :param image_id: 该图片id :param start_box_id: box_id起始编号 :param category_id: 归属类别 """ lines = File(file).read() box_id = start_box_id annotations = [] for line in lines.splitlines(): vals = line.split(',', maxsplit=8) if len(vals) < 2: continue attrs = { 'id': box_id, 'image_id': image_id, 'category_id': category_id } if len(vals) == 9: attrs['label'] = vals[8] # print(vals) seg = [int(v) for v in vals[:8]] attrs['segmentation'] = [seg] attrs['bbox'] = ltrb2xywh(rect_bounds(seg)) if kwargs: attrs.update(kwargs) annotations.append(cls.gen_annotation(**attrs)) box_id += 1 return annotations
def warp(im, warp_mat, dsize=None, *, view_rate=False, max_zoom=1, reserve_struct=False): """ 对图像进行透视变换 :param im: np.ndarray的图像数据 TODO 支持PIL.Image格式? :param warp_mat: 变换矩阵 :param dsize: 目标图片尺寸 没有任何输入时,同原图 如果有指定,则会决定最终的图片大小 如果使用了view_rate、max_zoom,会改变变换矩阵所展示的内容 :param view_rate: 视野比例,默认不开启,当输入非0正数时,几个数值功能效果如下 1,关注原图四个角点位置在变换后的位置,确保新的4个点依然在目标图中 为了达到该效果,会增加【平移】变换,以及自动控制dsize 2,将原图依中心面积放到至2倍,记录新的4个角点变换后的位置,确保变换后的4个点依然在目标图中 0.5,同理,只是只关注原图局部的一半位置 :param max_zoom: 默认1倍,当设置时(只在开启view_rate时有用),会增加【缩小】变换,限制view_rate扩展的上限 :param reserve_struct: 是否保留原来im的数据类型返回,默认True 关掉该功能可以提高性能,此时返回结果统一为 np 矩阵 :return: 见 reserve_struct """ from math import sqrt # 1 得到3*3的变换矩阵 warp_mat = np.array(warp_mat) if warp_mat.shape[0] == 2: warp_mat = np.concatenate([warp_mat, [[0, 0, 1]]], axis=0) # 2 view_rate,视野比例改变导致的变换矩阵规则变化 if view_rate: # 2.1 视野变化后的四个角点 h, w = im.shape[:2] y, x = h / 2, w / 2 # 图片中心点坐标 h1, w1 = view_rate * h / 2, view_rate * w / 2 l, t, r, b = [-w1 + x, -h1 + y, w1 + x, h1 + y] pts1 = np.array([[l, t], [r, t], [r, b], [l, b]]) # 2.2 变换后角点位置产生的外接矩形 left, top, right, bottom = rect_bounds(warp_points(pts1, warp_mat)) # 2.3 增加平移变换确保左上角在原点 warp_mat = np.dot([[1, 0, -left], [0, 1, -top], [0, 0, 1]], warp_mat) # 2.4 控制面积变化率 h2, w2 = (bottom - top, right - left) if max_zoom: rate = w2 * h2 / w / h # 目标面积比原面积 if rate > max_zoom: r = 1 / sqrt(rate / max_zoom) warp_mat = np.dot([[r, 0, 0], [0, r, 0], [0, 0, 1]], warp_mat) h2, w2 = round(h2 * r), round(w2 * r) if not dsize: dsize = (w2, h2) # 3 标准操作,不做额外处理,按照原图默认的图片尺寸展示 if dsize is None: dsize = (im.shape[1], im.shape[0]) dst = cv2.warpPerspective(im, warp_mat, dsize) # 4 返回值 return dst