Esempio n. 1
0
    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
Esempio n. 2
0
        def to_labelmes_advance():
            m = 50  # 匹配run时,上文关联的文字长度,越长越严格

            # 1 将带有下划线的run对象,使用特殊的hash规则存储起来
            content = []  # 使用已遍历到的文本内容作为hash值
            elements = {}
            for p in self.paragraphs:
                for r in p.runs:
                    # 要去掉空格,不然可能对不上。试过strip不行。分段会不太一样,还是把所有空格删了靠谱。
                    content.append(re.sub(r'\s+', '', r.text))
                    if r.underline or is_color(
                            r.font.color.rgb):  # 对有下划线、颜色的对象进行存储
                        # print(r.text + ',', r.underline, r.font.color.rgb, ''.join(content))
                        etag = get_etag(
                            ''.join(content)[-m:])  # 全部字符都判断的话太严格了,所以减小区间~
                        elements[etag] = r

            # 2 检查json标注中为span的,其哈希规则是否有对应,则要单独设置扩展属性
            content = ''
            for i, file in enumerate(imfiles):
                page = doc.load_page(i)
                lmdict = LabelmeDict.gen_data(file)
                lmdict['shapes'] = page.get_labelme_shapes('dict',
                                                           views=views,
                                                           scale=scale)

                for sp in lmdict['shapes']:
                    attrs = DictTool.json_loads(sp['label'], 'label')
                    if attrs['category_name'] == 'span':
                        content += re.sub(r'\s+', '', attrs['text'])
                        etag = get_etag(content[-m:])
                        # print(content)
                        if etag in elements:
                            # print(content)
                            r = elements[etag]  # 对应的原run对象
                            attrs = DictTool.json_loads(sp['label'])
                            x = r.underline
                            if x:
                                attrs['underline'] = int(x)
                            x = r.font.color.rgb
                            if is_color(x):
                                attrs['color'] = list(x)
                            sp['label'] = json.dumps(attrs)
                file.with_suffix('.json').write(lmdict, indent=indent)
Esempio n. 3
0
    def parse_shape(cls, shape, image=None):
        """ 解析一个shape的数据为dict字典 """
        # 1 解析原label为字典
        attrs = DictTool.json_loads(shape['label'], 'label')
        attrs.update(DictTool.sub(shape, ['label']))

        # 2 中心点 center
        shape_type = shape['shape_type']
        pts = shape['points']
        if shape_type in ('rectangle', 'polygon', 'line'):
            attrs['center'] = np.array(np.array(pts).mean(axis=0),
                                       dtype=int).tolist()
        elif shape_type == 'circle':
            attrs['center'] = pts[0]

        # 3 外接矩形 rect
        if shape_type in ('rectangle', 'polygon'):
            attrs['ltrb'] = np.array(pts, dtype=int).reshape(-1).tolist()
        elif shape_type == 'circle':
            x, y = pts[0]
            r = ((x - pts[1][0])**2 + (y - pts[1][1])**2)**0.5
            attrs['ltrb'] = [
                round_int(v) for v in [x - r, y - r, x + r, y + r]
            ]

        # 4 图片数据 img, etag
        if image is not None and attrs['ltrb']:
            attrs['img'] = xlcv.get_sub(image, attrs['ltrb'])
            # attrs['etag'] = get_etag(attrs['img'])
            # TODO 这里目的其实就是让相同图片对比尽量相似,所以可以用dhash而不是etag

        # 5 中心点像素值 pixel
        p = attrs['center']
        if image is not None and p:
            attrs['pixel'] = tuple(image[p[1], p[0]].tolist()[::-1])

        # if 'rect' in attrs:
        #     del attrs['rect']  # 旧版的格式数据,删除

        return attrs
