Browse Source

Format code with black

Kentaro Wada 4 years ago
parent
commit
5c9808446a
39 changed files with 1147 additions and 941 deletions
  1. 1 1
      .flake8
  2. 13 6
      .github/workflows/ci.yml
  3. 37 35
      examples/bbox_detection/labelme2voc.py
  4. 51 55
      examples/instance_segmentation/labelme2coco.py
  5. 38 35
      examples/instance_segmentation/labelme2voc.py
  6. 29 28
      examples/semantic_segmentation/labelme2voc.py
  7. 9 9
      examples/tutorial/load_label_png.py
  8. 6 6
      labelme/__init__.py
  9. 71 69
      labelme/__main__.py
  10. 374 262
      labelme/app.py
  11. 6 6
      labelme/cli/draw_json.py
  12. 6 5
      labelme/cli/draw_label_png.py
  13. 27 23
      labelme/cli/json_to_dataset.py
  14. 36 38
      labelme/cli/on_docker.py
  15. 19 19
      labelme/config/__init__.py
  16. 48 47
      labelme/label_file.py
  17. 12 15
      labelme/logger.py
  18. 36 20
      labelme/shape.py
  19. 10 10
      labelme/testing.py
  20. 5 5
      labelme/utils/_io.py
  21. 4 4
      labelme/utils/image.py
  22. 17 8
      labelme/utils/qt.py
  23. 25 24
      labelme/utils/shape.py
  24. 10 10
      labelme/widgets/brightness_contrast_dialog.py
  25. 78 48
      labelme/widgets/canvas.py
  26. 4 3
      labelme/widgets/color_dialog.py
  27. 0 1
      labelme/widgets/escapable_qlist_widget.py
  28. 31 24
      labelme/widgets/label_dialog.py
  29. 0 1
      labelme/widgets/tool_bar.py
  30. 4 4
      labelme/widgets/unique_label_qlist_widget.py
  31. 2 3
      labelme/widgets/zoom_widget.py
  32. 10 0
      pyproject.toml
  33. 64 54
      setup.py
  34. 8 8
      tests/docs_tests/man_tests/test_labelme_1.py
  35. 20 20
      tests/labelme_tests/test_app.py
  36. 3 3
      tests/labelme_tests/utils_tests/test_image.py
  37. 6 5
      tests/labelme_tests/utils_tests/test_shape.py
  38. 7 7
      tests/labelme_tests/utils_tests/util.py
  39. 20 20
      tests/labelme_tests/widgets_tests/test_label_dialog.py

+ 1 - 1
.flake8

@@ -1,3 +1,3 @@
 [flake8]
 [flake8]
 exclude = .anaconda3/*
 exclude = .anaconda3/*
-ignore = W504
+ignore = E203, E741, W503, W504

+ 13 - 6
.github/workflows/ci.yml

@@ -56,7 +56,7 @@ jobs:
       env:
       env:
         PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
         PYTHON_VERSION: ${{ matrix.PYTHON_VERSION }}
       run: |
       run: |
-        conda install -y python=$PYTHON_VERSION
+        conda install -q -y python=$PYTHON_VERSION
         which python
         which python
         python --version
         python --version
         pip --version
         pip --version
@@ -66,17 +66,17 @@ jobs:
       run: |
       run: |
         if [ "${{ matrix.PYTEST_QT_API }}" = "pyside2" ]; then
         if [ "${{ matrix.PYTEST_QT_API }}" = "pyside2" ]; then
           if [ "${{ matrix.PYTHON_VERSION }}" = "2.7" ]; then
           if [ "${{ matrix.PYTHON_VERSION }}" = "2.7" ]; then
-            conda install -y 'pyside2!=5.12.4' -c conda-forge
+            conda install -q -y 'pyside2!=5.12.4' -c conda-forge
           else
           else
-            conda install -y pyside2 -c conda-forge
+            conda install -q -y pyside2 -c conda-forge
           fi
           fi
         elif [ "${{ matrix.PYTEST_QT_API }}" = "pyqt4v2" ]; then
         elif [ "${{ matrix.PYTEST_QT_API }}" = "pyqt4v2" ]; then
-          conda install -y pyqt=4 -c conda-forge
+          conda install -q -y pyqt=4 -c conda-forge
         else  # pyqt5
         else  # pyqt5
-          conda install -y pyqt=5
+          conda install -q -y pyqt=5
         fi
         fi
         if [ "${{ matrix.os }}" != "windows-latest" ]; then
         if [ "${{ matrix.os }}" != "windows-latest" ]; then
-          conda install -y help2man
+          conda install -q -y help2man
         fi
         fi
         pip install hacking pytest pytest-qt
         pip install hacking pytest pytest-qt
 
 
@@ -91,6 +91,13 @@ jobs:
       run: |
       run: |
         flake8 .
         flake8 .
 
 
+    - name: Black
+      shell: bash -l {0}
+      if: matrix.os != 'windows-latest' && matrix.python-version != '2.7'
+      run: |
+        pip install black
+        black --check .
+
     - name: Test with pytest
     - name: Test with pytest
       shell: bash -l {0}
       shell: bash -l {0}
       if: matrix.os != 'windows-latest'
       if: matrix.os != 'windows-latest'

+ 37 - 35
examples/bbox_detection/labelme2voc.py

@@ -10,11 +10,12 @@ import sys
 
 
 import imgviz
 import imgviz
 import labelme
 import labelme
+
 try:
 try:
     import lxml.builder
     import lxml.builder
     import lxml.etree
     import lxml.etree
 except ImportError:
 except ImportError:
-    print('Please install lxml:\n\n    pip install lxml\n')
+    print("Please install lxml:\n\n    pip install lxml\n")
     sys.exit(1)
     sys.exit(1)
 
 
 
 
@@ -22,23 +23,23 @@ def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
     )
     )
-    parser.add_argument('input_dir', help='input annotated directory')
-    parser.add_argument('output_dir', help='output dataset directory')
-    parser.add_argument('--labels', help='labels file', required=True)
+    parser.add_argument("input_dir", help="input annotated directory")
+    parser.add_argument("output_dir", help="output dataset directory")
+    parser.add_argument("--labels", help="labels file", required=True)
     parser.add_argument(
     parser.add_argument(
-        '--noviz', help='no visualization', action='store_true'
+        "--noviz", help="no visualization", action="store_true"
     )
     )
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     if osp.exists(args.output_dir):
     if osp.exists(args.output_dir):
-        print('Output directory already exists:', args.output_dir)
+        print("Output directory already exists:", args.output_dir)
         sys.exit(1)
         sys.exit(1)
     os.makedirs(args.output_dir)
     os.makedirs(args.output_dir)
-    os.makedirs(osp.join(args.output_dir, 'JPEGImages'))
-    os.makedirs(osp.join(args.output_dir, 'Annotations'))
+    os.makedirs(osp.join(args.output_dir, "JPEGImages"))
+    os.makedirs(osp.join(args.output_dir, "Annotations"))
     if not args.noviz:
     if not args.noviz:
-        os.makedirs(osp.join(args.output_dir, 'AnnotationsVisualization'))
-    print('Creating dataset:', args.output_dir)
+        os.makedirs(osp.join(args.output_dir, "AnnotationsVisualization"))
+    print("Creating dataset:", args.output_dir)
 
 
     class_names = []
     class_names = []
     class_name_to_id = {}
     class_name_to_id = {}
@@ -47,31 +48,30 @@ def main():
         class_name = line.strip()
         class_name = line.strip()
         class_name_to_id[class_name] = class_id
         class_name_to_id[class_name] = class_id
         if class_id == -1:
         if class_id == -1:
-            assert class_name == '__ignore__'
+            assert class_name == "__ignore__"
             continue
             continue
         elif class_id == 0:
         elif class_id == 0:
-            assert class_name == '_background_'
+            assert class_name == "_background_"
         class_names.append(class_name)
         class_names.append(class_name)
     class_names = tuple(class_names)
     class_names = tuple(class_names)
-    print('class_names:', class_names)
-    out_class_names_file = osp.join(args.output_dir, 'class_names.txt')
-    with open(out_class_names_file, 'w') as f:
-        f.writelines('\n'.join(class_names))
-    print('Saved class_names:', out_class_names_file)
+    print("class_names:", class_names)
+    out_class_names_file = osp.join(args.output_dir, "class_names.txt")
+    with open(out_class_names_file, "w") as f:
+        f.writelines("\n".join(class_names))
+    print("Saved class_names:", out_class_names_file)
 
 
-    for filename in glob.glob(osp.join(args.input_dir, '*.json')):
-        print('Generating dataset from:', filename)
+    for filename in glob.glob(osp.join(args.input_dir, "*.json")):
+        print("Generating dataset from:", filename)
 
 
         label_file = labelme.LabelFile(filename=filename)
         label_file = labelme.LabelFile(filename=filename)
 
 
         base = osp.splitext(osp.basename(filename))[0]
         base = osp.splitext(osp.basename(filename))[0]
-        out_img_file = osp.join(
-            args.output_dir, 'JPEGImages', base + '.jpg')
-        out_xml_file = osp.join(
-            args.output_dir, 'Annotations', base + '.xml')
+        out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")
+        out_xml_file = osp.join(args.output_dir, "Annotations", base + ".xml")
         if not args.noviz:
         if not args.noviz:
             out_viz_file = osp.join(
             out_viz_file = osp.join(
-                args.output_dir, 'AnnotationsVisualization', base + '.jpg')
+                args.output_dir, "AnnotationsVisualization", base + ".jpg"
+            )
 
 
         img = labelme.utils.img_data_to_arr(label_file.imageData)
         img = labelme.utils.img_data_to_arr(label_file.imageData)
         imgviz.io.imsave(out_img_file, img)
         imgviz.io.imsave(out_img_file, img)
@@ -79,10 +79,10 @@ def main():
         maker = lxml.builder.ElementMaker()
         maker = lxml.builder.ElementMaker()
         xml = maker.annotation(
         xml = maker.annotation(
             maker.folder(),
             maker.folder(),
-            maker.filename(base + '.jpg'),
-            maker.database(),    # e.g., The VOC2007 Database
+            maker.filename(base + ".jpg"),
+            maker.database(),  # e.g., The VOC2007 Database
             maker.annotation(),  # e.g., Pascal VOC2007
             maker.annotation(),  # e.g., Pascal VOC2007
-            maker.image(),       # e.g., flickr
+            maker.image(),  # e.g., flickr
             maker.size(
             maker.size(
                 maker.height(str(img.shape[0])),
                 maker.height(str(img.shape[0])),
                 maker.width(str(img.shape[1])),
                 maker.width(str(img.shape[1])),
@@ -94,15 +94,17 @@ def main():
         bboxes = []
         bboxes = []
         labels = []
         labels = []
         for shape in label_file.shapes:
         for shape in label_file.shapes:
-            if shape['shape_type'] != 'rectangle':
-                print('Skipping shape: label={label}, shape_type={shape_type}'
-                      .format(**shape))
+            if shape["shape_type"] != "rectangle":
+                print(
+                    "Skipping shape: label={label}, "
+                    "shape_type={shape_type}".format(**shape)
+                )
                 continue
                 continue
 
 
-            class_name = shape['label']
+            class_name = shape["label"]
             class_id = class_names.index(class_name)
             class_id = class_names.index(class_name)
 
 
-            (xmin, ymin), (xmax, ymax) = shape['points']
+            (xmin, ymin), (xmax, ymax) = shape["points"]
             # swap if min is larger than max.
             # swap if min is larger than max.
             xmin, xmax = sorted([xmin, xmax])
             xmin, xmax = sorted([xmin, xmax])
             ymin, ymax = sorted([ymin, ymax])
             ymin, ymax = sorted([ymin, ymax])
@@ -112,7 +114,7 @@ def main():
 
 
             xml.append(
             xml.append(
                 maker.object(
                 maker.object(
-                    maker.name(shape['label']),
+                    maker.name(shape["label"]),
                     maker.pose(),
                     maker.pose(),
                     maker.truncated(),
                     maker.truncated(),
                     maker.difficult(),
                     maker.difficult(),
@@ -136,9 +138,9 @@ def main():
             )
             )
             imgviz.io.imsave(out_viz_file, viz)
             imgviz.io.imsave(out_viz_file, viz)
 
 
-        with open(out_xml_file, 'wb') as f:
+        with open(out_xml_file, "wb") as f:
             f.write(lxml.etree.tostring(xml, pretty_print=True))
             f.write(lxml.etree.tostring(xml, pretty_print=True))
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 51 - 55
examples/instance_segmentation/labelme2coco.py

@@ -18,7 +18,7 @@ import labelme
 try:
 try:
     import pycocotools.mask
     import pycocotools.mask
 except ImportError:
 except ImportError:
-    print('Please install pycocotools:\n\n    pip install pycocotools\n')
+    print("Please install pycocotools:\n\n    pip install pycocotools\n")
     sys.exit(1)
     sys.exit(1)
 
 
 
 
@@ -26,17 +26,17 @@ def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
     )
     )
-    parser.add_argument('input_dir', help='input annotated directory')
-    parser.add_argument('output_dir', help='output dataset directory')
-    parser.add_argument('--labels', help='labels file', required=True)
+    parser.add_argument("input_dir", help="input annotated directory")
+    parser.add_argument("output_dir", help="output dataset directory")
+    parser.add_argument("--labels", help="labels file", required=True)
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     if osp.exists(args.output_dir):
     if osp.exists(args.output_dir):
-        print('Output directory already exists:', args.output_dir)
+        print("Output directory already exists:", args.output_dir)
         sys.exit(1)
         sys.exit(1)
     os.makedirs(args.output_dir)
     os.makedirs(args.output_dir)
-    os.makedirs(osp.join(args.output_dir, 'JPEGImages'))
-    print('Creating dataset:', args.output_dir)
+    os.makedirs(osp.join(args.output_dir, "JPEGImages"))
+    print("Creating dataset:", args.output_dir)
 
 
     now = datetime.datetime.now()
     now = datetime.datetime.now()
 
 
@@ -47,17 +47,13 @@ def main():
             version=None,
             version=None,
             year=now.year,
             year=now.year,
             contributor=None,
             contributor=None,
-            date_created=now.strftime('%Y-%m-%d %H:%M:%S.%f'),
+            date_created=now.strftime("%Y-%m-%d %H:%M:%S.%f"),
         ),
         ),
-        licenses=[dict(
-            url=None,
-            id=0,
-            name=None,
-        )],
+        licenses=[dict(url=None, id=0, name=None,)],
         images=[
         images=[
             # license, url, file_name, height, width, date_captured, id
             # license, url, file_name, height, width, date_captured, id
         ],
         ],
-        type='instances',
+        type="instances",
         annotations=[
         annotations=[
             # segmentation, area, iscrowd, image_id, bbox, category_id, id
             # segmentation, area, iscrowd, image_id, bbox, category_id, id
         ],
         ],
@@ -71,46 +67,44 @@ def main():
         class_id = i - 1  # starts with -1
         class_id = i - 1  # starts with -1
         class_name = line.strip()
         class_name = line.strip()
         if class_id == -1:
         if class_id == -1:
-            assert class_name == '__ignore__'
+            assert class_name == "__ignore__"
             continue
             continue
         class_name_to_id[class_name] = class_id
         class_name_to_id[class_name] = class_id
-        data['categories'].append(dict(
-            supercategory=None,
-            id=class_id,
-            name=class_name,
-        ))
-
-    out_ann_file = osp.join(args.output_dir, 'annotations.json')
-    label_files = glob.glob(osp.join(args.input_dir, '*.json'))
+        data["categories"].append(
+            dict(supercategory=None, id=class_id, name=class_name,)
+        )
+
+    out_ann_file = osp.join(args.output_dir, "annotations.json")
+    label_files = glob.glob(osp.join(args.input_dir, "*.json"))
     for image_id, filename in enumerate(label_files):
     for image_id, filename in enumerate(label_files):
-        print('Generating dataset from:', filename)
+        print("Generating dataset from:", filename)
 
 
         label_file = labelme.LabelFile(filename=filename)
         label_file = labelme.LabelFile(filename=filename)
 
 
         base = osp.splitext(osp.basename(filename))[0]
         base = osp.splitext(osp.basename(filename))[0]
-        out_img_file = osp.join(
-            args.output_dir, 'JPEGImages', base + '.jpg'
-        )
+        out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")
 
 
         img = labelme.utils.img_data_to_arr(label_file.imageData)
         img = labelme.utils.img_data_to_arr(label_file.imageData)
         PIL.Image.fromarray(img).convert("RGB").save(out_img_file)
         PIL.Image.fromarray(img).convert("RGB").save(out_img_file)
-        data['images'].append(dict(
-            license=0,
-            url=None,
-            file_name=osp.relpath(out_img_file, osp.dirname(out_ann_file)),
-            height=img.shape[0],
-            width=img.shape[1],
-            date_captured=None,
-            id=image_id,
-        ))
-
-        masks = {}                                     # for area
+        data["images"].append(
+            dict(
+                license=0,
+                url=None,
+                file_name=osp.relpath(out_img_file, osp.dirname(out_ann_file)),
+                height=img.shape[0],
+                width=img.shape[1],
+                date_captured=None,
+                id=image_id,
+            )
+        )
+
+        masks = {}  # for area
         segmentations = collections.defaultdict(list)  # for segmentation
         segmentations = collections.defaultdict(list)  # for segmentation
         for shape in label_file.shapes:
         for shape in label_file.shapes:
-            points = shape['points']
-            label = shape['label']
-            group_id = shape.get('group_id')
-            shape_type = shape.get('shape_type', 'polygon')
+            points = shape["points"]
+            label = shape["label"]
+            group_id = shape.get("group_id")
+            shape_type = shape.get("shape_type", "polygon")
             mask = labelme.utils.shape_to_mask(
             mask = labelme.utils.shape_to_mask(
                 img.shape[:2], points, shape_type
                 img.shape[:2], points, shape_type
             )
             )
@@ -125,7 +119,7 @@ def main():
             else:
             else:
                 masks[instance] = mask
                 masks[instance] = mask
 
 
-            if shape_type == 'rectangle':
+            if shape_type == "rectangle":
                 (x1, y1), (x2, y2) = points
                 (x1, y1), (x2, y2) = points
                 x1, x2 = sorted([x1, x2])
                 x1, x2 = sorted([x1, x2])
                 y1, y2 = sorted([y1, y2])
                 y1, y2 = sorted([y1, y2])
@@ -147,19 +141,21 @@ def main():
             area = float(pycocotools.mask.area(mask))
             area = float(pycocotools.mask.area(mask))
             bbox = pycocotools.mask.toBbox(mask).flatten().tolist()
             bbox = pycocotools.mask.toBbox(mask).flatten().tolist()
 
 
-            data['annotations'].append(dict(
-                id=len(data['annotations']),
-                image_id=image_id,
-                category_id=cls_id,
-                segmentation=segmentations[instance],
-                area=area,
-                bbox=bbox,
-                iscrowd=0,
-            ))
-
-    with open(out_ann_file, 'w') as f:
+            data["annotations"].append(
+                dict(
+                    id=len(data["annotations"]),
+                    image_id=image_id,
+                    category_id=cls_id,
+                    segmentation=segmentations[instance],
+                    area=area,
+                    bbox=bbox,
+                    iscrowd=0,
+                )
+            )
+
+    with open(out_ann_file, "w") as f:
         json.dump(data, f)
         json.dump(data, f)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 38 - 35
examples/instance_segmentation/labelme2voc.py

@@ -18,32 +18,32 @@ def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
     )
     )
-    parser.add_argument('input_dir', help='input annotated directory')
-    parser.add_argument('output_dir', help='output dataset directory')
-    parser.add_argument('--labels', help='labels file', required=True)
+    parser.add_argument("input_dir", help="input annotated directory")
+    parser.add_argument("output_dir", help="output dataset directory")
+    parser.add_argument("--labels", help="labels file", required=True)
     parser.add_argument(
     parser.add_argument(
-        '--noviz', help='no visualization', action='store_true'
+        "--noviz", help="no visualization", action="store_true"
     )
     )
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     if osp.exists(args.output_dir):
     if osp.exists(args.output_dir):
-        print('Output directory already exists:', args.output_dir)
+        print("Output directory already exists:", args.output_dir)
         sys.exit(1)
         sys.exit(1)
     os.makedirs(args.output_dir)
     os.makedirs(args.output_dir)
-    os.makedirs(osp.join(args.output_dir, 'JPEGImages'))
-    os.makedirs(osp.join(args.output_dir, 'SegmentationClass'))
-    os.makedirs(osp.join(args.output_dir, 'SegmentationClassPNG'))
+    os.makedirs(osp.join(args.output_dir, "JPEGImages"))
+    os.makedirs(osp.join(args.output_dir, "SegmentationClass"))
+    os.makedirs(osp.join(args.output_dir, "SegmentationClassPNG"))
     if not args.noviz:
     if not args.noviz:
         os.makedirs(
         os.makedirs(
-            osp.join(args.output_dir, 'SegmentationClassVisualization')
+            osp.join(args.output_dir, "SegmentationClassVisualization")
         )
         )
-    os.makedirs(osp.join(args.output_dir, 'SegmentationObject'))
-    os.makedirs(osp.join(args.output_dir, 'SegmentationObjectPNG'))
+    os.makedirs(osp.join(args.output_dir, "SegmentationObject"))
+    os.makedirs(osp.join(args.output_dir, "SegmentationObjectPNG"))
     if not args.noviz:
     if not args.noviz:
         os.makedirs(
         os.makedirs(
-            osp.join(args.output_dir, 'SegmentationObjectVisualization')
+            osp.join(args.output_dir, "SegmentationObjectVisualization")
         )
         )
-    print('Creating dataset:', args.output_dir)
+    print("Creating dataset:", args.output_dir)
 
 
     class_names = []
     class_names = []
     class_name_to_id = {}
     class_name_to_id = {}
@@ -52,45 +52,48 @@ def main():
         class_name = line.strip()
         class_name = line.strip()
         class_name_to_id[class_name] = class_id
         class_name_to_id[class_name] = class_id
         if class_id == -1:
         if class_id == -1:
-            assert class_name == '__ignore__'
+            assert class_name == "__ignore__"
             continue
             continue
         elif class_id == 0:
         elif class_id == 0:
-            assert class_name == '_background_'
+            assert class_name == "_background_"
         class_names.append(class_name)
         class_names.append(class_name)
     class_names = tuple(class_names)
     class_names = tuple(class_names)
-    print('class_names:', class_names)
-    out_class_names_file = osp.join(args.output_dir, 'class_names.txt')
-    with open(out_class_names_file, 'w') as f:
-        f.writelines('\n'.join(class_names))
-    print('Saved class_names:', out_class_names_file)
+    print("class_names:", class_names)
+    out_class_names_file = osp.join(args.output_dir, "class_names.txt")
+    with open(out_class_names_file, "w") as f:
+        f.writelines("\n".join(class_names))
+    print("Saved class_names:", out_class_names_file)
 
 
-    for filename in glob.glob(osp.join(args.input_dir, '*.json')):
-        print('Generating dataset from:', filename)
+    for filename in glob.glob(osp.join(args.input_dir, "*.json")):
+        print("Generating dataset from:", filename)
 
 
         label_file = labelme.LabelFile(filename=filename)
         label_file = labelme.LabelFile(filename=filename)
 
 
         base = osp.splitext(osp.basename(filename))[0]
         base = osp.splitext(osp.basename(filename))[0]
-        out_img_file = osp.join(
-            args.output_dir, 'JPEGImages', base + '.jpg')
+        out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")
         out_cls_file = osp.join(
         out_cls_file = osp.join(
-            args.output_dir, 'SegmentationClass', base + '.npy')
+            args.output_dir, "SegmentationClass", base + ".npy"
+        )
         out_clsp_file = osp.join(
         out_clsp_file = osp.join(
-            args.output_dir, 'SegmentationClassPNG', base + '.png')
+            args.output_dir, "SegmentationClassPNG", base + ".png"
+        )
         if not args.noviz:
         if not args.noviz:
             out_clsv_file = osp.join(
             out_clsv_file = osp.join(
                 args.output_dir,
                 args.output_dir,
-                'SegmentationClassVisualization',
-                base + '.jpg',
+                "SegmentationClassVisualization",
+                base + ".jpg",
             )
             )
         out_ins_file = osp.join(
         out_ins_file = osp.join(
-            args.output_dir, 'SegmentationObject', base + '.npy')
+            args.output_dir, "SegmentationObject", base + ".npy"
+        )
         out_insp_file = osp.join(
         out_insp_file = osp.join(
-            args.output_dir, 'SegmentationObjectPNG', base + '.png')
+            args.output_dir, "SegmentationObjectPNG", base + ".png"
+        )
         if not args.noviz:
         if not args.noviz:
             out_insv_file = osp.join(
             out_insv_file = osp.join(
                 args.output_dir,
                 args.output_dir,
-                'SegmentationObjectVisualization',
-                base + '.jpg',
+                "SegmentationObjectVisualization",
+                base + ".jpg",
             )
             )
 
 
         img = labelme.utils.img_data_to_arr(label_file.imageData)
         img = labelme.utils.img_data_to_arr(label_file.imageData)
@@ -112,7 +115,7 @@ def main():
                 img=imgviz.rgb2gray(img),
                 img=imgviz.rgb2gray(img),
                 label_names=class_names,
                 label_names=class_names,
                 font_size=15,
                 font_size=15,
-                loc='rb',
+                loc="rb",
             )
             )
             imgviz.io.imsave(out_clsv_file, clsv)
             imgviz.io.imsave(out_clsv_file, clsv)
 
 
@@ -127,10 +130,10 @@ def main():
                 img=imgviz.rgb2gray(img),
                 img=imgviz.rgb2gray(img),
                 label_names=instance_names,
                 label_names=instance_names,
                 font_size=15,
                 font_size=15,
-                loc='rb',
+                loc="rb",
             )
             )
             imgviz.io.imsave(out_insv_file, insv)
             imgviz.io.imsave(out_insv_file, insv)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 29 - 28
examples/semantic_segmentation/labelme2voc.py

@@ -18,26 +18,26 @@ def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
         formatter_class=argparse.ArgumentDefaultsHelpFormatter
     )
     )
-    parser.add_argument('input_dir', help='input annotated directory')
-    parser.add_argument('output_dir', help='output dataset directory')
-    parser.add_argument('--labels', help='labels file', required=True)
+    parser.add_argument("input_dir", help="input annotated directory")
+    parser.add_argument("output_dir", help="output dataset directory")
+    parser.add_argument("--labels", help="labels file", required=True)
     parser.add_argument(
     parser.add_argument(
-        '--noviz', help='no visualization', action='store_true'
+        "--noviz", help="no visualization", action="store_true"
     )
     )
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     if osp.exists(args.output_dir):
     if osp.exists(args.output_dir):
-        print('Output directory already exists:', args.output_dir)
+        print("Output directory already exists:", args.output_dir)
         sys.exit(1)
         sys.exit(1)
     os.makedirs(args.output_dir)
     os.makedirs(args.output_dir)
-    os.makedirs(osp.join(args.output_dir, 'JPEGImages'))
-    os.makedirs(osp.join(args.output_dir, 'SegmentationClass'))
-    os.makedirs(osp.join(args.output_dir, 'SegmentationClassPNG'))
+    os.makedirs(osp.join(args.output_dir, "JPEGImages"))
+    os.makedirs(osp.join(args.output_dir, "SegmentationClass"))
+    os.makedirs(osp.join(args.output_dir, "SegmentationClassPNG"))
     if not args.noviz:
     if not args.noviz:
         os.makedirs(
         os.makedirs(
-            osp.join(args.output_dir, 'SegmentationClassVisualization')
+            osp.join(args.output_dir, "SegmentationClassVisualization")
         )
         )
-    print('Creating dataset:', args.output_dir)
+    print("Creating dataset:", args.output_dir)
 
 
     class_names = []
     class_names = []
     class_name_to_id = {}
     class_name_to_id = {}
@@ -46,38 +46,39 @@ def main():
         class_name = line.strip()
         class_name = line.strip()
         class_name_to_id[class_name] = class_id
         class_name_to_id[class_name] = class_id
         if class_id == -1:
         if class_id == -1:
-            assert class_name == '__ignore__'
+            assert class_name == "__ignore__"
             continue
             continue
         elif class_id == 0:
         elif class_id == 0:
-            assert class_name == '_background_'
+            assert class_name == "_background_"
         class_names.append(class_name)
         class_names.append(class_name)
     class_names = tuple(class_names)
     class_names = tuple(class_names)
-    print('class_names:', class_names)
-    out_class_names_file = osp.join(args.output_dir, 'class_names.txt')
-    with open(out_class_names_file, 'w') as f:
-        f.writelines('\n'.join(class_names))
-    print('Saved class_names:', out_class_names_file)
+    print("class_names:", class_names)
+    out_class_names_file = osp.join(args.output_dir, "class_names.txt")
+    with open(out_class_names_file, "w") as f:
+        f.writelines("\n".join(class_names))
+    print("Saved class_names:", out_class_names_file)
 
 
-    for filename in glob.glob(osp.join(args.input_dir, '*.json')):
-        print('Generating dataset from:', filename)
+    for filename in glob.glob(osp.join(args.input_dir, "*.json")):
+        print("Generating dataset from:", filename)
 
 
         label_file = labelme.LabelFile(filename=filename)
         label_file = labelme.LabelFile(filename=filename)
 
 
         base = osp.splitext(osp.basename(filename))[0]
         base = osp.splitext(osp.basename(filename))[0]
-        out_img_file = osp.join(
-            args.output_dir, 'JPEGImages', base + '.jpg')
+        out_img_file = osp.join(args.output_dir, "JPEGImages", base + ".jpg")
         out_lbl_file = osp.join(
         out_lbl_file = osp.join(
-            args.output_dir, 'SegmentationClass', base + '.npy')
+            args.output_dir, "SegmentationClass", base + ".npy"
+        )
         out_png_file = osp.join(
         out_png_file = osp.join(
-            args.output_dir, 'SegmentationClassPNG', base + '.png')
+            args.output_dir, "SegmentationClassPNG", base + ".png"
+        )
         if not args.noviz:
         if not args.noviz:
             out_viz_file = osp.join(
             out_viz_file = osp.join(
                 args.output_dir,
                 args.output_dir,
-                'SegmentationClassVisualization',
-                base + '.jpg',
+                "SegmentationClassVisualization",
+                base + ".jpg",
             )
             )
 
 
-        with open(out_img_file, 'wb') as f:
+        with open(out_img_file, "wb") as f:
             f.write(label_file.imageData)
             f.write(label_file.imageData)
         img = labelme.utils.img_data_to_arr(label_file.imageData)
         img = labelme.utils.img_data_to_arr(label_file.imageData)
 
 
@@ -96,10 +97,10 @@ def main():
                 img=imgviz.rgb2gray(img),
                 img=imgviz.rgb2gray(img),
                 font_size=15,
                 font_size=15,
                 label_names=class_names,
                 label_names=class_names,
-                loc='rb',
+                loc="rb",
             )
             )
             imgviz.io.imsave(out_viz_file, viz)
             imgviz.io.imsave(out_viz_file, viz)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 9 - 9
examples/tutorial/load_label_png.py

@@ -12,26 +12,26 @@ here = osp.dirname(osp.abspath(__file__))
 
 
 
 
 def main():
 def main():
-    label_png = osp.join(here, 'apc2016_obj3_json/label.png')
-    print('Loading:', label_png)
+    label_png = osp.join(here, "apc2016_obj3_json/label.png")
+    print("Loading:", label_png)
     print()
     print()
 
 
     lbl = np.asarray(PIL.Image.open(label_png))
     lbl = np.asarray(PIL.Image.open(label_png))
     labels = np.unique(lbl)
     labels = np.unique(lbl)
 
 
-    label_names_txt = osp.join(here, 'apc2016_obj3_json/label_names.txt')
+    label_names_txt = osp.join(here, "apc2016_obj3_json/label_names.txt")
     label_names = [name.strip() for name in open(label_names_txt)]
     label_names = [name.strip() for name in open(label_names_txt)]
-    print('# of labels:', len(labels))
-    print('# of label_names:', len(label_names))
+    print("# of labels:", len(labels))
+    print("# of label_names:", len(label_names))
     if len(labels) != len(label_names):
     if len(labels) != len(label_names):
-        print('Number of unique labels and label_names must be same.')
+        print("Number of unique labels and label_names must be same.")
         quit(1)
         quit(1)
     print()
     print()
 
 
-    print('label: label_name')
+    print("label: label_name")
     for label, label_name in zip(labels, label_names):
     for label, label_name in zip(labels, label_names):
-        print('%d: %s' % (label, label_name))
+        print("%d: %s" % (label, label_name))
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 6 - 6
labelme/__init__.py

@@ -6,20 +6,20 @@ import sys
 from qtpy import QT_VERSION
 from qtpy import QT_VERSION
 
 
 
 
-__appname__ = 'labelme'
+__appname__ = "labelme"
 
 
 # Semantic Versioning 2.0.0: https://semver.org/
 # Semantic Versioning 2.0.0: https://semver.org/
 # 1. MAJOR version when you make incompatible API changes;
 # 1. MAJOR version when you make incompatible API changes;
 # 2. MINOR version when you add functionality in a backwards-compatible manner;
 # 2. MINOR version when you add functionality in a backwards-compatible manner;
 # 3. PATCH version when you make backwards-compatible bug fixes.
 # 3. PATCH version when you make backwards-compatible bug fixes.
-__version__ = '4.4.0'
+__version__ = "4.4.0"
 
 
-QT4 = QT_VERSION[0] == '4'
-QT5 = QT_VERSION[0] == '5'
+QT4 = QT_VERSION[0] == "4"
+QT5 = QT_VERSION[0] == "5"
 del QT_VERSION
 del QT_VERSION
 
 
-PY2 = sys.version[0] == '2'
-PY3 = sys.version[0] == '3'
+PY2 = sys.version[0] == "2"
+PY3 = sys.version[0] == "3"
 del sys
 del sys
 
 
 from labelme.label_file import LabelFile
 from labelme.label_file import LabelFile

+ 71 - 69
labelme/__main__.py

@@ -20,140 +20,142 @@ from labelme.utils import newIcon
 def main():
 def main():
     parser = argparse.ArgumentParser()
     parser = argparse.ArgumentParser()
     parser.add_argument(
     parser.add_argument(
-        '--version', '-V', action='store_true', help='show version'
+        "--version", "-V", action="store_true", help="show version"
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--reset-config', action='store_true', help='reset qt config'
+        "--reset-config", action="store_true", help="reset qt config"
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--logger-level',
-        default='info',
-        choices=['debug', 'info', 'warning', 'fatal', 'error'],
-        help='logger level',
+        "--logger-level",
+        default="info",
+        choices=["debug", "info", "warning", "fatal", "error"],
+        help="logger level",
     )
     )
-    parser.add_argument('filename', nargs='?', help='image or label filename')
+    parser.add_argument("filename", nargs="?", help="image or label filename")
     parser.add_argument(
     parser.add_argument(
-        '--output',
-        '-O',
-        '-o',
-        help='output file or directory (if it ends with .json it is '
-             'recognized as file, else as directory)'
+        "--output",
+        "-O",
+        "-o",
+        help="output file or directory (if it ends with .json it is "
+        "recognized as file, else as directory)",
     )
     )
-    default_config_file = os.path.join(os.path.expanduser('~'), '.labelmerc')
+    default_config_file = os.path.join(os.path.expanduser("~"), ".labelmerc")
     parser.add_argument(
     parser.add_argument(
-        '--config',
-        dest='config',
-        help='config file or yaml-format string (default: {})'.format(
+        "--config",
+        dest="config",
+        help="config file or yaml-format string (default: {})".format(
             default_config_file
             default_config_file
         ),
         ),
         default=default_config_file,
         default=default_config_file,
     )
     )
     # config for the gui
     # config for the gui
     parser.add_argument(
     parser.add_argument(
-        '--nodata',
-        dest='store_data',
-        action='store_false',
-        help='stop storing image data to JSON file',
+        "--nodata",
+        dest="store_data",
+        action="store_false",
+        help="stop storing image data to JSON file",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--autosave',
-        dest='auto_save',
-        action='store_true',
-        help='auto save',
+        "--autosave",
+        dest="auto_save",
+        action="store_true",
+        help="auto save",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--nosortlabels',
-        dest='sort_labels',
-        action='store_false',
-        help='stop sorting labels',
+        "--nosortlabels",
+        dest="sort_labels",
+        action="store_false",
+        help="stop sorting labels",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--flags',
-        help='comma separated list of flags OR file containing flags',
+        "--flags",
+        help="comma separated list of flags OR file containing flags",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--labelflags',
-        dest='label_flags',
-        help=r'yaml string of label specific flags OR file containing json '
-             r'string of label specific flags (ex. {person-\d+: [male, tall], '
-             r'dog-\d+: [black, brown, white], .*: [occluded]})',  # NOQA
+        "--labelflags",
+        dest="label_flags",
+        help=r"yaml string of label specific flags OR file containing json "
+        r"string of label specific flags (ex. {person-\d+: [male, tall], "
+        r"dog-\d+: [black, brown, white], .*: [occluded]})",  # NOQA
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--labels',
-        help='comma separated list of labels OR file containing labels',
+        "--labels",
+        help="comma separated list of labels OR file containing labels",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--validatelabel',
-        dest='validate_label',
-        choices=['exact'],
-        help='label validation types',
+        "--validatelabel",
+        dest="validate_label",
+        choices=["exact"],
+        help="label validation types",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--keep-prev',
-        action='store_true',
-        help='keep annotation of previous frame',
+        "--keep-prev",
+        action="store_true",
+        help="keep annotation of previous frame",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     parser.add_argument(
     parser.add_argument(
-        '--epsilon',
+        "--epsilon",
         type=float,
         type=float,
-        help='epsilon to find nearest vertex on canvas',
+        help="epsilon to find nearest vertex on canvas",
         default=argparse.SUPPRESS,
         default=argparse.SUPPRESS,
     )
     )
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     if args.version:
     if args.version:
-        print('{0} {1}'.format(__appname__, __version__))
+        print("{0} {1}".format(__appname__, __version__))
         sys.exit(0)
         sys.exit(0)
 
 
     logger.setLevel(getattr(logging, args.logger_level.upper()))
     logger.setLevel(getattr(logging, args.logger_level.upper()))
 
 
-    if hasattr(args, 'flags'):
+    if hasattr(args, "flags"):
         if os.path.isfile(args.flags):
         if os.path.isfile(args.flags):
-            with codecs.open(args.flags, 'r', encoding='utf-8') as f:
+            with codecs.open(args.flags, "r", encoding="utf-8") as f:
                 args.flags = [line.strip() for line in f if line.strip()]
                 args.flags = [line.strip() for line in f if line.strip()]
         else:
         else:
-            args.flags = [line for line in args.flags.split(',') if line]
+            args.flags = [line for line in args.flags.split(",") if line]
 
 
-    if hasattr(args, 'labels'):
+    if hasattr(args, "labels"):
         if os.path.isfile(args.labels):
         if os.path.isfile(args.labels):
-            with codecs.open(args.labels, 'r', encoding='utf-8') as f:
+            with codecs.open(args.labels, "r", encoding="utf-8") as f:
                 args.labels = [line.strip() for line in f if line.strip()]
                 args.labels = [line.strip() for line in f if line.strip()]
         else:
         else:
-            args.labels = [line for line in args.labels.split(',') if line]
+            args.labels = [line for line in args.labels.split(",") if line]
 
 
-    if hasattr(args, 'label_flags'):
+    if hasattr(args, "label_flags"):
         if os.path.isfile(args.label_flags):
         if os.path.isfile(args.label_flags):
-            with codecs.open(args.label_flags, 'r', encoding='utf-8') as f:
+            with codecs.open(args.label_flags, "r", encoding="utf-8") as f:
                 args.label_flags = yaml.safe_load(f)
                 args.label_flags = yaml.safe_load(f)
         else:
         else:
             args.label_flags = yaml.safe_load(args.label_flags)
             args.label_flags = yaml.safe_load(args.label_flags)
 
 
     config_from_args = args.__dict__
     config_from_args = args.__dict__
-    config_from_args.pop('version')
-    reset_config = config_from_args.pop('reset_config')
-    filename = config_from_args.pop('filename')
-    output = config_from_args.pop('output')
-    config_file_or_yaml = config_from_args.pop('config')
+    config_from_args.pop("version")
+    reset_config = config_from_args.pop("reset_config")
+    filename = config_from_args.pop("filename")
+    output = config_from_args.pop("output")
+    config_file_or_yaml = config_from_args.pop("config")
     config = get_config(config_file_or_yaml, config_from_args)
     config = get_config(config_file_or_yaml, config_from_args)
 
 
-    if not config['labels'] and config['validate_label']:
-        logger.error('--labels must be specified with --validatelabel or '
-                     'validate_label: true in the config file '
-                     '(ex. ~/.labelmerc).')
+    if not config["labels"] and config["validate_label"]:
+        logger.error(
+            "--labels must be specified with --validatelabel or "
+            "validate_label: true in the config file "
+            "(ex. ~/.labelmerc)."
+        )
         sys.exit(1)
         sys.exit(1)
 
 
     output_file = None
     output_file = None
     output_dir = None
     output_dir = None
     if output is not None:
     if output is not None:
-        if output.endswith('.json'):
+        if output.endswith(".json"):
             output_file = output
             output_file = output
         else:
         else:
             output_dir = output
             output_dir = output
@@ -161,11 +163,11 @@ def main():
     translator = QtCore.QTranslator()
     translator = QtCore.QTranslator()
     translator.load(
     translator.load(
         QtCore.QLocale.system().name(),
         QtCore.QLocale.system().name(),
-        osp.dirname(osp.abspath(__file__)) + '/translate'
+        osp.dirname(osp.abspath(__file__)) + "/translate",
     )
     )
     app = QtWidgets.QApplication(sys.argv)
     app = QtWidgets.QApplication(sys.argv)
     app.setApplicationName(__appname__)
     app.setApplicationName(__appname__)
-    app.setWindowIcon(newIcon('icon'))
+    app.setWindowIcon(newIcon("icon"))
     app.installTranslator(translator)
     app.installTranslator(translator)
     win = MainWindow(
     win = MainWindow(
         config=config,
         config=config,
@@ -175,7 +177,7 @@ def main():
     )
     )
 
 
     if reset_config:
     if reset_config:
-        logger.info('Resetting Qt config: %s' % win.settings.fileName())
+        logger.info("Resetting Qt config: %s" % win.settings.fileName())
         win.settings.clear()
         win.settings.clear()
         sys.exit(0)
         sys.exit(0)
 
 
@@ -185,5 +187,5 @@ def main():
 
 
 
 
 # this main block is required to generate executable by pyinstaller
 # this main block is required to generate executable by pyinstaller
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

File diff suppressed because it is too large
+ 374 - 262
labelme/app.py


+ 6 - 6
labelme/cli/draw_json.py

@@ -15,15 +15,15 @@ PY2 = sys.version_info[0] == 2
 
 
 def main():
 def main():
     parser = argparse.ArgumentParser()
     parser = argparse.ArgumentParser()
-    parser.add_argument('json_file')
+    parser.add_argument("json_file")
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     label_file = LabelFile(args.json_file)
     label_file = LabelFile(args.json_file)
     img = utils.img_data_to_arr(label_file.imageData)
     img = utils.img_data_to_arr(label_file.imageData)
 
 
-    label_name_to_value = {'_background_': 0}
-    for shape in sorted(label_file.shapes, key=lambda x: x['label']):
-        label_name = shape['label']
+    label_name_to_value = {"_background_": 0}
+    for shape in sorted(label_file.shapes, key=lambda x: x["label"]):
+        label_name = shape["label"]
         if label_name in label_name_to_value:
         if label_name in label_name_to_value:
             label_value = label_name_to_value[label_name]
             label_value = label_name_to_value[label_name]
         else:
         else:
@@ -41,7 +41,7 @@ def main():
         img=imgviz.asgray(img),
         img=imgviz.asgray(img),
         label_names=label_names,
         label_names=label_names,
         font_size=30,
         font_size=30,
-        loc='rb',
+        loc="rb",
     )
     )
 
 
     plt.subplot(121)
     plt.subplot(121)
@@ -51,5 +51,5 @@ def main():
     plt.show()
     plt.show()
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 6 - 5
labelme/cli/draw_label_png.py

@@ -10,19 +10,20 @@ from labelme.logger import logger
 
 
 def main():
 def main():
     parser = argparse.ArgumentParser(
     parser = argparse.ArgumentParser(
-        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
-    parser.add_argument('label_png', help='label PNG file')
+        formatter_class=argparse.ArgumentDefaultsHelpFormatter
+    )
+    parser.add_argument("label_png", help="label PNG file")
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     lbl = np.asarray(PIL.Image.open(args.label_png))
     lbl = np.asarray(PIL.Image.open(args.label_png))
 
 
-    logger.info('label shape: {}'.format(lbl.shape))
-    logger.info('unique label values: {}'.format(np.unique(lbl)))
+    logger.info("label shape: {}".format(lbl.shape))
+    logger.info("unique label values: {}".format(np.unique(lbl)))
 
 
     lbl_viz = imgviz.label2rgb(lbl)
     lbl_viz = imgviz.label2rgb(lbl)
     plt.imshow(lbl_viz)
     plt.imshow(lbl_viz)
     plt.show()
     plt.show()
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 27 - 23
labelme/cli/json_to_dataset.py

@@ -12,20 +12,24 @@ from labelme import utils
 
 
 
 
 def main():
 def main():
-    logger.warning('This script is aimed to demonstrate how to convert the '
-                   'JSON file to a single image dataset.')
-    logger.warning("It won't handle multiple JSON files to generate a "
-                   "real-use dataset.")
+    logger.warning(
+        "This script is aimed to demonstrate how to convert the "
+        "JSON file to a single image dataset."
+    )
+    logger.warning(
+        "It won't handle multiple JSON files to generate a "
+        "real-use dataset."
+    )
 
 
     parser = argparse.ArgumentParser()
     parser = argparse.ArgumentParser()
-    parser.add_argument('json_file')
-    parser.add_argument('-o', '--out', default=None)
+    parser.add_argument("json_file")
+    parser.add_argument("-o", "--out", default=None)
     args = parser.parse_args()
     args = parser.parse_args()
 
 
     json_file = args.json_file
     json_file = args.json_file
 
 
     if args.out is None:
     if args.out is None:
-        out_dir = osp.basename(json_file).replace('.', '_')
+        out_dir = osp.basename(json_file).replace(".", "_")
         out_dir = osp.join(osp.dirname(json_file), out_dir)
         out_dir = osp.join(osp.dirname(json_file), out_dir)
     else:
     else:
         out_dir = args.out
         out_dir = args.out
@@ -33,25 +37,25 @@ def main():
         os.mkdir(out_dir)
         os.mkdir(out_dir)
 
 
     data = json.load(open(json_file))
     data = json.load(open(json_file))
-    imageData = data.get('imageData')
+    imageData = data.get("imageData")
 
 
     if not imageData:
     if not imageData:
-        imagePath = os.path.join(os.path.dirname(json_file), data['imagePath'])
-        with open(imagePath, 'rb') as f:
+        imagePath = os.path.join(os.path.dirname(json_file), data["imagePath"])
+        with open(imagePath, "rb") as f:
             imageData = f.read()
             imageData = f.read()
-            imageData = base64.b64encode(imageData).decode('utf-8')
+            imageData = base64.b64encode(imageData).decode("utf-8")
     img = utils.img_b64_to_arr(imageData)
     img = utils.img_b64_to_arr(imageData)
 
 
-    label_name_to_value = {'_background_': 0}
-    for shape in sorted(data['shapes'], key=lambda x: x['label']):
-        label_name = shape['label']
+    label_name_to_value = {"_background_": 0}
+    for shape in sorted(data["shapes"], key=lambda x: x["label"]):
+        label_name = shape["label"]
         if label_name in label_name_to_value:
         if label_name in label_name_to_value:
             label_value = label_name_to_value[label_name]
             label_value = label_name_to_value[label_name]
         else:
         else:
             label_value = len(label_name_to_value)
             label_value = len(label_name_to_value)
             label_name_to_value[label_name] = label_value
             label_name_to_value[label_name] = label_value
     lbl, _ = utils.shapes_to_label(
     lbl, _ = utils.shapes_to_label(
-        img.shape, data['shapes'], label_name_to_value
+        img.shape, data["shapes"], label_name_to_value
     )
     )
 
 
     label_names = [None] * (max(label_name_to_value.values()) + 1)
     label_names = [None] * (max(label_name_to_value.values()) + 1)
@@ -59,19 +63,19 @@ def main():
         label_names[value] = name
         label_names[value] = name
 
 
     lbl_viz = imgviz.label2rgb(
     lbl_viz = imgviz.label2rgb(
-        label=lbl, img=imgviz.asgray(img), label_names=label_names, loc='rb'
+        label=lbl, img=imgviz.asgray(img), label_names=label_names, loc="rb"
     )
     )
 
 
-    PIL.Image.fromarray(img).save(osp.join(out_dir, 'img.png'))
-    utils.lblsave(osp.join(out_dir, 'label.png'), lbl)
-    PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, 'label_viz.png'))
+    PIL.Image.fromarray(img).save(osp.join(out_dir, "img.png"))
+    utils.lblsave(osp.join(out_dir, "label.png"), lbl)
+    PIL.Image.fromarray(lbl_viz).save(osp.join(out_dir, "label_viz.png"))
 
 
-    with open(osp.join(out_dir, 'label_names.txt'), 'w') as f:
+    with open(osp.join(out_dir, "label_names.txt"), "w") as f:
         for lbl_name in label_names:
         for lbl_name in label_names:
-            f.write(lbl_name + '\n')
+            f.write(lbl_name + "\n")
 
 
-    logger.info('Saved to: {}'.format(out_dir))
+    logger.info("Saved to: {}".format(out_dir))
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 36 - 38
labelme/cli/on_docker.py

@@ -14,57 +14,55 @@ import sys
 
 
 
 
 def get_ip():
 def get_ip():
-    dist = platform.platform().split('-')[0]
-    if dist == 'Linux':
-        return ''
-    elif dist == 'Darwin':
-        cmd = 'ifconfig en0'
+    dist = platform.platform().split("-")[0]
+    if dist == "Linux":
+        return ""
+    elif dist == "Darwin":
+        cmd = "ifconfig en0"
         output = subprocess.check_output(shlex.split(cmd))
         output = subprocess.check_output(shlex.split(cmd))
         if str != bytes:  # Python3
         if str != bytes:  # Python3
-            output = output.decode('utf-8')
+            output = output.decode("utf-8")
         for row in output.splitlines():
         for row in output.splitlines():
-            cols = row.strip().split(' ')
-            if cols[0] == 'inet':
+            cols = row.strip().split(" ")
+            if cols[0] == "inet":
                 ip = cols[1]
                 ip = cols[1]
                 return ip
                 return ip
         else:
         else:
-            raise RuntimeError('No ip is found.')
+            raise RuntimeError("No ip is found.")
     else:
     else:
-        raise RuntimeError('Unsupported platform.')
+        raise RuntimeError("Unsupported platform.")
 
 
 
 
 def labelme_on_docker(in_file, out_file):
 def labelme_on_docker(in_file, out_file):
     ip = get_ip()
     ip = get_ip()
-    cmd = 'xhost + %s' % ip
+    cmd = "xhost + %s" % ip
     subprocess.check_output(shlex.split(cmd))
     subprocess.check_output(shlex.split(cmd))
 
 
     if out_file:
     if out_file:
         out_file = osp.abspath(out_file)
         out_file = osp.abspath(out_file)
         if osp.exists(out_file):
         if osp.exists(out_file):
-            raise RuntimeError('File exists: %s' % out_file)
+            raise RuntimeError("File exists: %s" % out_file)
         else:
         else:
-            open(osp.abspath(out_file), 'w')
+            open(osp.abspath(out_file), "w")
 
 
-    cmd = 'docker run -it --rm' \
-        ' -e DISPLAY={0}:0' \
-        ' -e QT_X11_NO_MITSHM=1' \
-        ' -v /tmp/.X11-unix:/tmp/.X11-unix' \
-        ' -v {1}:{2}' \
-        ' -w /home/developer'
-    in_file_a = osp.abspath(in_file)
-    in_file_b = osp.join('/home/developer', osp.basename(in_file))
-    cmd = cmd.format(
-        ip,
-        in_file_a,
-        in_file_b,
+    cmd = (
+        "docker run -it --rm"
+        " -e DISPLAY={0}:0"
+        " -e QT_X11_NO_MITSHM=1"
+        " -v /tmp/.X11-unix:/tmp/.X11-unix"
+        " -v {1}:{2}"
+        " -w /home/developer"
     )
     )
+    in_file_a = osp.abspath(in_file)
+    in_file_b = osp.join("/home/developer", osp.basename(in_file))
+    cmd = cmd.format(ip, in_file_a, in_file_b,)
     if out_file:
     if out_file:
         out_file_a = osp.abspath(out_file)
         out_file_a = osp.abspath(out_file)
-        out_file_b = osp.join('/home/developer', osp.basename(out_file))
-        cmd += ' -v {0}:{1}'.format(out_file_a, out_file_b)
-    cmd += ' wkentaro/labelme labelme {0}'.format(in_file_b)
+        out_file_b = osp.join("/home/developer", osp.basename(out_file))
+        cmd += " -v {0}:{1}".format(out_file_a, out_file_b)
+    cmd += " wkentaro/labelme labelme {0}".format(in_file_b)
     if out_file:
     if out_file:
-        cmd += ' -O {0}'.format(out_file_b)
+        cmd += " -O {0}".format(out_file_b)
     subprocess.call(shlex.split(cmd))
     subprocess.call(shlex.split(cmd))
 
 
     if out_file:
     if out_file:
@@ -72,29 +70,29 @@ def labelme_on_docker(in_file, out_file):
             json.load(open(out_file))
             json.load(open(out_file))
             return out_file
             return out_file
         except Exception:
         except Exception:
-            if open(out_file).read() == '':
+            if open(out_file).read() == "":
                 os.remove(out_file)
                 os.remove(out_file)
-            raise RuntimeError('Annotation is cancelled.')
+            raise RuntimeError("Annotation is cancelled.")
 
 
 
 
 def main():
 def main():
     parser = argparse.ArgumentParser()
     parser = argparse.ArgumentParser()
-    parser.add_argument('in_file', help='Input file or directory.')
-    parser.add_argument('-O', '--output')
+    parser.add_argument("in_file", help="Input file or directory.")
+    parser.add_argument("-O", "--output")
     args = parser.parse_args()
     args = parser.parse_args()
 
 
-    if not distutils.spawn.find_executable('docker'):
-        print('Please install docker', file=sys.stderr)
+    if not distutils.spawn.find_executable("docker"):
+        print("Please install docker", file=sys.stderr)
         sys.exit(1)
         sys.exit(1)
 
 
     try:
     try:
         out_file = labelme_on_docker(args.in_file, args.output)
         out_file = labelme_on_docker(args.in_file, args.output)
         if out_file:
         if out_file:
-            print('Saved to: %s' % out_file)
+            print("Saved to: %s" % out_file)
     except RuntimeError as e:
     except RuntimeError as e:
-        sys.stderr.write(e.__str__() + '\n')
+        sys.stderr.write(e.__str__() + "\n")
         sys.exit(1)
         sys.exit(1)
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 19 - 19
labelme/config/__init__.py

@@ -14,11 +14,9 @@ def update_dict(target_dict, new_dict, validate_item=None):
         if validate_item:
         if validate_item:
             validate_item(key, value)
             validate_item(key, value)
         if key not in target_dict:
         if key not in target_dict:
-            logger.warn('Skipping unexpected key in config: {}'
-                        .format(key))
+            logger.warn("Skipping unexpected key in config: {}".format(key))
             continue
             continue
-        if isinstance(target_dict[key], dict) and \
-                isinstance(value, dict):
+        if isinstance(target_dict[key], dict) and isinstance(value, dict):
             update_dict(target_dict[key], value, validate_item=validate_item)
             update_dict(target_dict[key], value, validate_item=validate_item)
         else:
         else:
             target_dict[key] = value
             target_dict[key] = value
@@ -28,33 +26,33 @@ def update_dict(target_dict, new_dict, validate_item=None):
 
 
 
 
 def get_default_config():
 def get_default_config():
-    config_file = osp.join(here, 'default_config.yaml')
+    config_file = osp.join(here, "default_config.yaml")
     with open(config_file) as f:
     with open(config_file) as f:
         config = yaml.safe_load(f)
         config = yaml.safe_load(f)
 
 
     # save default config to ~/.labelmerc
     # save default config to ~/.labelmerc
-    user_config_file = osp.join(osp.expanduser('~'), '.labelmerc')
+    user_config_file = osp.join(osp.expanduser("~"), ".labelmerc")
     if not osp.exists(user_config_file):
     if not osp.exists(user_config_file):
         try:
         try:
             shutil.copy(config_file, user_config_file)
             shutil.copy(config_file, user_config_file)
         except Exception:
         except Exception:
-            logger.warn('Failed to save config: {}'.format(user_config_file))
+            logger.warn("Failed to save config: {}".format(user_config_file))
 
 
     return config
     return config
 
 
 
 
 def validate_config_item(key, value):
 def validate_config_item(key, value):
-    if key == 'validate_label' and value not in [None, 'exact']:
+    if key == "validate_label" and value not in [None, "exact"]:
         raise ValueError(
         raise ValueError(
-            "Unexpected value for config key 'validate_label': {}"
-            .format(value)
+            "Unexpected value for config key 'validate_label': {}".format(
+                value
+            )
         )
         )
-    if key == 'shape_color' and value not in [None, 'auto', 'manual']:
+    if key == "shape_color" and value not in [None, "auto", "manual"]:
         raise ValueError(
         raise ValueError(
-            "Unexpected value for config key 'shape_color': {}"
-            .format(value)
+            "Unexpected value for config key 'shape_color': {}".format(value)
         )
         )
-    if key == 'labels' and value is not None and len(value) != len(set(value)):
+    if key == "labels" and value is not None and len(value) != len(set(value)):
         raise ValueError(
         raise ValueError(
             "Duplicates are detected for config key 'labels': {}".format(value)
             "Duplicates are detected for config key 'labels': {}".format(value)
         )
         )
@@ -70,15 +68,17 @@ def get_config(config_file_or_yaml=None, config_from_args=None):
         if not isinstance(config_from_yaml, dict):
         if not isinstance(config_from_yaml, dict):
             with open(config_from_yaml) as f:
             with open(config_from_yaml) as f:
                 logger.info(
                 logger.info(
-                    'Loading config file from: {}'.format(config_from_yaml)
+                    "Loading config file from: {}".format(config_from_yaml)
                 )
                 )
                 config_from_yaml = yaml.safe_load(f)
                 config_from_yaml = yaml.safe_load(f)
-        update_dict(config, config_from_yaml,
-                    validate_item=validate_config_item)
+        update_dict(
+            config, config_from_yaml, validate_item=validate_config_item
+        )
 
 
     # 3. command line argument or specified config file
     # 3. command line argument or specified config file
     if config_from_args is not None:
     if config_from_args is not None:
-        update_dict(config, config_from_args,
-                    validate_item=validate_config_item)
+        update_dict(
+            config, config_from_args, validate_item=validate_config_item
+        )
 
 
     return config
     return config

+ 48 - 47
labelme/label_file.py

@@ -21,7 +21,7 @@ class LabelFileError(Exception):
 
 
 class LabelFile(object):
 class LabelFile(object):
 
 
-    suffix = '.json'
+    suffix = ".json"
 
 
     def __init__(self, filename=None):
     def __init__(self, filename=None):
         self.shapes = []
         self.shapes = []
@@ -36,7 +36,7 @@ class LabelFile(object):
         try:
         try:
             image_pil = PIL.Image.open(filename)
             image_pil = PIL.Image.open(filename)
         except IOError:
         except IOError:
-            logger.error('Failed opening image file: {}'.format(filename))
+            logger.error("Failed opening image file: {}".format(filename))
             return
             return
 
 
         # apply orientation to image according to exif
         # apply orientation to image according to exif
@@ -45,77 +45,78 @@ class LabelFile(object):
         with io.BytesIO() as f:
         with io.BytesIO() as f:
             ext = osp.splitext(filename)[1].lower()
             ext = osp.splitext(filename)[1].lower()
             if PY2 and QT4:
             if PY2 and QT4:
-                format = 'PNG'
-            elif ext in ['.jpg', '.jpeg']:
-                format = 'JPEG'
+                format = "PNG"
+            elif ext in [".jpg", ".jpeg"]:
+                format = "JPEG"
             else:
             else:
-                format = 'PNG'
+                format = "PNG"
             image_pil.save(f, format=format)
             image_pil.save(f, format=format)
             f.seek(0)
             f.seek(0)
             return f.read()
             return f.read()
 
 
     def load(self, filename):
     def load(self, filename):
         keys = [
         keys = [
-            'version',
-            'imageData',
-            'imagePath',
-            'shapes',  # polygonal annotations
-            'flags',   # image level flags
-            'imageHeight',
-            'imageWidth',
+            "version",
+            "imageData",
+            "imagePath",
+            "shapes",  # polygonal annotations
+            "flags",  # image level flags
+            "imageHeight",
+            "imageWidth",
         ]
         ]
         shape_keys = [
         shape_keys = [
-            'label',
-            'points',
-            'group_id',
-            'shape_type',
-            'flags',
+            "label",
+            "points",
+            "group_id",
+            "shape_type",
+            "flags",
         ]
         ]
         try:
         try:
-            with open(filename, 'rb' if PY2 else 'r') as f:
+            with open(filename, "rb" if PY2 else "r") as f:
                 data = json.load(f)
                 data = json.load(f)
-            version = data.get('version')
+            version = data.get("version")
             if version is None:
             if version is None:
                 logger.warn(
                 logger.warn(
-                    'Loading JSON file ({}) of unknown version'
-                    .format(filename)
+                    "Loading JSON file ({}) of unknown version".format(
+                        filename
+                    )
                 )
                 )
-            elif version.split('.')[0] != __version__.split('.')[0]:
+            elif version.split(".")[0] != __version__.split(".")[0]:
                 logger.warn(
                 logger.warn(
-                    'This JSON file ({}) may be incompatible with '
-                    'current labelme. version in file: {}, '
-                    'current version: {}'.format(
+                    "This JSON file ({}) may be incompatible with "
+                    "current labelme. version in file: {}, "
+                    "current version: {}".format(
                         filename, version, __version__
                         filename, version, __version__
                     )
                     )
                 )
                 )
 
 
-            if data['imageData'] is not None:
-                imageData = base64.b64decode(data['imageData'])
+            if data["imageData"] is not None:
+                imageData = base64.b64decode(data["imageData"])
                 if PY2 and QT4:
                 if PY2 and QT4:
                     imageData = utils.img_data_to_png_data(imageData)
                     imageData = utils.img_data_to_png_data(imageData)
             else:
             else:
                 # relative path from label file to relative path from cwd
                 # relative path from label file to relative path from cwd
-                imagePath = osp.join(osp.dirname(filename), data['imagePath'])
+                imagePath = osp.join(osp.dirname(filename), data["imagePath"])
                 imageData = self.load_image_file(imagePath)
                 imageData = self.load_image_file(imagePath)
-            flags = data.get('flags') or {}
-            imagePath = data['imagePath']
+            flags = data.get("flags") or {}
+            imagePath = data["imagePath"]
             self._check_image_height_and_width(
             self._check_image_height_and_width(
-                base64.b64encode(imageData).decode('utf-8'),
-                data.get('imageHeight'),
-                data.get('imageWidth'),
+                base64.b64encode(imageData).decode("utf-8"),
+                data.get("imageHeight"),
+                data.get("imageWidth"),
             )
             )
             shapes = [
             shapes = [
                 dict(
                 dict(
-                    label=s['label'],
-                    points=s['points'],
-                    shape_type=s.get('shape_type', 'polygon'),
-                    flags=s.get('flags', {}),
-                    group_id=s.get('group_id'),
+                    label=s["label"],
+                    points=s["points"],
+                    shape_type=s.get("shape_type", "polygon"),
+                    flags=s.get("flags", {}),
+                    group_id=s.get("group_id"),
                     other_data={
                     other_data={
                         k: v for k, v in s.items() if k not in shape_keys
                         k: v for k, v in s.items() if k not in shape_keys
-                    }
+                    },
                 )
                 )
-                for s in data['shapes']
+                for s in data["shapes"]
             ]
             ]
         except Exception as e:
         except Exception as e:
             raise LabelFileError(e)
             raise LabelFileError(e)
@@ -138,14 +139,14 @@ class LabelFile(object):
         img_arr = utils.img_b64_to_arr(imageData)
         img_arr = utils.img_b64_to_arr(imageData)
         if imageHeight is not None and img_arr.shape[0] != imageHeight:
         if imageHeight is not None and img_arr.shape[0] != imageHeight:
             logger.error(
             logger.error(
-                'imageHeight does not match with imageData or imagePath, '
-                'so getting imageHeight from actual image.'
+                "imageHeight does not match with imageData or imagePath, "
+                "so getting imageHeight from actual image."
             )
             )
             imageHeight = img_arr.shape[0]
             imageHeight = img_arr.shape[0]
         if imageWidth is not None and img_arr.shape[1] != imageWidth:
         if imageWidth is not None and img_arr.shape[1] != imageWidth:
             logger.error(
             logger.error(
-                'imageWidth does not match with imageData or imagePath, '
-                'so getting imageWidth from actual image.'
+                "imageWidth does not match with imageData or imagePath, "
+                "so getting imageWidth from actual image."
             )
             )
             imageWidth = img_arr.shape[1]
             imageWidth = img_arr.shape[1]
         return imageHeight, imageWidth
         return imageHeight, imageWidth
@@ -162,7 +163,7 @@ class LabelFile(object):
         flags=None,
         flags=None,
     ):
     ):
         if imageData is not None:
         if imageData is not None:
-            imageData = base64.b64encode(imageData).decode('utf-8')
+            imageData = base64.b64encode(imageData).decode("utf-8")
             imageHeight, imageWidth = self._check_image_height_and_width(
             imageHeight, imageWidth = self._check_image_height_and_width(
                 imageData, imageHeight, imageWidth
                 imageData, imageHeight, imageWidth
             )
             )
@@ -183,7 +184,7 @@ class LabelFile(object):
             assert key not in data
             assert key not in data
             data[key] = value
             data[key] = value
         try:
         try:
-            with open(filename, 'wb' if PY2 else 'w') as f:
+            with open(filename, "wb" if PY2 else "w") as f:
                 json.dump(data, f, ensure_ascii=False, indent=2)
                 json.dump(data, f, ensure_ascii=False, indent=2)
             self.filename = filename
             self.filename = filename
         except Exception as e:
         except Exception as e:

+ 12 - 15
labelme/logger.py

@@ -7,16 +7,15 @@ from . import __appname__
 
 
 
 
 COLORS = {
 COLORS = {
-    'WARNING': 'yellow',
-    'INFO': 'white',
-    'DEBUG': 'blue',
-    'CRITICAL': 'red',
-    'ERROR': 'red',
+    "WARNING": "yellow",
+    "INFO": "white",
+    "DEBUG": "blue",
+    "CRITICAL": "red",
+    "ERROR": "red",
 }
 }
 
 
 
 
 class ColoredFormatter(logging.Formatter):
 class ColoredFormatter(logging.Formatter):
-
     def __init__(self, fmt, use_color=True):
     def __init__(self, fmt, use_color=True):
         logging.Formatter.__init__(self, fmt)
         logging.Formatter.__init__(self, fmt)
         self.use_color = use_color
         self.use_color = use_color
@@ -27,27 +26,25 @@ class ColoredFormatter(logging.Formatter):
 
 
             def colored(text):
             def colored(text):
                 return termcolor.colored(
                 return termcolor.colored(
-                    text,
-                    color=COLORS[levelname],
-                    attrs={'bold': True},
+                    text, color=COLORS[levelname], attrs={"bold": True},
                 )
                 )
 
 
-            record.levelname2 = colored('{:<7}'.format(record.levelname))
+            record.levelname2 = colored("{:<7}".format(record.levelname))
             record.message2 = colored(record.msg)
             record.message2 = colored(record.msg)
 
 
             asctime2 = datetime.datetime.fromtimestamp(record.created)
             asctime2 = datetime.datetime.fromtimestamp(record.created)
-            record.asctime2 = termcolor.colored(asctime2, color='green')
+            record.asctime2 = termcolor.colored(asctime2, color="green")
 
 
-            record.module2 = termcolor.colored(record.module, color='cyan')
-            record.funcName2 = termcolor.colored(record.funcName, color='cyan')
-            record.lineno2 = termcolor.colored(record.lineno, color='cyan')
+            record.module2 = termcolor.colored(record.module, color="cyan")
+            record.funcName2 = termcolor.colored(record.funcName, color="cyan")
+            record.lineno2 = termcolor.colored(record.lineno, color="cyan")
         return logging.Formatter.format(self, record)
         return logging.Formatter.format(self, record)
 
 
 
 
 class ColoredLogger(logging.Logger):
 class ColoredLogger(logging.Logger):
 
 
     FORMAT = (
     FORMAT = (
-        '[%(levelname2)s] %(module2)s:%(funcName2)s:%(lineno2)s - %(message2)s'
+        "[%(levelname2)s] %(module2)s:%(funcName2)s:%(lineno2)s - %(message2)s"
     )
     )
 
 
     def __init__(self, name):
     def __init__(self, name):

+ 36 - 20
labelme/shape.py

@@ -12,11 +12,11 @@ import labelme.utils
 
 
 
 
 R, G, B = SHAPE_COLOR = 0, 255, 0  # green
 R, G, B = SHAPE_COLOR = 0, 255, 0  # green
-DEFAULT_LINE_COLOR = QtGui.QColor(R, G, B, 128)                # bf hovering
-DEFAULT_FILL_COLOR = QtGui.QColor(R, G, B, 128)                # hovering
-DEFAULT_SELECT_LINE_COLOR = QtGui.QColor(255, 255, 255)        # selected
-DEFAULT_SELECT_FILL_COLOR = QtGui.QColor(R, G, B, 155)         # selected
-DEFAULT_VERTEX_FILL_COLOR = QtGui.QColor(R, G, B, 255)         # hovering
+DEFAULT_LINE_COLOR = QtGui.QColor(R, G, B, 128)  # bf hovering
+DEFAULT_FILL_COLOR = QtGui.QColor(R, G, B, 128)  # hovering
+DEFAULT_SELECT_LINE_COLOR = QtGui.QColor(255, 255, 255)  # selected
+DEFAULT_SELECT_FILL_COLOR = QtGui.QColor(R, G, B, 155)  # selected
+DEFAULT_VERTEX_FILL_COLOR = QtGui.QColor(R, G, B, 255)  # hovering
 DEFAULT_HVERTEX_FILL_COLOR = QtGui.QColor(255, 255, 255, 255)  # hovering
 DEFAULT_HVERTEX_FILL_COLOR = QtGui.QColor(255, 255, 255, 255)  # hovering
 
 
 
 
@@ -37,8 +37,14 @@ class Shape(object):
     point_size = 8
     point_size = 8
     scale = 1.0
     scale = 1.0
 
 
-    def __init__(self, label=None, line_color=None, shape_type=None,
-                 flags=None, group_id=None):
+    def __init__(
+        self,
+        label=None,
+        line_color=None,
+        shape_type=None,
+        flags=None,
+        group_id=None,
+    ):
         self.label = label
         self.label = label
         self.group_id = group_id
         self.group_id = group_id
         self.points = []
         self.points = []
@@ -72,10 +78,16 @@ class Shape(object):
     @shape_type.setter
     @shape_type.setter
     def shape_type(self, value):
     def shape_type(self, value):
         if value is None:
         if value is None:
-            value = 'polygon'
-        if value not in ['polygon', 'rectangle', 'point',
-           'line', 'circle', 'linestrip']:
-            raise ValueError('Unexpected shape_type: {}'.format(value))
+            value = "polygon"
+        if value not in [
+            "polygon",
+            "rectangle",
+            "point",
+            "line",
+            "circle",
+            "linestrip",
+        ]:
+            raise ValueError("Unexpected shape_type: {}".format(value))
         self._shape_type = value
         self._shape_type = value
 
 
     def close(self):
     def close(self):
@@ -88,7 +100,7 @@ class Shape(object):
             self.points.append(point)
             self.points.append(point)
 
 
     def canAddPoint(self):
     def canAddPoint(self):
-        return self.shape_type in ['polygon', 'linestrip']
+        return self.shape_type in ["polygon", "linestrip"]
 
 
     def popPoint(self):
     def popPoint(self):
         if self.points:
         if self.points:
@@ -114,8 +126,9 @@ class Shape(object):
 
 
     def paint(self, painter):
     def paint(self, painter):
         if self.points:
         if self.points:
-            color = self.select_line_color \
-                if self.selected else self.line_color
+            color = (
+                self.select_line_color if self.selected else self.line_color
+            )
             pen = QtGui.QPen(color)
             pen = QtGui.QPen(color)
             # Try using integer sizes for smoother drawing(?)
             # Try using integer sizes for smoother drawing(?)
             pen.setWidth(max(1, int(round(2.0 / self.scale))))
             pen.setWidth(max(1, int(round(2.0 / self.scale))))
@@ -124,7 +137,7 @@ class Shape(object):
             line_path = QtGui.QPainterPath()
             line_path = QtGui.QPainterPath()
             vrtx_path = QtGui.QPainterPath()
             vrtx_path = QtGui.QPainterPath()
 
 
-            if self.shape_type == 'rectangle':
+            if self.shape_type == "rectangle":
                 assert len(self.points) in [1, 2]
                 assert len(self.points) in [1, 2]
                 if len(self.points) == 2:
                 if len(self.points) == 2:
                     rectangle = self.getRectFromLine(*self.points)
                     rectangle = self.getRectFromLine(*self.points)
@@ -160,8 +173,11 @@ class Shape(object):
             painter.drawPath(vrtx_path)
             painter.drawPath(vrtx_path)
             painter.fillPath(vrtx_path, self._vertex_fill_color)
             painter.fillPath(vrtx_path, self._vertex_fill_color)
             if self.fill:
             if self.fill:
-                color = self.select_fill_color \
-                    if self.selected else self.fill_color
+                color = (
+                    self.select_fill_color
+                    if self.selected
+                    else self.fill_color
+                )
                 painter.fillPath(line_path, color)
                 painter.fillPath(line_path, color)
 
 
     def drawVertex(self, path, i):
     def drawVertex(self, path, i):
@@ -183,7 +199,7 @@ class Shape(object):
             assert False, "unsupported vertex shape"
             assert False, "unsupported vertex shape"
 
 
     def nearestVertex(self, point, epsilon):
     def nearestVertex(self, point, epsilon):
-        min_distance = float('inf')
+        min_distance = float("inf")
         min_i = None
         min_i = None
         for i, p in enumerate(self.points):
         for i, p in enumerate(self.points):
             dist = labelme.utils.distance(p - point)
             dist = labelme.utils.distance(p - point)
@@ -193,7 +209,7 @@ class Shape(object):
         return min_i
         return min_i
 
 
     def nearestEdge(self, point, epsilon):
     def nearestEdge(self, point, epsilon):
-        min_distance = float('inf')
+        min_distance = float("inf")
         post_i = None
         post_i = None
         for i in range(len(self.points)):
         for i in range(len(self.points)):
             line = [self.points[i - 1], self.points[i]]
             line = [self.points[i - 1], self.points[i]]
@@ -217,7 +233,7 @@ class Shape(object):
         return rectangle
         return rectangle
 
 
     def makePath(self):
     def makePath(self):
-        if self.shape_type == 'rectangle':
+        if self.shape_type == "rectangle":
             path = QtGui.QPainterPath()
             path = QtGui.QPainterPath()
             if len(self.points) == 2:
             if len(self.points) == 2:
                 rectangle = self.getRectFromLine(*self.points)
                 rectangle = self.getRectFromLine(*self.points)

+ 10 - 10
labelme/testing.py

@@ -10,24 +10,24 @@ def assert_labelfile_sanity(filename):
 
 
     data = json.load(open(filename))
     data = json.load(open(filename))
 
 
-    assert 'imagePath' in data
-    imageData = data.get('imageData', None)
+    assert "imagePath" in data
+    imageData = data.get("imageData", None)
     if imageData is None:
     if imageData is None:
         parent_dir = osp.dirname(filename)
         parent_dir = osp.dirname(filename)
-        img_file = osp.join(parent_dir, data['imagePath'])
+        img_file = osp.join(parent_dir, data["imagePath"])
         assert osp.exists(img_file)
         assert osp.exists(img_file)
         img = imgviz.io.imread(img_file)
         img = imgviz.io.imread(img_file)
     else:
     else:
         img = labelme.utils.img_b64_to_arr(imageData)
         img = labelme.utils.img_b64_to_arr(imageData)
 
 
     H, W = img.shape[:2]
     H, W = img.shape[:2]
-    assert H == data['imageHeight']
-    assert W == data['imageWidth']
+    assert H == data["imageHeight"]
+    assert W == data["imageWidth"]
 
 
-    assert 'shapes' in data
-    for shape in data['shapes']:
-        assert 'label' in shape
-        assert 'points' in shape
-        for x, y in shape['points']:
+    assert "shapes" in data
+    for shape in data["shapes"]:
+        assert "label" in shape
+        assert "points" in shape
+        for x, y in shape["points"]:
             assert 0 <= x <= W
             assert 0 <= x <= W
             assert 0 <= y <= H
             assert 0 <= y <= H

+ 5 - 5
labelme/utils/_io.py

@@ -7,17 +7,17 @@ import PIL.Image
 def lblsave(filename, lbl):
 def lblsave(filename, lbl):
     import imgviz
     import imgviz
 
 
-    if osp.splitext(filename)[1] != '.png':
-        filename += '.png'
+    if osp.splitext(filename)[1] != ".png":
+        filename += ".png"
     # Assume label ranses [-1, 254] for int32,
     # Assume label ranses [-1, 254] for int32,
     # and [0, 255] for uint8 as VOC.
     # and [0, 255] for uint8 as VOC.
     if lbl.min() >= -1 and lbl.max() < 255:
     if lbl.min() >= -1 and lbl.max() < 255:
-        lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode='P')
+        lbl_pil = PIL.Image.fromarray(lbl.astype(np.uint8), mode="P")
         colormap = imgviz.label_colormap()
         colormap = imgviz.label_colormap()
         lbl_pil.putpalette(colormap.flatten())
         lbl_pil.putpalette(colormap.flatten())
         lbl_pil.save(filename)
         lbl_pil.save(filename)
     else:
     else:
         raise ValueError(
         raise ValueError(
-            '[%s] Cannot save the pixel-wise class label as PNG. '
-            'Please consider using the .npy format.' % filename
+            "[%s] Cannot save the pixel-wise class label as PNG. "
+            "Please consider using the .npy format." % filename
         )
         )

+ 4 - 4
labelme/utils/image.py

@@ -23,9 +23,9 @@ def img_b64_to_arr(img_b64):
 def img_arr_to_b64(img_arr):
 def img_arr_to_b64(img_arr):
     img_pil = PIL.Image.fromarray(img_arr)
     img_pil = PIL.Image.fromarray(img_arr)
     f = io.BytesIO()
     f = io.BytesIO()
-    img_pil.save(f, format='PNG')
+    img_pil.save(f, format="PNG")
     img_bin = f.getvalue()
     img_bin = f.getvalue()
-    if hasattr(base64, 'encodebytes'):
+    if hasattr(base64, "encodebytes"):
         img_b64 = base64.encodebytes(img_bin)
         img_b64 = base64.encodebytes(img_bin)
     else:
     else:
         img_b64 = base64.encodestring(img_bin)
         img_b64 = base64.encodestring(img_bin)
@@ -38,7 +38,7 @@ def img_data_to_png_data(img_data):
         img = PIL.Image.open(f)
         img = PIL.Image.open(f)
 
 
         with io.BytesIO() as f:
         with io.BytesIO() as f:
-            img.save(f, 'PNG')
+            img.save(f, "PNG")
             f.seek(0)
             f.seek(0)
             return f.read()
             return f.read()
 
 
@@ -58,7 +58,7 @@ def apply_exif_orientation(image):
         if k in PIL.ExifTags.TAGS
         if k in PIL.ExifTags.TAGS
     }
     }
 
 
-    orientation = exif.get('Orientation', None)
+    orientation = exif.get("Orientation", None)
 
 
     if orientation == 1:
     if orientation == 1:
         # do nothing
         # do nothing

+ 17 - 8
labelme/utils/qt.py

@@ -12,8 +12,8 @@ here = osp.dirname(osp.abspath(__file__))
 
 
 
 
 def newIcon(icon):
 def newIcon(icon):
-    icons_dir = osp.join(here, '../icons')
-    return QtGui.QIcon(osp.join(':/', icons_dir, '%s.png' % icon))
+    icons_dir = osp.join(here, "../icons")
+    return QtGui.QIcon(osp.join(":/", icons_dir, "%s.png" % icon))
 
 
 
 
 def newButton(text, icon=None, slot=None):
 def newButton(text, icon=None, slot=None):
@@ -25,12 +25,21 @@ def newButton(text, icon=None, slot=None):
     return b
     return b
 
 
 
 
-def newAction(parent, text, slot=None, shortcut=None, icon=None,
-              tip=None, checkable=False, enabled=True, checked=False):
+def newAction(
+    parent,
+    text,
+    slot=None,
+    shortcut=None,
+    icon=None,
+    tip=None,
+    checkable=False,
+    enabled=True,
+    checked=False,
+):
     """Create a new action and assign callbacks, shortcuts, etc."""
     """Create a new action and assign callbacks, shortcuts, etc."""
     a = QtWidgets.QAction(text, parent)
     a = QtWidgets.QAction(text, parent)
     if icon is not None:
     if icon is not None:
-        a.setIconText(text.replace(' ', '\n'))
+        a.setIconText(text.replace(" ", "\n"))
         a.setIcon(newIcon(icon))
         a.setIcon(newIcon(icon))
     if shortcut is not None:
     if shortcut is not None:
         if isinstance(shortcut, (list, tuple)):
         if isinstance(shortcut, (list, tuple)):
@@ -60,7 +69,7 @@ def addActions(widget, actions):
 
 
 
 
 def labelValidator():
 def labelValidator():
-    return QtGui.QRegExpValidator(QtCore.QRegExp(r'^[^ \t].+'), None)
+    return QtGui.QRegExpValidator(QtCore.QRegExp(r"^[^ \t].+"), None)
 
 
 
 
 class struct(object):
 class struct(object):
@@ -85,5 +94,5 @@ def distancetoline(point, line):
 
 
 
 
 def fmtShortcut(text):
 def fmtShortcut(text):
-    mod, key = text.split('+', 1)
-    return '<b>%s</b>+<b>%s</b>' % (mod, key)
+    mod, key = text.split("+", 1)
+    return "<b>%s</b>+<b>%s</b>" % (mod, key)

+ 25 - 24
labelme/utils/shape.py

@@ -16,32 +16,33 @@ def polygons_to_mask(img_shape, polygons, shape_type=None):
     return shape_to_mask(img_shape, points=polygons, shape_type=shape_type)
     return shape_to_mask(img_shape, points=polygons, shape_type=shape_type)
 
 
 
 
-def shape_to_mask(img_shape, points, shape_type=None,
-                  line_width=10, point_size=5):
+def shape_to_mask(
+    img_shape, points, shape_type=None, line_width=10, point_size=5
+):
     mask = np.zeros(img_shape[:2], dtype=np.uint8)
     mask = np.zeros(img_shape[:2], dtype=np.uint8)
     mask = PIL.Image.fromarray(mask)
     mask = PIL.Image.fromarray(mask)
     draw = PIL.ImageDraw.Draw(mask)
     draw = PIL.ImageDraw.Draw(mask)
     xy = [tuple(point) for point in points]
     xy = [tuple(point) for point in points]
-    if shape_type == 'circle':
-        assert len(xy) == 2, 'Shape of shape_type=circle must have 2 points'
+    if shape_type == "circle":
+        assert len(xy) == 2, "Shape of shape_type=circle must have 2 points"
         (cx, cy), (px, py) = xy
         (cx, cy), (px, py) = xy
         d = math.sqrt((cx - px) ** 2 + (cy - py) ** 2)
         d = math.sqrt((cx - px) ** 2 + (cy - py) ** 2)
         draw.ellipse([cx - d, cy - d, cx + d, cy + d], outline=1, fill=1)
         draw.ellipse([cx - d, cy - d, cx + d, cy + d], outline=1, fill=1)
-    elif shape_type == 'rectangle':
-        assert len(xy) == 2, 'Shape of shape_type=rectangle must have 2 points'
+    elif shape_type == "rectangle":
+        assert len(xy) == 2, "Shape of shape_type=rectangle must have 2 points"
         draw.rectangle(xy, outline=1, fill=1)
         draw.rectangle(xy, outline=1, fill=1)
-    elif shape_type == 'line':
-        assert len(xy) == 2, 'Shape of shape_type=line must have 2 points'
+    elif shape_type == "line":
+        assert len(xy) == 2, "Shape of shape_type=line must have 2 points"
         draw.line(xy=xy, fill=1, width=line_width)
         draw.line(xy=xy, fill=1, width=line_width)
-    elif shape_type == 'linestrip':
+    elif shape_type == "linestrip":
         draw.line(xy=xy, fill=1, width=line_width)
         draw.line(xy=xy, fill=1, width=line_width)
-    elif shape_type == 'point':
-        assert len(xy) == 1, 'Shape of shape_type=point must have 1 points'
+    elif shape_type == "point":
+        assert len(xy) == 1, "Shape of shape_type=point must have 1 points"
         cx, cy = xy[0]
         cx, cy = xy[0]
         r = point_size
         r = point_size
         draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=1, fill=1)
         draw.ellipse([cx - r, cy - r, cx + r, cy + r], outline=1, fill=1)
     else:
     else:
-        assert len(xy) > 2, 'Polygon must have points more than 2'
+        assert len(xy) > 2, "Polygon must have points more than 2"
         draw.polygon(xy=xy, outline=1, fill=1)
         draw.polygon(xy=xy, outline=1, fill=1)
     mask = np.array(mask, dtype=bool)
     mask = np.array(mask, dtype=bool)
     return mask
     return mask
@@ -52,12 +53,12 @@ def shapes_to_label(img_shape, shapes, label_name_to_value):
     ins = np.zeros_like(cls)
     ins = np.zeros_like(cls)
     instances = []
     instances = []
     for shape in shapes:
     for shape in shapes:
-        points = shape['points']
-        label = shape['label']
-        group_id = shape.get('group_id')
+        points = shape["points"]
+        label = shape["label"]
+        group_id = shape.get("group_id")
         if group_id is None:
         if group_id is None:
             group_id = uuid.uuid1()
             group_id = uuid.uuid1()
-        shape_type = shape.get('shape_type', None)
+        shape_type = shape.get("shape_type", None)
 
 
         cls_name = label
         cls_name = label
         instance = (cls_name, group_id)
         instance = (cls_name, group_id)
@@ -75,12 +76,14 @@ def shapes_to_label(img_shape, shapes, label_name_to_value):
 
 
 
 
 def labelme_shapes_to_label(img_shape, shapes):
 def labelme_shapes_to_label(img_shape, shapes):
-    logger.warn('labelme_shapes_to_label is deprecated, so please use '
-                'shapes_to_label.')
+    logger.warn(
+        "labelme_shapes_to_label is deprecated, so please use "
+        "shapes_to_label."
+    )
 
 
-    label_name_to_value = {'_background_': 0}
+    label_name_to_value = {"_background_": 0}
     for shape in shapes:
     for shape in shapes:
-        label_name = shape['label']
+        label_name = shape["label"]
         if label_name in label_name_to_value:
         if label_name in label_name_to_value:
             label_value = label_name_to_value[label_name]
             label_value = label_name_to_value[label_name]
         else:
         else:
@@ -94,13 +97,11 @@ def labelme_shapes_to_label(img_shape, shapes):
 def masks_to_bboxes(masks):
 def masks_to_bboxes(masks):
     if masks.ndim != 3:
     if masks.ndim != 3:
         raise ValueError(
         raise ValueError(
-            'masks.ndim must be 3, but it is {}'
-            .format(masks.ndim)
+            "masks.ndim must be 3, but it is {}".format(masks.ndim)
         )
         )
     if masks.dtype != bool:
     if masks.dtype != bool:
         raise ValueError(
         raise ValueError(
-            'masks.dtype must be bool type, but it is {}'
-            .format(masks.dtype)
+            "masks.dtype must be bool type, but it is {}".format(masks.dtype)
         )
         )
     bboxes = []
     bboxes = []
     for mask in masks:
     for mask in masks:

+ 10 - 10
labelme/widgets/brightness_contrast_dialog.py

@@ -10,31 +10,31 @@ class BrightnessContrastDialog(QtWidgets.QDialog):
     def __init__(self, filename, callback, parent=None):
     def __init__(self, filename, callback, parent=None):
         super(BrightnessContrastDialog, self).__init__(parent)
         super(BrightnessContrastDialog, self).__init__(parent)
         self.setModal(True)
         self.setModal(True)
-        self.setWindowTitle('Brightness/Contrast')
+        self.setWindowTitle("Brightness/Contrast")
 
 
         self.slider_brightness = self._create_slider()
         self.slider_brightness = self._create_slider()
         self.slider_contrast = self._create_slider()
         self.slider_contrast = self._create_slider()
 
 
         formLayout = QtWidgets.QFormLayout()
         formLayout = QtWidgets.QFormLayout()
-        formLayout.addRow(self.tr('Brightness'), self.slider_brightness)
-        formLayout.addRow(self.tr('Contrast'), self.slider_contrast)
+        formLayout.addRow(self.tr("Brightness"), self.slider_brightness)
+        formLayout.addRow(self.tr("Contrast"), self.slider_contrast)
         self.setLayout(formLayout)
         self.setLayout(formLayout)
 
 
-        self.img = Image.open(filename).convert('RGBA')
+        self.img = Image.open(filename).convert("RGBA")
         self.callback = callback
         self.callback = callback
 
 
     def onNewValue(self, value):
     def onNewValue(self, value):
-        brightness = self.slider_brightness.value() / 100.
-        contrast = self.slider_contrast.value() / 100.
+        brightness = self.slider_brightness.value() / 100.0
+        contrast = self.slider_contrast.value() / 100.0
 
 
         img = self.img
         img = self.img
         img = ImageEnhance.Brightness(img).enhance(brightness)
         img = ImageEnhance.Brightness(img).enhance(brightness)
         img = ImageEnhance.Contrast(img).enhance(contrast)
         img = ImageEnhance.Contrast(img).enhance(contrast)
 
 
-        bytes = img.tobytes('raw', 'RGBA')
-        qimage = QtGui.QImage(bytes,
-                              img.size[0], img.size[1],
-                              QtGui.QImage.Format_RGB32).rgbSwapped()
+        bytes = img.tobytes("raw", "RGBA")
+        qimage = QtGui.QImage(
+            bytes, img.size[0], img.size[1], QtGui.QImage.Format_RGB32
+        ).rgbSwapped()
         self.callback(qimage)
         self.callback(qimage)
 
 
     def _create_slider(self):
     def _create_slider(self):

+ 78 - 48
labelme/widgets/canvas.py

@@ -32,17 +32,18 @@ class Canvas(QtWidgets.QWidget):
     CREATE, EDIT = 0, 1
     CREATE, EDIT = 0, 1
 
 
     # polygon, rectangle, line, or point
     # polygon, rectangle, line, or point
-    _createMode = 'polygon'
+    _createMode = "polygon"
 
 
     _fill_drawing = False
     _fill_drawing = False
 
 
     def __init__(self, *args, **kwargs):
     def __init__(self, *args, **kwargs):
-        self.epsilon = kwargs.pop('epsilon', 10.0)
-        self.double_click = kwargs.pop('double_click', 'close')
-        if self.double_click not in [None, 'close']:
+        self.epsilon = kwargs.pop("epsilon", 10.0)
+        self.double_click = kwargs.pop("double_click", "close")
+        if self.double_click not in [None, "close"]:
             raise ValueError(
             raise ValueError(
-                'Unexpected value for double_click event: {}'
-                .format(self.double_click)
+                "Unexpected value for double_click event: {}".format(
+                    self.double_click
+                )
             )
             )
         super(Canvas, self).__init__(*args, **kwargs)
         super(Canvas, self).__init__(*args, **kwargs)
         # Initialise local state.
         # Initialise local state.
@@ -95,9 +96,15 @@ class Canvas(QtWidgets.QWidget):
 
 
     @createMode.setter
     @createMode.setter
     def createMode(self, value):
     def createMode(self, value):
-        if value not in ['polygon', 'rectangle', 'circle',
-           'line', 'point', 'linestrip']:
-            raise ValueError('Unsupported createMode: %s' % value)
+        if value not in [
+            "polygon",
+            "rectangle",
+            "circle",
+            "line",
+            "point",
+            "linestrip",
+        ]:
+            raise ValueError("Unsupported createMode: %s" % value)
         self._createMode = value
         self._createMode = value
 
 
     def storeShapes(self):
     def storeShapes(self):
@@ -187,26 +194,29 @@ class Canvas(QtWidgets.QWidget):
                 # Don't allow the user to draw outside the pixmap.
                 # Don't allow the user to draw outside the pixmap.
                 # Project the point to the pixmap's edges.
                 # Project the point to the pixmap's edges.
                 pos = self.intersectionPoint(self.current[-1], pos)
                 pos = self.intersectionPoint(self.current[-1], pos)
-            elif len(self.current) > 1 and self.createMode == 'polygon' and\
-                    self.closeEnough(pos, self.current[0]):
+            elif (
+                len(self.current) > 1
+                and self.createMode == "polygon"
+                and self.closeEnough(pos, self.current[0])
+            ):
                 # Attract line to starting point and
                 # Attract line to starting point and
                 # colorise to alert the user.
                 # colorise to alert the user.
                 pos = self.current[0]
                 pos = self.current[0]
                 self.overrideCursor(CURSOR_POINT)
                 self.overrideCursor(CURSOR_POINT)
                 self.current.highlightVertex(0, Shape.NEAR_VERTEX)
                 self.current.highlightVertex(0, Shape.NEAR_VERTEX)
-            if self.createMode in ['polygon', 'linestrip']:
+            if self.createMode in ["polygon", "linestrip"]:
                 self.line[0] = self.current[-1]
                 self.line[0] = self.current[-1]
                 self.line[1] = pos
                 self.line[1] = pos
-            elif self.createMode == 'rectangle':
+            elif self.createMode == "rectangle":
                 self.line.points = [self.current[0], pos]
                 self.line.points = [self.current[0], pos]
                 self.line.close()
                 self.line.close()
-            elif self.createMode == 'circle':
+            elif self.createMode == "circle":
                 self.line.points = [self.current[0], pos]
                 self.line.points = [self.current[0], pos]
                 self.line.shape_type = "circle"
                 self.line.shape_type = "circle"
-            elif self.createMode == 'line':
+            elif self.createMode == "line":
                 self.line.points = [self.current[0], pos]
                 self.line.points = [self.current[0], pos]
                 self.line.close()
                 self.line.close()
-            elif self.createMode == 'point':
+            elif self.createMode == "point":
                 self.line.points = [self.current[0]]
                 self.line.points = [self.current[0]]
                 self.line.close()
                 self.line.close()
             self.repaint()
             self.repaint()
@@ -220,8 +230,9 @@ class Canvas(QtWidgets.QWidget):
                 self.boundedMoveShapes(self.selectedShapesCopy, pos)
                 self.boundedMoveShapes(self.selectedShapesCopy, pos)
                 self.repaint()
                 self.repaint()
             elif self.selectedShapes:
             elif self.selectedShapes:
-                self.selectedShapesCopy = \
-                    [s.copy() for s in self.selectedShapes]
+                self.selectedShapesCopy = [
+                    s.copy() for s in self.selectedShapes
+                ]
                 self.repaint()
                 self.repaint()
             return
             return
 
 
@@ -268,7 +279,8 @@ class Canvas(QtWidgets.QWidget):
                 self.prevhShape = self.hShape = shape
                 self.prevhShape = self.hShape = shape
                 self.prevhEdge = self.hEdge = index_edge
                 self.prevhEdge = self.hEdge = index_edge
                 self.setToolTip(
                 self.setToolTip(
-                    self.tr("Click & drag to move shape '%s'") % shape.label)
+                    self.tr("Click & drag to move shape '%s'") % shape.label
+                )
                 self.setStatusTip(self.toolTip())
                 self.setStatusTip(self.toolTip())
                 self.overrideCursor(CURSOR_GRAB)
                 self.overrideCursor(CURSOR_GRAB)
                 self.update()
                 self.update()
@@ -313,16 +325,16 @@ class Canvas(QtWidgets.QWidget):
             if self.drawing():
             if self.drawing():
                 if self.current:
                 if self.current:
                     # Add point to existing shape.
                     # Add point to existing shape.
-                    if self.createMode == 'polygon':
+                    if self.createMode == "polygon":
                         self.current.addPoint(self.line[1])
                         self.current.addPoint(self.line[1])
                         self.line[0] = self.current[-1]
                         self.line[0] = self.current[-1]
                         if self.current.isClosed():
                         if self.current.isClosed():
                             self.finalise()
                             self.finalise()
-                    elif self.createMode in ['rectangle', 'circle', 'line']:
+                    elif self.createMode in ["rectangle", "circle", "line"]:
                         assert len(self.current.points) == 1
                         assert len(self.current.points) == 1
                         self.current.points = self.line.points
                         self.current.points = self.line.points
                         self.finalise()
                         self.finalise()
-                    elif self.createMode == 'linestrip':
+                    elif self.createMode == "linestrip":
                         self.current.addPoint(self.line[1])
                         self.current.addPoint(self.line[1])
                         self.line[0] = self.current[-1]
                         self.line[0] = self.current[-1]
                         if int(ev.modifiers()) == QtCore.Qt.ControlModifier:
                         if int(ev.modifiers()) == QtCore.Qt.ControlModifier:
@@ -331,22 +343,22 @@ class Canvas(QtWidgets.QWidget):
                     # Create new shape.
                     # Create new shape.
                     self.current = Shape(shape_type=self.createMode)
                     self.current = Shape(shape_type=self.createMode)
                     self.current.addPoint(pos)
                     self.current.addPoint(pos)
-                    if self.createMode == 'point':
+                    if self.createMode == "point":
                         self.finalise()
                         self.finalise()
                     else:
                     else:
-                        if self.createMode == 'circle':
-                            self.current.shape_type = 'circle'
+                        if self.createMode == "circle":
+                            self.current.shape_type = "circle"
                         self.line.points = [pos, pos]
                         self.line.points = [pos, pos]
                         self.setHiding()
                         self.setHiding()
                         self.drawingPolygon.emit(True)
                         self.drawingPolygon.emit(True)
                         self.update()
                         self.update()
             else:
             else:
-                group_mode = (int(ev.modifiers()) == QtCore.Qt.ControlModifier)
+                group_mode = int(ev.modifiers()) == QtCore.Qt.ControlModifier
                 self.selectShapePoint(pos, multiple_selection_mode=group_mode)
                 self.selectShapePoint(pos, multiple_selection_mode=group_mode)
                 self.prevPoint = pos
                 self.prevPoint = pos
                 self.repaint()
                 self.repaint()
         elif ev.button() == QtCore.Qt.RightButton and self.editing():
         elif ev.button() == QtCore.Qt.RightButton and self.editing():
-            group_mode = (int(ev.modifiers()) == QtCore.Qt.ControlModifier)
+            group_mode = int(ev.modifiers()) == QtCore.Qt.ControlModifier
             self.selectShapePoint(pos, multiple_selection_mode=group_mode)
             self.selectShapePoint(pos, multiple_selection_mode=group_mode)
             self.prevPoint = pos
             self.prevPoint = pos
             self.repaint()
             self.repaint()
@@ -355,8 +367,10 @@ class Canvas(QtWidgets.QWidget):
         if ev.button() == QtCore.Qt.RightButton:
         if ev.button() == QtCore.Qt.RightButton:
             menu = self.menus[len(self.selectedShapesCopy) > 0]
             menu = self.menus[len(self.selectedShapesCopy) > 0]
             self.restoreCursor()
             self.restoreCursor()
-            if not menu.exec_(self.mapToGlobal(ev.pos())) \
-                    and self.selectedShapesCopy:
+            if (
+                not menu.exec_(self.mapToGlobal(ev.pos()))
+                and self.selectedShapesCopy
+            ):
                 # Cancel the move by deleting the shadow copy.
                 # Cancel the move by deleting the shadow copy.
                 self.selectedShapesCopy = []
                 self.selectedShapesCopy = []
                 self.repaint()
                 self.repaint()
@@ -365,8 +379,10 @@ class Canvas(QtWidgets.QWidget):
 
 
         if self.movingShape and self.hShape:
         if self.movingShape and self.hShape:
             index = self.shapes.index(self.hShape)
             index = self.shapes.index(self.hShape)
-            if (self.shapesBackups[-1][index].points !=
-                    self.shapes[index].points):
+            if (
+                self.shapesBackups[-1][index].points
+                != self.shapes[index].points
+            ):
                 self.storeShapes()
                 self.storeShapes()
                 self.shapeMoved.emit()
                 self.shapeMoved.emit()
 
 
@@ -405,8 +421,11 @@ class Canvas(QtWidgets.QWidget):
     def mouseDoubleClickEvent(self, ev):
     def mouseDoubleClickEvent(self, ev):
         # We need at least 4 points here, since the mousePress handler
         # We need at least 4 points here, since the mousePress handler
         # adds an extra one before this handler is called.
         # adds an extra one before this handler is called.
-        if (self.double_click == 'close' and self.canCloseShape() and
-                len(self.current) > 3):
+        if (
+            self.double_click == "close"
+            and self.canCloseShape()
+            and len(self.current) > 3
+        ):
             self.current.popPoint()
             self.current.popPoint()
             self.finalise()
             self.finalise()
 
 
@@ -428,7 +447,8 @@ class Canvas(QtWidgets.QWidget):
                     if multiple_selection_mode:
                     if multiple_selection_mode:
                         if shape not in self.selectedShapes:
                         if shape not in self.selectedShapes:
                             self.selectionChanged.emit(
                             self.selectionChanged.emit(
-                                self.selectedShapes + [shape])
+                                self.selectedShapes + [shape]
+                            )
                     else:
                     else:
                         self.selectionChanged.emit([shape])
                         self.selectionChanged.emit([shape])
                     return
                     return
@@ -457,8 +477,10 @@ class Canvas(QtWidgets.QWidget):
             pos -= QtCore.QPoint(min(0, o1.x()), min(0, o1.y()))
             pos -= QtCore.QPoint(min(0, o1.x()), min(0, o1.y()))
         o2 = pos + self.offsets[1]
         o2 = pos + self.offsets[1]
         if self.outOfPixmap(o2):
         if self.outOfPixmap(o2):
-            pos += QtCore.QPoint(min(0, self.pixmap.width() - o2.x()),
-                                 min(0, self.pixmap.height() - o2.y()))
+            pos += QtCore.QPoint(
+                min(0, self.pixmap.width() - o2.x()),
+                min(0, self.pixmap.height() - o2.y()),
+            )
         # XXX: The next line tracks the new position of the cursor
         # XXX: The next line tracks the new position of the cursor
         # relative to the shape, but also results in making it
         # relative to the shape, but also results in making it
         # a bit "shaky" when nearing the border and allows it to
         # a bit "shaky" when nearing the border and allows it to
@@ -522,8 +544,9 @@ class Canvas(QtWidgets.QWidget):
         p.drawPixmap(0, 0, self.pixmap)
         p.drawPixmap(0, 0, self.pixmap)
         Shape.scale = self.scale
         Shape.scale = self.scale
         for shape in self.shapes:
         for shape in self.shapes:
-            if (shape.selected or not self._hideBackround) and \
-                    self.isVisible(shape):
+            if (shape.selected or not self._hideBackround) and self.isVisible(
+                shape
+            ):
                 shape.fill = shape.selected or shape == self.hShape
                 shape.fill = shape.selected or shape == self.hShape
                 shape.paint(p)
                 shape.paint(p)
         if self.current:
         if self.current:
@@ -533,8 +556,12 @@ class Canvas(QtWidgets.QWidget):
             for s in self.selectedShapesCopy:
             for s in self.selectedShapesCopy:
                 s.paint(p)
                 s.paint(p)
 
 
-        if (self.fillDrawing() and self.createMode == 'polygon' and
-                self.current is not None and len(self.current.points) >= 2):
+        if (
+            self.fillDrawing()
+            and self.createMode == "polygon"
+            and self.current is not None
+            and len(self.current.points) >= 2
+        ):
             drawing_shape = self.current.copy()
             drawing_shape = self.current.copy()
             drawing_shape.addPoint(self.line[1])
             drawing_shape.addPoint(self.line[1])
             drawing_shape.fill = True
             drawing_shape.fill = True
@@ -582,10 +609,12 @@ class Canvas(QtWidgets.QWidget):
         # and find the one intersecting the current line segment.
         # and find the one intersecting the current line segment.
         # http://paulbourke.net/geometry/lineline2d/
         # http://paulbourke.net/geometry/lineline2d/
         size = self.pixmap.size()
         size = self.pixmap.size()
-        points = [(0, 0),
-                  (size.width() - 1, 0),
-                  (size.width() - 1, size.height() - 1),
-                  (0, size.height() - 1)]
+        points = [
+            (0, 0),
+            (size.width() - 1, 0),
+            (size.width() - 1, size.height() - 1),
+            (0, size.height() - 1),
+        ]
         # x1, y1 should be in the pixmap, x2, y2 should be out of the pixmap
         # x1, y1 should be in the pixmap, x2, y2 should be out of the pixmap
         x1 = min(max(p1.x(), 0), size.width() - 1)
         x1 = min(max(p1.x(), 0), size.width() - 1)
         y1 = min(max(p1.y(), 0), size.height() - 1)
         y1 = min(max(p1.y(), 0), size.height() - 1)
@@ -663,7 +692,8 @@ class Canvas(QtWidgets.QWidget):
                         ev.delta(),
                         ev.delta(),
                         QtCore.Qt.Horizontal
                         QtCore.Qt.Horizontal
                         if (QtCore.Qt.ShiftModifier == int(mods))
                         if (QtCore.Qt.ShiftModifier == int(mods))
-                        else QtCore.Qt.Vertical)
+                        else QtCore.Qt.Vertical,
+                    )
             else:
             else:
                 self.scrollRequest.emit(ev.delta(), QtCore.Qt.Horizontal)
                 self.scrollRequest.emit(ev.delta(), QtCore.Qt.Horizontal)
         ev.accept()
         ev.accept()
@@ -689,11 +719,11 @@ class Canvas(QtWidgets.QWidget):
         assert self.shapes
         assert self.shapes
         self.current = self.shapes.pop()
         self.current = self.shapes.pop()
         self.current.setOpen()
         self.current.setOpen()
-        if self.createMode in ['polygon', 'linestrip']:
+        if self.createMode in ["polygon", "linestrip"]:
             self.line.points = [self.current[-1], self.current[0]]
             self.line.points = [self.current[-1], self.current[0]]
-        elif self.createMode in ['rectangle', 'line', 'circle']:
+        elif self.createMode in ["rectangle", "line", "circle"]:
             self.current.points = self.current.points[0:1]
             self.current.points = self.current.points[0:1]
-        elif self.createMode == 'point':
+        elif self.createMode == "point":
             self.current = None
             self.current = None
         self.drawingPolygon.emit(True)
         self.drawingPolygon.emit(True)
 
 

+ 4 - 3
labelme/widgets/color_dialog.py

@@ -2,7 +2,6 @@ from qtpy import QtWidgets
 
 
 
 
 class ColorDialog(QtWidgets.QColorDialog):
 class ColorDialog(QtWidgets.QColorDialog):
-
     def __init__(self, parent=None):
     def __init__(self, parent=None):
         super(ColorDialog, self).__init__(parent)
         super(ColorDialog, self).__init__(parent)
         self.setOption(QtWidgets.QColorDialog.ShowAlphaChannel)
         self.setOption(QtWidgets.QColorDialog.ShowAlphaChannel)
@@ -25,6 +24,8 @@ class ColorDialog(QtWidgets.QColorDialog):
         return self.currentColor() if self.exec_() else None
         return self.currentColor() if self.exec_() else None
 
 
     def checkRestore(self, button):
     def checkRestore(self, button):
-        if self.bb.buttonRole(button) & \
-                QtWidgets.QDialogButtonBox.ResetRole and self.default:
+        if (
+            self.bb.buttonRole(button) & QtWidgets.QDialogButtonBox.ResetRole
+            and self.default
+        ):
             self.setCurrentColor(self.default)
             self.setCurrentColor(self.default)

+ 0 - 1
labelme/widgets/escapable_qlist_widget.py

@@ -3,7 +3,6 @@ from qtpy import QtWidgets
 
 
 
 
 class EscapableQListWidget(QtWidgets.QListWidget):
 class EscapableQListWidget(QtWidgets.QListWidget):
-
     def keyPressEvent(self, event):
     def keyPressEvent(self, event):
         super(EscapableQListWidget, self).keyPressEvent(event)
         super(EscapableQListWidget, self).keyPressEvent(event)
         if event.key() == Qt.Key_Escape:
         if event.key() == Qt.Key_Escape:

+ 31 - 24
labelme/widgets/label_dialog.py

@@ -9,7 +9,7 @@ from labelme.logger import logger
 import labelme.utils
 import labelme.utils
 
 
 
 
-QT5 = QT_VERSION[0] == '5'
+QT5 = QT_VERSION[0] == "5"
 
 
 
 
 # TODO(unknown):
 # TODO(unknown):
@@ -17,7 +17,6 @@ QT5 = QT_VERSION[0] == '5'
 
 
 
 
 class LabelQLineEdit(QtWidgets.QLineEdit):
 class LabelQLineEdit(QtWidgets.QLineEdit):
-
     def setListWidget(self, list_widget):
     def setListWidget(self, list_widget):
         self.list_widget = list_widget
         self.list_widget = list_widget
 
 
@@ -29,12 +28,19 @@ class LabelQLineEdit(QtWidgets.QLineEdit):
 
 
 
 
 class LabelDialog(QtWidgets.QDialog):
 class LabelDialog(QtWidgets.QDialog):
-
-    def __init__(self, text="Enter object label", parent=None, labels=None,
-                 sort_labels=True, show_text_field=True,
-                 completion='startswith', fit_to_content=None, flags=None):
+    def __init__(
+        self,
+        text="Enter object label",
+        parent=None,
+        labels=None,
+        sort_labels=True,
+        show_text_field=True,
+        completion="startswith",
+        fit_to_content=None,
+        flags=None,
+    ):
         if fit_to_content is None:
         if fit_to_content is None:
-            fit_to_content = {'row': False, 'column': True}
+            fit_to_content = {"row": False, "column": True}
         self._fit_to_content = fit_to_content
         self._fit_to_content = fit_to_content
 
 
         super(LabelDialog, self).__init__(parent)
         super(LabelDialog, self).__init__(parent)
@@ -45,9 +51,9 @@ class LabelDialog(QtWidgets.QDialog):
         if flags:
         if flags:
             self.edit.textChanged.connect(self.updateFlags)
             self.edit.textChanged.connect(self.updateFlags)
         self.edit_group_id = QtWidgets.QLineEdit()
         self.edit_group_id = QtWidgets.QLineEdit()
-        self.edit_group_id.setPlaceholderText('Group ID')
+        self.edit_group_id.setPlaceholderText("Group ID")
         self.edit_group_id.setValidator(
         self.edit_group_id.setValidator(
-            QtGui.QRegExpValidator(QtCore.QRegExp(r'\d*'), None)
+            QtGui.QRegExpValidator(QtCore.QRegExp(r"\d*"), None)
         )
         )
         layout = QtWidgets.QVBoxLayout()
         layout = QtWidgets.QVBoxLayout()
         if show_text_field:
         if show_text_field:
@@ -61,18 +67,18 @@ class LabelDialog(QtWidgets.QDialog):
             QtCore.Qt.Horizontal,
             QtCore.Qt.Horizontal,
             self,
             self,
         )
         )
-        bb.button(bb.Ok).setIcon(labelme.utils.newIcon('done'))
-        bb.button(bb.Cancel).setIcon(labelme.utils.newIcon('undo'))
+        bb.button(bb.Ok).setIcon(labelme.utils.newIcon("done"))
+        bb.button(bb.Cancel).setIcon(labelme.utils.newIcon("undo"))
         bb.accepted.connect(self.validate)
         bb.accepted.connect(self.validate)
         bb.rejected.connect(self.reject)
         bb.rejected.connect(self.reject)
         layout.addWidget(bb)
         layout.addWidget(bb)
         # label_list
         # label_list
         self.labelList = QtWidgets.QListWidget()
         self.labelList = QtWidgets.QListWidget()
-        if self._fit_to_content['row']:
+        if self._fit_to_content["row"]:
             self.labelList.setHorizontalScrollBarPolicy(
             self.labelList.setHorizontalScrollBarPolicy(
                 QtCore.Qt.ScrollBarAlwaysOff
                 QtCore.Qt.ScrollBarAlwaysOff
             )
             )
-        if self._fit_to_content['column']:
+        if self._fit_to_content["column"]:
             self.labelList.setVerticalScrollBarPolicy(
             self.labelList.setVerticalScrollBarPolicy(
                 QtCore.Qt.ScrollBarAlwaysOff
                 QtCore.Qt.ScrollBarAlwaysOff
             )
             )
@@ -83,7 +89,8 @@ class LabelDialog(QtWidgets.QDialog):
             self.labelList.sortItems()
             self.labelList.sortItems()
         else:
         else:
             self.labelList.setDragDropMode(
             self.labelList.setDragDropMode(
-                QtWidgets.QAbstractItemView.InternalMove)
+                QtWidgets.QAbstractItemView.InternalMove
+            )
         self.labelList.currentItemChanged.connect(self.labelSelected)
         self.labelList.currentItemChanged.connect(self.labelSelected)
         self.labelList.itemDoubleClicked.connect(self.labelDoubleClicked)
         self.labelList.itemDoubleClicked.connect(self.labelDoubleClicked)
         self.edit.setListWidget(self.labelList)
         self.edit.setListWidget(self.labelList)
@@ -99,21 +106,21 @@ class LabelDialog(QtWidgets.QDialog):
         self.setLayout(layout)
         self.setLayout(layout)
         # completion
         # completion
         completer = QtWidgets.QCompleter()
         completer = QtWidgets.QCompleter()
-        if not QT5 and completion != 'startswith':
+        if not QT5 and completion != "startswith":
             logger.warn(
             logger.warn(
                 "completion other than 'startswith' is only "
                 "completion other than 'startswith' is only "
                 "supported with Qt5. Using 'startswith'"
                 "supported with Qt5. Using 'startswith'"
             )
             )
-            completion = 'startswith'
-        if completion == 'startswith':
+            completion = "startswith"
+        if completion == "startswith":
             completer.setCompletionMode(QtWidgets.QCompleter.InlineCompletion)
             completer.setCompletionMode(QtWidgets.QCompleter.InlineCompletion)
             # Default settings.
             # Default settings.
             # completer.setFilterMode(QtCore.Qt.MatchStartsWith)
             # completer.setFilterMode(QtCore.Qt.MatchStartsWith)
-        elif completion == 'contains':
+        elif completion == "contains":
             completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)
             completer.setCompletionMode(QtWidgets.QCompleter.PopupCompletion)
             completer.setFilterMode(QtCore.Qt.MatchContains)
             completer.setFilterMode(QtCore.Qt.MatchContains)
         else:
         else:
-            raise ValueError('Unsupported completion: {}'.format(completion))
+            raise ValueError("Unsupported completion: {}".format(completion))
         completer.setModel(self.labelList.model())
         completer.setModel(self.labelList.model())
         self.edit.setCompleter(completer)
         self.edit.setCompleter(completer)
 
 
@@ -129,7 +136,7 @@ class LabelDialog(QtWidgets.QDialog):
 
 
     def validate(self):
     def validate(self):
         text = self.edit.text()
         text = self.edit.text()
-        if hasattr(text, 'strip'):
+        if hasattr(text, "strip"):
             text = text.strip()
             text = text.strip()
         else:
         else:
             text = text.trimmed()
             text = text.trimmed()
@@ -141,7 +148,7 @@ class LabelDialog(QtWidgets.QDialog):
 
 
     def postProcess(self):
     def postProcess(self):
         text = self.edit.text()
         text = self.edit.text()
-        if hasattr(text, 'strip'):
+        if hasattr(text, "strip"):
             text = text.strip()
             text = text.strip()
         else:
         else:
             text = text.trimmed()
             text = text.trimmed()
@@ -164,7 +171,7 @@ class LabelDialog(QtWidgets.QDialog):
             self.flagsLayout.removeWidget(item)
             self.flagsLayout.removeWidget(item)
             item.setParent(None)
             item.setParent(None)
 
 
-    def resetFlags(self, label=''):
+    def resetFlags(self, label=""):
         flags = {}
         flags = {}
         for pattern, keys in self._flags.items():
         for pattern, keys in self._flags.items():
             if re.match(pattern, label):
             if re.match(pattern, label):
@@ -194,11 +201,11 @@ class LabelDialog(QtWidgets.QDialog):
         return None
         return None
 
 
     def popUp(self, text=None, move=True, flags=None, group_id=None):
     def popUp(self, text=None, move=True, flags=None, group_id=None):
-        if self._fit_to_content['row']:
+        if self._fit_to_content["row"]:
             self.labelList.setMinimumHeight(
             self.labelList.setMinimumHeight(
                 self.labelList.sizeHintForRow(0) * self.labelList.count() + 2
                 self.labelList.sizeHintForRow(0) * self.labelList.count() + 2
             )
             )
-        if self._fit_to_content['column']:
+        if self._fit_to_content["column"]:
             self.labelList.setMinimumWidth(
             self.labelList.setMinimumWidth(
                 self.labelList.sizeHintForColumn(0) + 2
                 self.labelList.sizeHintForColumn(0) + 2
             )
             )

+ 0 - 1
labelme/widgets/tool_bar.py

@@ -3,7 +3,6 @@ from qtpy import QtWidgets
 
 
 
 
 class ToolBar(QtWidgets.QToolBar):
 class ToolBar(QtWidgets.QToolBar):
-
     def __init__(self, title):
     def __init__(self, title):
         super(ToolBar, self).__init__(title)
         super(ToolBar, self).__init__(title)
         layout = self.layout()
         layout = self.layout()

+ 4 - 4
labelme/widgets/unique_label_qlist_widget.py

@@ -7,7 +7,6 @@ from .escapable_qlist_widget import EscapableQListWidget
 
 
 
 
 class UniqueLabelQListWidget(EscapableQListWidget):
 class UniqueLabelQListWidget(EscapableQListWidget):
-
     def mousePressEvent(self, event):
     def mousePressEvent(self, event):
         super(UniqueLabelQListWidget, self).mousePressEvent(event)
         super(UniqueLabelQListWidget, self).mousePressEvent(event)
         if not self.indexAt(event.pos()).isValid():
         if not self.indexAt(event.pos()).isValid():
@@ -29,11 +28,12 @@ class UniqueLabelQListWidget(EscapableQListWidget):
     def setItemLabel(self, item, label, color=None):
     def setItemLabel(self, item, label, color=None):
         qlabel = QtWidgets.QLabel()
         qlabel = QtWidgets.QLabel()
         if color is None:
         if color is None:
-            qlabel.setText('{}'.format(label))
+            qlabel.setText("{}".format(label))
         else:
         else:
             qlabel.setText(
             qlabel.setText(
-                '{} <font color="#{:02x}{:02x}{:02x}">●</font>'
-                .format(label, *color)
+                '{} <font color="#{:02x}{:02x}{:02x}">●</font>'.format(
+                    label, *color
+                )
             )
             )
         qlabel.setAlignment(Qt.AlignBottom)
         qlabel.setAlignment(Qt.AlignBottom)
 
 

+ 2 - 3
labelme/widgets/zoom_widget.py

@@ -4,14 +4,13 @@ from qtpy import QtWidgets
 
 
 
 
 class ZoomWidget(QtWidgets.QSpinBox):
 class ZoomWidget(QtWidgets.QSpinBox):
-
     def __init__(self, value=100):
     def __init__(self, value=100):
         super(ZoomWidget, self).__init__()
         super(ZoomWidget, self).__init__()
         self.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
         self.setButtonSymbols(QtWidgets.QAbstractSpinBox.NoButtons)
         self.setRange(1, 1000)
         self.setRange(1, 1000)
-        self.setSuffix(' %')
+        self.setSuffix(" %")
         self.setValue(value)
         self.setValue(value)
-        self.setToolTip('Zoom Level')
+        self.setToolTip("Zoom Level")
         self.setStatusTip(self.toolTip())
         self.setStatusTip(self.toolTip())
         self.setAlignment(QtCore.Qt.AlignCenter)
         self.setAlignment(QtCore.Qt.AlignCenter)
 
 

+ 10 - 0
pyproject.toml

@@ -0,0 +1,10 @@
+[tool.black]
+line-length = 79
+exclude = '''
+(
+    ^/\..*
+  | ^/docs/
+  | ^/build/
+  | ^/github2pypi/
+)
+'''

+ 64 - 54
setup.py

@@ -5,15 +5,21 @@ import re
 from setuptools import find_packages
 from setuptools import find_packages
 from setuptools import setup
 from setuptools import setup
 import shlex
 import shlex
+import site
 import subprocess
 import subprocess
 import sys
 import sys
 
 
 
 
+# XXX: with pyproject.toml and without --no-use-pep517,
+# sitepackages are not inserted to sys.path by default.
+sys.path.extend(site.getsitepackages())
+
+
 def get_version():
 def get_version():
-    filename = 'labelme/__init__.py'
+    filename = "labelme/__init__.py"
     with open(filename) as f:
     with open(filename) as f:
         match = re.search(
         match = re.search(
-            r'''^__version__ = ['"]([^'"]*)['"]''', f.read(), re.M
+            r"""^__version__ = ['"]([^'"]*)['"]""", f.read(), re.M
         )
         )
     if not match:
     if not match:
         raise RuntimeError("{} doesn't contain __version__".format(filename))
         raise RuntimeError("{} doesn't contain __version__".format(filename))
@@ -27,13 +33,13 @@ def get_install_requires():
     assert PY3 or PY2
     assert PY3 or PY2
 
 
     install_requires = [
     install_requires = [
-        'imgviz>=0.11.0',
-        'matplotlib',
-        'numpy',
-        'Pillow>=2.8.0',
-        'PyYAML',
-        'qtpy',
-        'termcolor',
+        "imgviz>=0.11.0",
+        "matplotlib",
+        "numpy",
+        "Pillow>=2.8.0",
+        "PyYAML",
+        "qtpy",
+        "termcolor",
     ]
     ]
 
 
     # Find python binding for qt with priority:
     # Find python binding for qt with priority:
@@ -43,45 +49,49 @@ def get_install_requires():
 
 
     try:
     try:
         import PyQt5  # NOQA
         import PyQt5  # NOQA
-        QT_BINDING = 'pyqt5'
+
+        QT_BINDING = "pyqt5"
     except ImportError:
     except ImportError:
         pass
         pass
 
 
     if QT_BINDING is None:
     if QT_BINDING is None:
         try:
         try:
             import PySide2  # NOQA
             import PySide2  # NOQA
-            QT_BINDING = 'pyside2'
+
+            QT_BINDING = "pyside2"
         except ImportError:
         except ImportError:
             pass
             pass
 
 
     if QT_BINDING is None:
     if QT_BINDING is None:
         try:
         try:
             import PyQt4  # NOQA
             import PyQt4  # NOQA
-            QT_BINDING = 'pyqt4'
+
+            QT_BINDING = "pyqt4"
         except ImportError:
         except ImportError:
             if PY2:
             if PY2:
                 print(
                 print(
-                    'Please install PyQt5, PySide2 or PyQt4 for Python2.\n'
-                    'Note that PyQt5 can be installed via pip for Python3.',
+                    "Please install PyQt5, PySide2 or PyQt4 for Python2.\n"
+                    "Note that PyQt5 can be installed via pip for Python3.",
                     file=sys.stderr,
                     file=sys.stderr,
                 )
                 )
                 sys.exit(1)
                 sys.exit(1)
             assert PY3
             assert PY3
             # PyQt5 can be installed via pip for Python3
             # PyQt5 can be installed via pip for Python3
-            install_requires.append('PyQt5')
-            QT_BINDING = 'pyqt5'
+            install_requires.append("PyQt5")
+            QT_BINDING = "pyqt5"
     del QT_BINDING
     del QT_BINDING
 
 
     return install_requires
     return install_requires
 
 
 
 
 def get_long_description():
 def get_long_description():
-    with open('README.md') as f:
+    with open("README.md") as f:
         long_description = f.read()
         long_description = f.read()
     try:
     try:
         import github2pypi
         import github2pypi
+
         return github2pypi.replace_url(
         return github2pypi.replace_url(
-            slug='wkentaro/labelme', content=long_description
+            slug="wkentaro/labelme", content=long_description
         )
         )
     except Exception:
     except Exception:
         return long_description
         return long_description
@@ -90,63 +100,63 @@ def get_long_description():
 def main():
 def main():
     version = get_version()
     version = get_version()
 
 
-    if sys.argv[1] == 'release':
-        if not distutils.spawn.find_executable('twine'):
+    if sys.argv[1] == "release":
+        if not distutils.spawn.find_executable("twine"):
             print(
             print(
-                'Please install twine:\n\n\tpip install twine\n',
+                "Please install twine:\n\n\tpip install twine\n",
                 file=sys.stderr,
                 file=sys.stderr,
             )
             )
             sys.exit(1)
             sys.exit(1)
 
 
         commands = [
         commands = [
-            'python tests/docs_tests/man_tests/test_labelme_1.py',
-            'git tag v{:s}'.format(version),
-            'git push origin master --tag',
-            'python setup.py sdist',
-            'twine upload dist/labelme-{:s}.tar.gz'.format(version),
+            "python tests/docs_tests/man_tests/test_labelme_1.py",
+            "git tag v{:s}".format(version),
+            "git push origin master --tag",
+            "python setup.py sdist",
+            "twine upload dist/labelme-{:s}.tar.gz".format(version),
         ]
         ]
         for cmd in commands:
         for cmd in commands:
             subprocess.check_call(shlex.split(cmd))
             subprocess.check_call(shlex.split(cmd))
         sys.exit(0)
         sys.exit(0)
 
 
     setup(
     setup(
-        name='labelme',
+        name="labelme",
         version=version,
         version=version,
-        packages=find_packages(exclude=['github2pypi']),
-        description='Image Polygonal Annotation with Python',
+        packages=find_packages(exclude=["github2pypi"]),
+        description="Image Polygonal Annotation with Python",
         long_description=get_long_description(),
         long_description=get_long_description(),
-        long_description_content_type='text/markdown',
-        author='Kentaro Wada',
-        author_email='www.kentaro.wada@gmail.com',
-        url='https://github.com/wkentaro/labelme',
+        long_description_content_type="text/markdown",
+        author="Kentaro Wada",
+        author_email="www.kentaro.wada@gmail.com",
+        url="https://github.com/wkentaro/labelme",
         install_requires=get_install_requires(),
         install_requires=get_install_requires(),
-        license='GPLv3',
-        keywords='Image Annotation, Machine Learning',
+        license="GPLv3",
+        keywords="Image Annotation, Machine Learning",
         classifiers=[
         classifiers=[
-            'Development Status :: 5 - Production/Stable',
-            'Intended Audience :: Developers',
-            'Natural Language :: English',
-            'Programming Language :: Python',
-            'Programming Language :: Python :: 2.7',
-            'Programming Language :: Python :: 3.5',
-            'Programming Language :: Python :: 3.6',
-            'Programming Language :: Python :: 3.7',
-            'Programming Language :: Python :: Implementation :: CPython',
-            'Programming Language :: Python :: Implementation :: PyPy',
+            "Development Status :: 5 - Production/Stable",
+            "Intended Audience :: Developers",
+            "Natural Language :: English",
+            "Programming Language :: Python",
+            "Programming Language :: Python :: 2.7",
+            "Programming Language :: Python :: 3.5",
+            "Programming Language :: Python :: 3.6",
+            "Programming Language :: Python :: 3.7",
+            "Programming Language :: Python :: Implementation :: CPython",
+            "Programming Language :: Python :: Implementation :: PyPy",
         ],
         ],
-        package_data={'labelme': ['icons/*', 'config/*.yaml']},
+        package_data={"labelme": ["icons/*", "config/*.yaml"]},
         entry_points={
         entry_points={
-            'console_scripts': [
-                'labelme=labelme.__main__:main',
-                'labelme_draw_json=labelme.cli.draw_json:main',
-                'labelme_draw_label_png=labelme.cli.draw_label_png:main',
-                'labelme_json_to_dataset=labelme.cli.json_to_dataset:main',
-                'labelme_on_docker=labelme.cli.on_docker:main',
+            "console_scripts": [
+                "labelme=labelme.__main__:main",
+                "labelme_draw_json=labelme.cli.draw_json:main",
+                "labelme_draw_label_png=labelme.cli.draw_label_png:main",
+                "labelme_json_to_dataset=labelme.cli.json_to_dataset:main",
+                "labelme_on_docker=labelme.cli.on_docker:main",
             ],
             ],
         },
         },
-        data_files=[('share/man/man1', ['docs/man/labelme.1'])],
+        data_files=[("share/man/man1", ["docs/man/labelme.1"])],
     )
     )
 
 
 
 
-if __name__ == '__main__':
+if __name__ == "__main__":
     main()
     main()

+ 8 - 8
tests/docs_tests/man_tests/test_labelme_1.py

@@ -11,17 +11,17 @@ import sys
 
 
 here = osp.dirname(osp.abspath(__file__))
 here = osp.dirname(osp.abspath(__file__))
 
 
-cmd = 'help2man labelme'
+cmd = "help2man labelme"
 man_expected = subprocess.check_output(shlex.split(cmd)).decode().splitlines()
 man_expected = subprocess.check_output(shlex.split(cmd)).decode().splitlines()
 
 
-man_file = osp.realpath(osp.join(here, '../../../docs/man/labelme.1'))
+man_file = osp.realpath(osp.join(here, "../../../docs/man/labelme.1"))
 with open(man_file) as f:
 with open(man_file) as f:
     man_actual = f.read().splitlines()
     man_actual = f.read().splitlines()
 
 
 patterns_exclude = [
 patterns_exclude = [
-    r'^\.TH .*',
-    r'^.*/\.labelmerc\)$',
-    r'^\.\\.*',
+    r"^\.TH .*",
+    r"^.*/\.labelmerc\)$",
+    r"^\.\\.*",
 ]
 ]
 
 
 PASS = 1
 PASS = 1