Esempio n. 4
0
    def to_coco_gt_dict(self, categories=None):
        """ 将labelme转成 coco gt 标注的格式

        分两种大情况
        1、一种是raw原始数据转labelme标注后,首次转coco格式,这种编号等相关数据都可以重新生成
            raw_data --可视化--> labelme --转存--> coco
        2、还有种原来就是coco,转labelme修改标注后,又要再转回coco,这种应该尽量保存原始值
            coco --> labelme --手动修改--> labelme' --> coco'
            这种在coco转labelme时,会做一些特殊标记,方便后续转回coco
        3、 1, 2两种情况是可以连在一起,然后形成 labelme 和 coco 之间的多次互转的

        :param categories: 类别
            默认只设一个类别 {'id': 0, 'name': 'text', 'supercategory'}
            支持自定义,所有annotations的category_id
        :return: gt_dict
            注意,如果对文件顺序、ann顺序有需求的,请先自行操作self.data数据后,再调用该to_coco函数
            对image_id、annotation_id有需求的,需要使用CocoData进一步操作
        """
        from pyxllib.data.coco import CocoGtData

        if not categories:
            if 'categories' in self.extdata:
                # coco 转过来的labelme,存储有原始的 categories
                categories = self.extdata['categories']
            else:
                categories = [{'id': 0, 'name': 'text', 'supercategory': ''}]

        # 1 第一轮遍历:结构处理 jsonfile, lmdict --> data(image, shapes)
        img_id, ann_id, data = 0, 0, []
        for jsonfile, lmdict in self.rp2data.items():
            # 1.0 升级为字典类型
            lmdict = LabelmeDict.update_labelattr(lmdict, points=True)

            for sp in lmdict['shapes']:  # label转成字典
                sp['label'] = json.loads(sp['label'])

            # 1.1 找shapes里的image
            image = None
            # 1.1.1 xltype='image'
            for sp in filter(lambda x: x.get('xltype', None) == 'image',
                             lmdict['shapes']):
                image = DictTool.json_loads(sp['label'])
                if not image:
                    raise ValueError(sp['label'])
                # TODO 删除 coco_eval 等字段?
                del image['xltype']
                break
            # 1.1.2 shapes里没有图像级标注则生成一个
            if image is None:
                # TODO file_name 加上相对路径?
                image = CocoGtData.gen_image(-1, lmdict['imagePath'],
                                             lmdict['imageHeight'],
                                             lmdict['imageWidth'])
            img_id = max(img_id, image.get('id', -1))

            # 1.2 遍历shapes
            shapes = []
            for sp in lmdict['shapes']:
                label = sp['label']
                if 'xltype' not in label:
                    # 普通的标注框
                    d = sp['label'].copy()
                    # DictTool.isub_(d, '')
                    ann_id = max(ann_id, d.get('id', -1))
                    shapes.append(d)
                elif label['xltype'] == 'image':
                    # image,图像级标注数据;之前已经处理了,这里可以跳过
                    pass
                elif label['xltype'] == 'seg':
                    # seg,衍生的分割标注框,在转回coco时可以丢弃
                    pass
                else:
                    raise ValueError
            data.append([image, shapes])

        # 2 第二轮遍历:处理id等问题
        images, annotations = [], []
        for image, shapes in data:
            # 2.1 image
            if image.get('id', -1) == -1:
                img_id += 1
                image['id'] = img_id
            images.append(image)

            # 2.2 annotations
            for sp in shapes:
                sp['image_id'] = img_id
                if sp.get('id', -1) == -1:
                    ann_id += 1
                    sp['id'] = ann_id
                # 如果没有框类别,会默认设置一个。 (强烈建议外部业务功能代码自行设置好category_id)
                if 'category_id' not in sp:
                    sp['category_id'] = categories[0]['id']
                DictTool.isub(sp, ['category_name'])
                ann = CocoGtData.gen_annotation(**sp)
                annotations.append(ann)

        # 3 result
        gt_dict = CocoGtData.gen_gt_dict(images, annotations, categories)
        return gt_dict