@@ -31,13 +31,13 @@ for line_expected, line_actual in zip(man_expected, man_actual):
             break
             break
     else:
     else:
         if line_expected != line_actual:
         if line_expected != line_actual:
-            print(repr('> {}'.format(line_expected)), file=sys.stderr)
-            print(repr('< {}'.format(line_actual)), file=sys.stderr)
+            print(repr("> {}".format(line_expected)), file=sys.stderr)
+            print(repr("< {}".format(line_actual)), file=sys.stderr)
             PASS = 0
             PASS = 0
 
 
 if not PASS:
 if not PASS:
     print(
     print(
-        'Please run:\n\n\thelp2man labelme > {}\n'.format(man_file),
+        "Please run:\n\n\thelp2man labelme > {}\n".format(man_file),
         file=sys.stderr,
         file=sys.stderr,
     )
     )
 assert PASS
 assert PASS

+ 20 - 20
tests/labelme_tests/test_app.py

@@ -8,14 +8,14 @@ import labelme.testing
 
 
 
 
 here = osp.dirname(osp.abspath(__file__))
 here = osp.dirname(osp.abspath(__file__))
-data_dir = osp.join(here, 'data')
+data_dir = osp.join(here, "data")
 
 
 
 
 def _win_show_and_wait_imageData(qtbot, win):
 def _win_show_and_wait_imageData(qtbot, win):
     win.show()
     win.show()
 
 
     def check_imageData():
     def check_imageData():
-        assert hasattr(win, 'imageData')
+        assert hasattr(win, "imageData")
         assert win.imageData is not None
         assert win.imageData is not None
 
 
     qtbot.waitUntil(check_imageData)  # wait for loadFile
     qtbot.waitUntil(check_imageData)  # wait for loadFile
@@ -29,7 +29,7 @@ def test_MainWindow_open(qtbot):
 
 
 
 
 def test_MainWindow_open_img(qtbot):
 def test_MainWindow_open_img(qtbot):
-    img_file = osp.join(data_dir, 'raw/2011_000003.jpg')
+    img_file = osp.join(data_dir, "raw/2011_000003.jpg")
     win = labelme.app.MainWindow(filename=img_file)
     win = labelme.app.MainWindow(filename=img_file)
     qtbot.addWidget(win)
     qtbot.addWidget(win)
     _win_show_and_wait_imageData(qtbot, win)
     _win_show_and_wait_imageData(qtbot, win)
@@ -38,8 +38,8 @@ def test_MainWindow_open_img(qtbot):
 
 
 def test_MainWindow_open_json(qtbot):
 def test_MainWindow_open_json(qtbot):
     json_files = [
     json_files = [
-        osp.join(data_dir, 'annotated_with_data/apc2016_obj3.json'),
-        osp.join(data_dir, 'annotated/2011_000003.json'),
+        osp.join(data_dir, "annotated_with_data/apc2016_obj3.json"),
+        osp.join(data_dir, "annotated/2011_000003.json"),
     ]
     ]
     for json_file in json_files:
     for json_file in json_files:
         labelme.testing.assert_labelfile_sanity(json_file)
         labelme.testing.assert_labelfile_sanity(json_file)
@@ -51,7 +51,7 @@ def test_MainWindow_open_json(qtbot):
 
 
 
 
 def test_MainWindow_open_dir(qtbot):
 def test_MainWindow_open_dir(qtbot):
-    directory = osp.join(data_dir, 'raw')
+    directory = osp.join(data_dir, "raw")
     win = labelme.app.MainWindow(filename=directory)
     win = labelme.app.MainWindow(filename=directory)
     qtbot.addWidget(win)
     qtbot.addWidget(win)
     _win_show_and_wait_imageData(qtbot, win)
     _win_show_and_wait_imageData(qtbot, win)
@@ -70,33 +70,33 @@ def test_MainWindow_openPrevImg(qtbot):
 
 
 def test_MainWindow_annotate_jpg(qtbot):
 def test_MainWindow_annotate_jpg(qtbot):
     tmp_dir = tempfile.mkdtemp()
     tmp_dir = tempfile.mkdtemp()
-    input_file = osp.join(data_dir, 'raw/2011_000003.jpg')
-    out_file = osp.join(tmp_dir, '2011_000003.json')
+    input_file = osp.join(data_dir, "raw/2011_000003.jpg")
+    out_file = osp.join(tmp_dir, "2011_000003.json")
 
 
     config = labelme.config.get_default_config()
     config = labelme.config.get_default_config()
     win = labelme.app.MainWindow(
     win = labelme.app.MainWindow(
-        config=config,
-        filename=input_file,
-        output_file=out_file,
+        config=config, filename=input_file, output_file=out_file,
     )
     )
     qtbot.addWidget(win)
     qtbot.addWidget(win)
     _win_show_and_wait_imageData(qtbot, win)
     _win_show_and_wait_imageData(qtbot, win)
 
 
-    label = 'whole'
+    label = "whole"
     points = [
     points = [
         (100, 100),
         (100, 100),
         (100, 238),
         (100, 238),
         (400, 238),
         (400, 238),
         (400, 100),
         (400, 100),
     ]
     ]
-    shapes = [dict(
-        label=label,
-        group_id=None,
-        points=points,
-        shape_type='polygon',
-        flags={},
-        other_data={}
-    )]
+    shapes = [
+        dict(
+            label=label,
+            group_id=None,
+            points=points,
+            shape_type="polygon",
+            flags={},
+            other_data={},
+        )
+    ]
     win.loadLabels(shapes)
     win.loadLabels(shapes)
     win.saveFile()
     win.saveFile()
 
 

+ 3 - 3
tests/labelme_tests/utils_tests/test_image.py

@@ -16,7 +16,7 @@ def test_img_b64_to_arr():
 
 
 
 
 def test_img_arr_to_b64():
 def test_img_arr_to_b64():
-    img_file = osp.join(data_dir, 'annotated_with_data/apc2016_obj3.jpg')
+    img_file = osp.join(data_dir, "annotated_with_data/apc2016_obj3.jpg")
     img_arr = np.asarray(PIL.Image.open(img_file))
     img_arr = np.asarray(PIL.Image.open(img_file))
     img_b64 = image_module.img_arr_to_b64(img_arr)
     img_b64 = image_module.img_arr_to_b64(img_arr)
     img_arr2 = image_module.img_b64_to_arr(img_b64)
     img_arr2 = image_module.img_b64_to_arr(img_b64)
@@ -24,8 +24,8 @@ def test_img_arr_to_b64():
 
 
 
 
 def test_img_data_to_png_data():
 def test_img_data_to_png_data():
-    img_file = osp.join(data_dir, 'annotated_with_data/apc2016_obj3.jpg')
-    with open(img_file, 'rb') as f:
+    img_file = osp.join(data_dir, "annotated_with_data/apc2016_obj3.jpg")
+    with open(img_file, "rb") as f:
         img_data = f.read()
         img_data = f.read()
     png_data = image_module.img_data_to_png_data(img_data)
     png_data = image_module.img_data_to_png_data(img_data)
     assert isinstance(png_data, bytes)
     assert isinstance(png_data, bytes)

+ 6 - 5
tests/labelme_tests/utils_tests/test_shape.py

@@ -6,18 +6,19 @@ from labelme.utils import shape as shape_module
 def test_shapes_to_label():
 def test_shapes_to_label():
     img, data = get_img_and_data()
     img, data = get_img_and_data()
     label_name_to_value = {}
     label_name_to_value = {}
-    for shape in data['shapes']:
-        label_name = shape['label']
+    for shape in data["shapes"]:
+        label_name = shape["label"]
         label_value = len(label_name_to_value)
         label_value = len(label_name_to_value)
         label_name_to_value[label_name] = label_value
         label_name_to_value[label_name] = label_value
     cls, _ = shape_module.shapes_to_label(
     cls, _ = shape_module.shapes_to_label(
-        img.shape, data['shapes'], label_name_to_value)
+        img.shape, data["shapes"], label_name_to_value
+    )
     assert cls.shape == img.shape[:2]
     assert cls.shape == img.shape[:2]
 
 
 
 
 def test_shape_to_mask():
 def test_shape_to_mask():
     img, data = get_img_and_data()
     img, data = get_img_and_data()
-    for shape in data['shapes']:
-        points = shape['points']
+    for shape in data["shapes"]:
+        points = shape["points"]
         mask = shape_module.shape_to_mask(img.shape[:2], points)
         mask = shape_module.shape_to_mask(img.shape[:2], points)
         assert mask.shape == img.shape[:2]
         assert mask.shape == img.shape[:2]

+ 7 - 7
tests/labelme_tests/utils_tests/util.py

@@ -6,13 +6,13 @@ from labelme.utils import shape as shape_module
 
 
 
 
 here = osp.dirname(osp.abspath(__file__))
 here = osp.dirname(osp.abspath(__file__))
-data_dir = osp.join(here, '../data')
+data_dir = osp.join(here, "../data")
 
 
 
 
 def get_img_and_data():
 def get_img_and_data():
-    json_file = osp.join(data_dir, 'annotated_with_data/apc2016_obj3.json')
+    json_file = osp.join(data_dir, "annotated_with_data/apc2016_obj3.json")
     data = json.load(open(json_file))
     data = json.load(open(json_file))
-    img_b64 = data['imageData']
+    img_b64 = data["imageData"]
     img = image_module.img_b64_to_arr(img_b64)
     img = image_module.img_b64_to_arr(img_b64)
     return img, data
     return img, data
 
 
@@ -20,9 +20,9 @@ def get_img_and_data():
 def get_img_and_lbl():
 def get_img_and_lbl():
     img, data = get_img_and_data()
     img, data = get_img_and_data()
 
 
-    label_name_to_value = {'__background__': 0}
-    for shape in data['shapes']:
-        label_name = shape['label']
+    label_name_to_value = {"__background__": 0}
+    for shape in data["shapes"]:
+        label_name = shape["label"]
         label_value = len(label_name_to_value)
         label_value = len(label_name_to_value)
         label_name_to_value[label_name] = label_value
         label_name_to_value[label_name] = label_value
 
 
@@ -32,6 +32,6 @@ def get_img_and_lbl():
         label_names[label_value] = label_name
         label_names[label_value] = label_name
 
 
     lbl, _ = shape_module.shapes_to_label(
     lbl, _ = shape_module.shapes_to_label(
-        img.shape, data['shapes'], label_name_to_value
+        img.shape, data["shapes"], label_name_to_value
     )
     )
     return img, lbl, label_names
     return img, lbl, label_names

+ 20 - 20
tests/labelme_tests/widgets_tests/test_label_dialog.py

@@ -7,21 +7,17 @@ from labelme.widgets import LabelQLineEdit
 
 
 def test_LabelQLineEdit(qtbot):
 def test_LabelQLineEdit(qtbot):
     list_widget = QtWidgets.QListWidget()
     list_widget = QtWidgets.QListWidget()
-    list_widget.addItems([
-        'cat',
-        'dog',
-        'person',
-    ])
+    list_widget.addItems(["cat", "dog", "person"])
     widget = LabelQLineEdit()
     widget = LabelQLineEdit()
     widget.setListWidget(list_widget)
     widget.setListWidget(list_widget)
     qtbot.addWidget(widget)
     qtbot.addWidget(widget)
 
 
     # key press to navigate in label list
     # key press to navigate in label list
-    item = widget.list_widget.findItems('cat', QtCore.Qt.MatchExactly)[0]
+    item = widget.list_widget.findItems("cat", QtCore.Qt.MatchExactly)[0]
     widget.list_widget.setCurrentItem(item)
     widget.list_widget.setCurrentItem(item)
-    assert widget.list_widget.currentItem().text() == 'cat'
+    assert widget.list_widget.currentItem().text() == "cat"
     qtbot.keyPress(widget, QtCore.Qt.Key_Down)
     qtbot.keyPress(widget, QtCore.Qt.Key_Down)
-    assert widget.list_widget.currentItem().text() == 'dog'
+    assert widget.list_widget.currentItem().text() == "dog"
 
 
     # key press to enter label
     # key press to enter label
     qtbot.keyPress(widget, QtCore.Qt.Key_P)
     qtbot.keyPress(widget, QtCore.Qt.Key_P)
@@ -30,37 +26,39 @@ def test_LabelQLineEdit(qtbot):
     qtbot.keyPress(widget, QtCore.Qt.Key_S)
     qtbot.keyPress(widget, QtCore.Qt.Key_S)
     qtbot.keyPress(widget, QtCore.Qt.Key_O)
     qtbot.keyPress(widget, QtCore.Qt.Key_O)
     qtbot.keyPress(widget, QtCore.Qt.Key_N)
     qtbot.keyPress(widget, QtCore.Qt.Key_N)
-    assert widget.text() == 'person'
+    assert widget.text() == "person"
 
 
 
 
 def test_LabelDialog_addLabelHistory(qtbot):
 def test_LabelDialog_addLabelHistory(qtbot):
-    labels = ['cat', 'dog', 'person']
+    labels = ["cat", "dog", "person"]
     widget = LabelDialog(labels=labels, sort_labels=True)
     widget = LabelDialog(labels=labels, sort_labels=True)
     qtbot.addWidget(widget)
     qtbot.addWidget(widget)
 
 
-    widget.addLabelHistory('bicycle')
+    widget.addLabelHistory("bicycle")
     assert widget.labelList.count() == 4
     assert widget.labelList.count() == 4
-    widget.addLabelHistory('bicycle')
+    widget.addLabelHistory("bicycle")
     assert widget.labelList.count() == 4
     assert widget.labelList.count() == 4
     item = widget.labelList.item(0)
     item = widget.labelList.item(0)
-    assert item.text() == 'bicycle'
+    assert item.text() == "bicycle"
 
 
 
 
 def test_LabelDialog_popUp(qtbot):
 def test_LabelDialog_popUp(qtbot):
-    labels = ['cat', 'dog', 'person']
+    labels = ["cat", "dog", "person"]
     widget = LabelDialog(labels=labels, sort_labels=True)
     widget = LabelDialog(labels=labels, sort_labels=True)
     qtbot.addWidget(widget)
     qtbot.addWidget(widget)
 
 
     # popUp(text='cat')
     # popUp(text='cat')
 
 
     def interact():
     def interact():
-        qtbot.keyClick(widget.edit, QtCore.Qt.Key_P)  # enter 'p' for 'person'  # NOQA
+        qtbot.keyClick(
+            widget.edit, QtCore.Qt.Key_P
+        )  # enter 'p' for 'person'  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
 
 
     QtCore.QTimer.singleShot(500, interact)
     QtCore.QTimer.singleShot(500, interact)
-    label, flags, group_id = widget.popUp('cat')
-    assert label == 'person'
+    label, flags, group_id = widget.popUp("cat")
+    assert label == "person"
     assert flags == {}
     assert flags == {}
     assert group_id is None
     assert group_id is None
 
 
@@ -72,19 +70,21 @@ def test_LabelDialog_popUp(qtbot):
 
 
     QtCore.QTimer.singleShot(500, interact)
     QtCore.QTimer.singleShot(500, interact)
     label, flags, group_id = widget.popUp()
     label, flags, group_id = widget.popUp()
-    assert label == 'person'
+    assert label == "person"
     assert flags == {}
     assert flags == {}
     assert group_id is None
     assert group_id is None
 
 
     # popUp() + key_Up
     # popUp() + key_Up
 
 
     def interact():
     def interact():
-        qtbot.keyClick(widget.edit, QtCore.Qt.Key_Up)  # 'person' -> 'dog'  # NOQA
+        qtbot.keyClick(
+            widget.edit, QtCore.Qt.Key_Up
+        )  # 'person' -> 'dog'  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
         qtbot.keyClick(widget.edit, QtCore.Qt.Key_Enter)  # NOQA
 
 
     QtCore.QTimer.singleShot(500, interact)
     QtCore.QTimer.singleShot(500, interact)
     label, flags, group_id = widget.popUp()
     label, flags, group_id = widget.popUp()
-    assert label == 'dog'
+    assert label == "dog"
     assert flags == {}
     assert flags == {}
     assert group_id is None
     assert group_id is None

Some files were not shown because too many files changed in this diff