├── labelImg-master
├── data
│ └── predefined_classes.txt
├── tests
│ ├── 臉書.jpg
│ ├── test.bmp
│ └── test.py
├── demo
│ ├── demo.png
│ ├── demo2.png
│ └── demo3.jpg
├── icons
│ ├── close.png
│ ├── color.png
│ ├── copy.png
│ ├── done.png
│ ├── edit.png
│ ├── eye.png
│ ├── file.png
│ ├── fit.png
│ ├── help.png
│ ├── new.png
│ ├── next.png
│ ├── open.png
│ ├── prev.png
│ ├── quit.png
│ ├── save.png
│ ├── undo.png
│ ├── zoom.png
│ ├── cancel.png
│ ├── delete.png
│ ├── expert1.png
│ ├── expert2.png
│ ├── labels.png
│ ├── objects.png
│ ├── save-as.png
│ ├── zoom-in.png
│ ├── color_line.png
│ ├── fit-width.png
│ ├── fit-window.png
│ ├── undo-cross.png
│ ├── zoom-out.png
│ ├── feBlend-icon.png
│ ├── open.svg
│ ├── done.svg
│ ├── save.svg
│ └── labels.svg
├── contributors.txt
├── build-tools
│ ├── .gitignore
│ ├── build-ubuntu-binary.sh
│ ├── build-windows-binary.sh
│ └── envsetup.sh
├── .gitignore
├── _init_path.py
├── Makefile
├── libs
│ ├── zoomWidget.py
│ ├── toolBar.py
│ ├── colorDialog.py
│ ├── lib.py
│ ├── labelDialog.py
│ ├── labelFile.py
│ ├── labelFile.py~
│ ├── pascal_voc_io.py
│ ├── pascal_voc_io.py~
│ ├── shape.py
│ └── canvas.py
├── LICENSE.md
├── resources.qrc
├── README.md
└── .travis.yml
├── get_img_names.py
├── make_main_txt.py
├── change_img_name.py
└── README.md
/labelImg-master/data/predefined_classes.txt:
--------------------------------------------------------------------------------
1 | dog
2 | person
3 | cat
4 | tv
5 | car
6 |
--------------------------------------------------------------------------------
/labelImg-master/tests/臉書.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/tests/臉書.jpg
--------------------------------------------------------------------------------
/labelImg-master/demo/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/demo/demo.png
--------------------------------------------------------------------------------
/labelImg-master/demo/demo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/demo/demo2.png
--------------------------------------------------------------------------------
/labelImg-master/demo/demo3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/demo/demo3.jpg
--------------------------------------------------------------------------------
/labelImg-master/icons/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/close.png
--------------------------------------------------------------------------------
/labelImg-master/icons/color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/color.png
--------------------------------------------------------------------------------
/labelImg-master/icons/copy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/copy.png
--------------------------------------------------------------------------------
/labelImg-master/icons/done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/done.png
--------------------------------------------------------------------------------
/labelImg-master/icons/edit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/edit.png
--------------------------------------------------------------------------------
/labelImg-master/icons/eye.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/eye.png
--------------------------------------------------------------------------------
/labelImg-master/icons/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/file.png
--------------------------------------------------------------------------------
/labelImg-master/icons/fit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/fit.png
--------------------------------------------------------------------------------
/labelImg-master/icons/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/help.png
--------------------------------------------------------------------------------
/labelImg-master/icons/new.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/new.png
--------------------------------------------------------------------------------
/labelImg-master/icons/next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/next.png
--------------------------------------------------------------------------------
/labelImg-master/icons/open.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/open.png
--------------------------------------------------------------------------------
/labelImg-master/icons/prev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/prev.png
--------------------------------------------------------------------------------
/labelImg-master/icons/quit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/quit.png
--------------------------------------------------------------------------------
/labelImg-master/icons/save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/save.png
--------------------------------------------------------------------------------
/labelImg-master/icons/undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/undo.png
--------------------------------------------------------------------------------
/labelImg-master/icons/zoom.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/zoom.png
--------------------------------------------------------------------------------
/labelImg-master/tests/test.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/tests/test.bmp
--------------------------------------------------------------------------------
/labelImg-master/icons/cancel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/cancel.png
--------------------------------------------------------------------------------
/labelImg-master/icons/delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/delete.png
--------------------------------------------------------------------------------
/labelImg-master/icons/expert1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/expert1.png
--------------------------------------------------------------------------------
/labelImg-master/icons/expert2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/expert2.png
--------------------------------------------------------------------------------
/labelImg-master/icons/labels.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/labels.png
--------------------------------------------------------------------------------
/labelImg-master/icons/objects.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/objects.png
--------------------------------------------------------------------------------
/labelImg-master/icons/save-as.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/save-as.png
--------------------------------------------------------------------------------
/labelImg-master/icons/zoom-in.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/zoom-in.png
--------------------------------------------------------------------------------
/labelImg-master/contributors.txt:
--------------------------------------------------------------------------------
1 | TzuTa Lin
2 | [LabelMe](http://labelme2.csail.mit.edu/Release3.0/index.php)
3 | Ryan Flynn
4 |
--------------------------------------------------------------------------------
/labelImg-master/icons/color_line.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/color_line.png
--------------------------------------------------------------------------------
/labelImg-master/icons/fit-width.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/fit-width.png
--------------------------------------------------------------------------------
/labelImg-master/icons/fit-window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/fit-window.png
--------------------------------------------------------------------------------
/labelImg-master/icons/undo-cross.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/undo-cross.png
--------------------------------------------------------------------------------
/labelImg-master/icons/zoom-out.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/zoom-out.png
--------------------------------------------------------------------------------
/labelImg-master/icons/feBlend-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/EddyGao/make_VOC2007/HEAD/labelImg-master/icons/feBlend-icon.png
--------------------------------------------------------------------------------
/labelImg-master/build-tools/.gitignore:
--------------------------------------------------------------------------------
1 | *.spec
2 | build
3 | dist
4 | pyinstaller
5 | python-2.*
6 | pywin32*
7 | virtual-wine
8 | venv_wine
9 | PyQt4-*
10 | lxml-*
11 |
--------------------------------------------------------------------------------
/labelImg-master/.gitignore:
--------------------------------------------------------------------------------
1 | icons/.DS_Store
2 |
3 | resources.py
4 |
5 | *.pyc
6 | .*.swp
7 |
8 | build/
9 | dist/
10 |
11 | tags
12 | cscope*
13 | .ycm_extra_conf.py
14 | .subvimrc
15 |
--------------------------------------------------------------------------------
/labelImg-master/_init_path.py:
--------------------------------------------------------------------------------
1 | """Set up paths"""
2 | import sys
3 |
4 |
5 | def add_path(path):
6 | if path not in sys.path:
7 | sys.path.insert(0, path)
8 |
9 | add_path('libs')
10 |
--------------------------------------------------------------------------------
/labelImg-master/tests/test.py:
--------------------------------------------------------------------------------
1 |
2 | from unittest import TestCase
3 |
4 | from labelImg import get_main_app
5 |
6 |
7 | class TestMainWindow(TestCase):
8 |
9 | app = None
10 | win = None
11 |
12 | def setUp(self):
13 | self.app, self.win = get_main_app()
14 |
15 | def tearDown(self):
16 | self.win.close()
17 | self.app.quit()
18 |
19 | def test_noop(self):
20 | pass
21 |
--------------------------------------------------------------------------------
/labelImg-master/Makefile:
--------------------------------------------------------------------------------
1 | # ex: set ts=8 noet:
2 |
3 | all: qt4
4 |
5 | test: testpy2
6 |
7 | testpy2:
8 | python -m unittest discover tests
9 |
10 | testpy3:
11 | python3 -m unittest discover tests
12 |
13 | qt4: qt4py2
14 |
15 | qt5: qt4py3
16 |
17 | qt4py2:
18 | pyrcc4 -py2 -o resources.py resources.qrc
19 |
20 | qt4py3:
21 | pyrcc4 -py3 -o resources.py resources.qrc
22 |
23 | qt5py3:
24 | pyrcc5 -o resources.py resources.qrc
25 |
26 | .PHONY: test
27 |
--------------------------------------------------------------------------------
/labelImg-master/build-tools/build-ubuntu-binary.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ### Ubuntu use pyinstall v3.0
3 | THIS_SCRIPT_PATH=`readlink -f $0`
4 | THIS_SCRIPT_DIR=`dirname ${THIS_SCRIPT_PATH}`
5 | cd pyinstaller
6 | git checkout v3.2
7 | cd ${THIS_SCRIPT_DIR}
8 |
9 | rm -r build
10 | rm -r dist
11 | rm labelImg.spec
12 | python pyinstaller/pyinstaller.py --hidden-import=xml \
13 | --hidden-import=xml.etree \
14 | --hidden-import=xml.etree.ElementTree \
15 | --hidden-import=lxml.etree \
16 | -D -F -n labelImg -c "../labelImg.py" -p ../libs
17 |
18 | FOLDER=$(git describe --abbrev=0 --tags)
19 | FOLDER="linux_"$FOLDER
20 | rm -rf "$FOLDER"
21 | mkdir "$FOLDER"
22 | cp dist/labelImg $FOLDER
23 | cp -rf ../data $FOLDER/data
24 | zip "$FOLDER.zip" -r $FOLDER
25 |
--------------------------------------------------------------------------------
/labelImg-master/build-tools/build-windows-binary.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | ### Window requires pyinstall v2.1
3 | THIS_SCRIPT_PATH=`readlink -f $0`
4 | THIS_SCRIPT_DIR=`dirname ${THIS_SCRIPT_PATH}`
5 | cd pyinstaller
6 | git checkout v2.1
7 | cd ${THIS_SCRIPT_DIR}
8 |
9 | . venv_wine/bin/activate
10 | rm -r build
11 | rm -r dist
12 | rm labelImg.spec
13 | wine c:/Python27/python.exe pyinstaller/pyinstaller.py --hidden-import=xml \
14 | --hidden-import=xml.etree \
15 | --hidden-import=xml.etree.ElementTree \
16 | --hidden-import=lxml.etree \
17 | -D -F -n labelImg -c "../labelImg.py" -p ../libs
18 |
19 | FOLDER=$(git describe --abbrev=0 --tags)
20 | FOLDER="windows_"$FOLDER
21 | rm -rf "$FOLDER"
22 | mkdir "$FOLDER"
23 | cp dist/labelImg.exe $FOLDER
24 | cp -rf ../data $FOLDER/data
25 | zip "$FOLDER.zip" -r $FOLDER
26 |
--------------------------------------------------------------------------------
/labelImg-master/libs/zoomWidget.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PyQt5.QtGui import *
3 | from PyQt5.QtCore import *
4 | from PyQt5.QtWidgets import *
5 | except ImportError:
6 | from PyQt4.QtGui import *
7 | from PyQt4.QtCore import *
8 |
9 |
10 | class ZoomWidget(QSpinBox):
11 |
12 | def __init__(self, value=100):
13 | super(ZoomWidget, self).__init__()
14 | self.setButtonSymbols(QAbstractSpinBox.NoButtons)
15 | self.setRange(1, 500)
16 | self.setSuffix(' %')
17 | self.setValue(value)
18 | self.setToolTip(u'Zoom Level')
19 | self.setStatusTip(self.toolTip())
20 | self.setAlignment(Qt.AlignCenter)
21 |
22 | def minimumSizeHint(self):
23 | height = super(ZoomWidget, self).minimumSizeHint().height()
24 | fm = QFontMetrics(self.font())
25 | width = fm.width(str(self.maximum()))
26 | return QSize(width, height)
27 |
--------------------------------------------------------------------------------
/get_img_names.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | def ListFilesToTxt(dir,file,wildcard,recursion):
4 | exts = wildcard.split(" ")
5 | files = os.listdir(dir)
6 | for name in files:
7 | fullname=os.path.join(dir,name)
8 | if(os.path.isdir(fullname) & recursion):
9 | ListFilesToTxt(fullname,file,wildcard,recursion)
10 | else:
11 | for ext in exts:
12 | if(name.endswith('.jpg')):
13 | name = name.split('.')[0]
14 | file.write(name + "\n")
15 | break
16 |
17 | def Test():
18 | dir="/home/ghz/fast-rcnn/data/VOCdevkit/VOC2007/JPEGImages"
19 | outfile="allnames.txt"
20 | wildcard = ".txt .exe .dll .lib"
21 |
22 | file = open(outfile,"w")
23 | if not file:
24 | print ("cannot open the file %s for writing" % outfile)
25 |
26 | ListFilesToTxt(dir,file,wildcard, 1)
27 |
28 | file.close()
29 |
30 | Test()
31 |
--------------------------------------------------------------------------------
/make_main_txt.py:
--------------------------------------------------------------------------------
1 | import os
2 | import random
3 |
4 | trainval_percent = 0.66
5 | train_percent = 0.5
6 | xmlfilepath = 'Annotations'
7 | txtsavepath = 'ImageSets\Main'
8 | total_xml = os.listdir(xmlfilepath)
9 |
10 | num=len(total_xml)
11 | list=range(num)
12 | tv=int(num*trainval_percent)
13 | tr=int(tv*train_percent)
14 | trainval= random.sample(list,tv)
15 | train=random.sample(trainval,tr)
16 |
17 | ftrainval = open('ImageSets/Main/trainval.txt', 'w')
18 | ftest = open('ImageSets/Main/test.txt', 'w')
19 | ftrain = open('ImageSets/Main/train.txt', 'w')
20 | fval = open('ImageSets/Main/val.txt', 'w')
21 |
22 | for i in list:
23 | name=total_xml[i][:-4]+'\n'
24 | if i in trainval:
25 | ftrainval.write(name)
26 | if i in train:
27 | ftrain.write(name)
28 | else:
29 | fval.write(name)
30 | else:
31 | ftest.write(name)
32 |
33 | ftrainval.close()
34 | ftrain.close()
35 | fval.close()
36 | ftest .close()
37 |
--------------------------------------------------------------------------------
/change_img_name.py:
--------------------------------------------------------------------------------
1 | # -*- coding:utf8 -*-
2 |
3 | import os
4 |
5 | class BatchRename():
6 | '''
7 | 批量重命名文件夹中的图片文件
8 |
9 | '''
10 | def __init__(self):
11 | self.path = '/home/ghz/mydata/VOCdevkit/VOC2007/JPEGImages'
12 |
13 | def rename(self):
14 | filelist = os.listdir(self.path)
15 | total_num = len(filelist)
16 | i = 1
17 | n = 6
18 | for item in filelist:
19 | if item.endswith('.jpg'):
20 | n = 6 - len(str(i))
21 | src = os.path.join(os.path.abspath(self.path), item)
22 | dst = os.path.join(os.path.abspath(self.path), str(0)*n + str(i) + '.jpg')
23 | try:
24 | os.rename(src, dst)
25 | print 'converting %s to %s ...' % (src, dst)
26 | i = i + 1
27 |
28 | except:
29 | continue
30 | print 'total %d to rename & converted %d jpgs' % (total_num, i)
31 |
32 | if __name__ == '__main__':
33 | demo = BatchRename()
34 | demo.rename()
35 |
--------------------------------------------------------------------------------
/labelImg-master/LICENSE.md:
--------------------------------------------------------------------------------
1 | Copyright (c) <2015-2016> Tzutalin
2 |
3 | Copyright (C) 2013 MIT, Computer Science and Artificial Intelligence Laboratory. Bryan Russell, Antonio Torralba, William T. Freeman
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 相信看这篇文章的人都在做深度学习吧,此数据集是为目标检测做的数据集,有错误处请海涵
2 | 我的本篇博客地址
3 | http://blog.csdn.net/gaohuazhao/article/details/60871886
4 | 第一步:首先了解VOC2007数据集的格式
5 |
6 | 1)JPEGImages文件夹
7 |
8 | 文件夹里包含了训练图片和测试图片,混放在一起
9 |
10 | 2)Annatations文件夹
11 |
12 | 文件夹存放的是xml格式的标签文件,每个xml文件都对应于JPEGImages文件夹的一张图片
13 |
14 | 3)ImageSets文件夹
15 |
16 | Action存放的是人的动作,我们暂时不用
17 |
18 | Layout存放的人体部位的数据。我们暂时不用
19 |
20 | Main存放的是图像物体识别的数据,分为20类,当然我们自己制作就呵呵呵不一定了,如果你有精力,Main里面有test.txt , train.txt, val.txt ,trainval.txt.这四个文件我们后面会生成
21 |
22 | Segmentation存放的是可用于分割的数据
23 |
24 | 4)其他的文件夹不解释了,分割XXX等用的
25 |
26 | 如果你下载了VOC2007数据集,那么把它解压,把各个文件夹里面的东西删除,保留文件夹名字。如果没下载,那么就仿照他的文件夹格式,自己建好空文件夹就行。
27 |
28 |
29 | 第二步:搞定JPEGSImages文件夹
30 |
31 | 1)把你的图片放到JPEGSImages里面,在VOC2007里面,人家的图片文件名都是000001.jpg类似这样的,我们也统一格式,把我们的图片名字重命名成这样的,如果你的文件太多怎么办,请看我的另一篇文章http://blog.csdn.NET/gaohuazhao/article/details/60324715 能批量重命名文件
32 |
33 | 第三步:搞定Annatations文件夹
34 |
35 | 网上很多教程,但是我觉得都很麻烦,直到我遇到了一位大神做的软件,手动标注,会自动生成图片信息的xml文件
36 |
37 | 1)本项目中的labelImg-master,执行labelImg.py
38 |
39 | 2)保存的路径就是我们的Annatations文件夹,别保存别的地方去了,,,
40 |
41 | 3)一张张的慢慢画框。。。。。。。。。大约过了几个小时,好继续下一步
42 |
43 | 第四步:搞定ImageSets文件夹中的Main文件夹中的四个文件
44 |
45 | 直接上一个代码给你:
46 | make_main_txt.py
47 |
48 | OK,制作完成,就是这么简单,那么解释一下这四个txt文档是干嘛的,看名字就知道,就是分分多少图片作为训练,多少图片作为测试,,,,
49 |
50 |
51 | 我们将继续填坑
52 |
--------------------------------------------------------------------------------
/labelImg-master/resources.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | icons/help.png
5 | icons/expert2.png
6 | icons/expert2.png
7 | icons/done.png
8 | icons/file.png
9 | icons/labels.png
10 | icons/objects.png
11 | icons/close.png
12 | icons/fit-width.png
13 | icons/fit-window.png
14 | icons/undo.png
15 | icons/eye.png
16 | icons/quit.png
17 | icons/copy.png
18 | icons/edit.png
19 | icons/open.png
20 | icons/save.png
21 | icons/save-as.png
22 | icons/color.png
23 | icons/color_line.png
24 | icons/zoom.png
25 | icons/zoom-in.png
26 | icons/zoom-out.png
27 | icons/cancel.png
28 | icons/next.png
29 | icons/prev.png
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/labelImg-master/libs/toolBar.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PyQt5.QtGui import *
3 | from PyQt5.QtCore import *
4 | from PyQt5.QtWidgets import *
5 | except ImportError:
6 | from PyQt4.QtGui import *
7 | from PyQt4.QtCore import *
8 |
9 |
10 | class ToolBar(QToolBar):
11 |
12 | def __init__(self, title):
13 | super(ToolBar, self).__init__(title)
14 | layout = self.layout()
15 | m = (0, 0, 0, 0)
16 | layout.setSpacing(0)
17 | layout.setContentsMargins(*m)
18 | self.setContentsMargins(*m)
19 | self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint)
20 |
21 | def addAction(self, action):
22 | if isinstance(action, QWidgetAction):
23 | return super(ToolBar, self).addAction(action)
24 | btn = ToolButton()
25 | btn.setDefaultAction(action)
26 | btn.setToolButtonStyle(self.toolButtonStyle())
27 | self.addWidget(btn)
28 |
29 |
30 | class ToolButton(QToolButton):
31 | """ToolBar companion class which ensures all buttons have the same size."""
32 | minSize = (60, 60)
33 |
34 | def minimumSizeHint(self):
35 | ms = super(ToolButton, self).minimumSizeHint()
36 | w1, h1 = ms.width(), ms.height()
37 | w2, h2 = self.minSize
38 | ToolButton.minSize = max(w1, w2), max(h1, h2)
39 | return QSize(*ToolButton.minSize)
40 |
--------------------------------------------------------------------------------
/labelImg-master/libs/colorDialog.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PyQt5.QtGui import *
3 | from PyQt5.QtCore import *
4 | from PyQt5.QtWidgets import QColorDialog, QDialogButtonBox
5 | except ImportError:
6 | from PyQt4.QtGui import *
7 | from PyQt4.QtCore import *
8 |
9 | BB = QDialogButtonBox
10 |
11 |
12 | class ColorDialog(QColorDialog):
13 |
14 | def __init__(self, parent=None):
15 | super(ColorDialog, self).__init__(parent)
16 | self.setOption(QColorDialog.ShowAlphaChannel)
17 | # The Mac native dialog does not support our restore button.
18 | self.setOption(QColorDialog.DontUseNativeDialog)
19 | # Add a restore defaults button.
20 | # The default is set at invocation time, so that it
21 | # works across dialogs for different elements.
22 | self.default = None
23 | self.bb = self.layout().itemAt(1).widget()
24 | self.bb.addButton(BB.RestoreDefaults)
25 | self.bb.clicked.connect(self.checkRestore)
26 |
27 | def getColor(self, value=None, title=None, default=None):
28 | self.default = default
29 | if title:
30 | self.setWindowTitle(title)
31 | if value:
32 | self.setCurrentColor(value)
33 | return self.currentColor() if self.exec_() else None
34 |
35 | def checkRestore(self, button):
36 | if self.bb.buttonRole(button) & BB.ResetRole and self.default:
37 | self.setCurrentColor(self.default)
38 |
--------------------------------------------------------------------------------
/labelImg-master/build-tools/envsetup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | THIS_SCRIPT_PATH=`readlink -f $0`
4 | THIS_SCRIPT_DIR=`dirname ${THIS_SCRIPT_PATH}`
5 | #OS Ubuntu 14.04
6 | ### Common packages for linux/windows
7 | if [ ! -e "pyinstaller" ]; then
8 | git clone https://github.com/pyinstaller/pyinstaller
9 | cd pyinstaller
10 | git checkout v2.1 -b v2.1
11 | cd ${THIS_SCRIPT_DIR}
12 | fi
13 |
14 | echo "Going to clone and download packages for building windows"
15 | #Pacakges
16 | #> pyinstaller (2.1)
17 | #> wine (1.6.2)
18 | #> virtual-wine (0.1)
19 | #> python-2.7.8.msi
20 | #> pywin32-218.win32-py2.7.exe
21 |
22 | ## tool to install on Ubuntu
23 | #$ sudo apt-get install wine
24 |
25 | ### Clone a repo to create virtual wine env
26 | if [ ! -e "virtual-wine" ]; then
27 | git clone https://github.com/htgoebel/virtual-wine.git
28 | fi
29 |
30 | apt-get install scons
31 | ### Create virtual env
32 | rm -rf venv_wine
33 | ./virtual-wine/vwine-setup venv_wine
34 | #### Active virutal env
35 | . venv_wine/bin/activate
36 |
37 | ### Use wine to install packages to virtual env
38 | if [ ! -e "python-2.7.8.msi" ]; then
39 | wget "https://www.python.org/ftp/python/2.7.8/python-2.7.8.msi"
40 | fi
41 |
42 | if [ ! -e "pywin32-218.win32-py2.7.exe" ]; then
43 | wget "http://nchc.dl.sourceforge.net/project/pywin32/pywin32/Build%20218/pywin32-218.win32-py2.7.exe"
44 | fi
45 |
46 | if [ ! -e "PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe" ]; then
47 | wget "http://nchc.dl.sourceforge.net/project/pyqt/PyQt4/PyQt-4.11.4/PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe"
48 | fi
49 |
50 | if [ ! -e "lxml-3.7.3.win32-py2.7.exe" ]; then
51 | wget "https://pypi.python.org/packages/a3/f6/a28c5cf63873f6c55a3eb7857b736379229b85ba918261d2e88cf886905e/lxml-3.7.3.win32-py2.7.exe#md5=a0f746355876aca4ca5371cb0f1d13ce"
52 | fi
53 |
54 |
55 | wine msiexec -i python-2.7.8.msi
56 | wine pywin32-218.win32-py2.7.exe
57 | wine PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe
58 | wine lxml-3.7.3.win32-py2.7.exe
59 |
--------------------------------------------------------------------------------
/labelImg-master/libs/lib.py:
--------------------------------------------------------------------------------
1 | from math import sqrt
2 |
3 | try:
4 | from PyQt5.QtGui import *
5 | from PyQt5.QtCore import *
6 | from PyQt5.QtWidgets import *
7 | except ImportError:
8 | from PyQt4.QtGui import *
9 | from PyQt4.QtCore import *
10 |
11 |
12 | def newIcon(icon):
13 | return QIcon(':/' + icon)
14 |
15 |
16 | def newButton(text, icon=None, slot=None):
17 | b = QPushButton(text)
18 | if icon is not None:
19 | b.setIcon(newIcon(icon))
20 | if slot is not None:
21 | b.clicked.connect(slot)
22 | return b
23 |
24 |
25 | def newAction(parent, text, slot=None, shortcut=None, icon=None,
26 | tip=None, checkable=False, enabled=True):
27 | """Create a new action and assign callbacks, shortcuts, etc."""
28 | a = QAction(text, parent)
29 | if icon is not None:
30 | a.setIcon(newIcon(icon))
31 | if shortcut is not None:
32 | if isinstance(shortcut, (list, tuple)):
33 | a.setShortcuts(shortcut)
34 | else:
35 | a.setShortcut(shortcut)
36 | if tip is not None:
37 | a.setToolTip(tip)
38 | a.setStatusTip(tip)
39 | if slot is not None:
40 | a.triggered.connect(slot)
41 | if checkable:
42 | a.setCheckable(True)
43 | a.setEnabled(enabled)
44 | return a
45 |
46 |
47 | def addActions(widget, actions):
48 | for action in actions:
49 | if action is None:
50 | widget.addSeparator()
51 | elif isinstance(action, QMenu):
52 | widget.addMenu(action)
53 | else:
54 | widget.addAction(action)
55 |
56 |
57 | def labelValidator():
58 | return QRegExpValidator(QRegExp(r'^[^ \t].+'), None)
59 |
60 |
61 | class struct(object):
62 |
63 | def __init__(self, **kwargs):
64 | self.__dict__.update(kwargs)
65 |
66 |
67 | def distance(p):
68 | return sqrt(p.x() * p.x() + p.y() * p.y())
69 |
70 |
71 | def fmtShortcut(text):
72 | mod, key = text.split('+', 1)
73 | return '%s+%s' % (mod, key)
74 |
--------------------------------------------------------------------------------
/labelImg-master/libs/labelDialog.py:
--------------------------------------------------------------------------------
1 | try:
2 | from PyQt5.QtGui import *
3 | from PyQt5.QtCore import *
4 | from PyQt5.QtWidgets import *
5 | except ImportError:
6 | from PyQt4.QtGui import *
7 | from PyQt4.QtCore import *
8 |
9 | from lib import newIcon, labelValidator
10 |
11 | BB = QDialogButtonBox
12 |
13 |
14 | class LabelDialog(QDialog):
15 |
16 | def __init__(self, text="Enter object label", parent=None, listItem=None):
17 | super(LabelDialog, self).__init__(parent)
18 | self.edit = QLineEdit()
19 | self.edit.setText(text)
20 | self.edit.setValidator(labelValidator())
21 | self.edit.editingFinished.connect(self.postProcess)
22 | layout = QVBoxLayout()
23 | layout.addWidget(self.edit)
24 | self.buttonBox = bb = BB(BB.Ok | BB.Cancel, Qt.Horizontal, self)
25 | bb.button(BB.Ok).setIcon(newIcon('done'))
26 | bb.button(BB.Cancel).setIcon(newIcon('undo'))
27 | bb.accepted.connect(self.validate)
28 | bb.rejected.connect(self.reject)
29 | layout.addWidget(bb)
30 |
31 | if listItem is not None and len(listItem) > 0:
32 | self.listWidget = QListWidget(self)
33 | for item in listItem:
34 | self.listWidget.addItem(item)
35 | self.listWidget.itemDoubleClicked.connect(self.listItemClick)
36 | layout.addWidget(self.listWidget)
37 |
38 | self.setLayout(layout)
39 |
40 | def validate(self):
41 | try:
42 | if self.edit.text().trimmed():
43 | self.accept()
44 | except AttributeError:
45 | # PyQt5: AttributeError: 'str' object has no attribute 'trimmed'
46 | if self.edit.text().strip():
47 | self.accept()
48 |
49 | def postProcess(self):
50 | try:
51 | self.edit.setText(self.edit.text().trimmed())
52 | except AttributeError:
53 | # PyQt5: AttributeError: 'str' object has no attribute 'trimmed'
54 | self.edit.setText(self.edit.text())
55 |
56 | def popUp(self, text='', move=True):
57 | self.edit.setText(text)
58 | self.edit.setSelection(0, len(text))
59 | self.edit.setFocus(Qt.PopupFocusReason)
60 | if move:
61 | self.move(QCursor.pos())
62 | return self.edit.text() if self.exec_() else None
63 |
64 | def listItemClick(self, tQListWidgetItem):
65 | try:
66 | text = tQListWidgetItem.text().trimmed()
67 | except AttributeError:
68 | # PyQt5: AttributeError: 'str' object has no attribute 'trimmed'
69 | text = tQListWidgetItem.text().strip()
70 | self.edit.setText(text)
71 | self.validate()
72 |
--------------------------------------------------------------------------------
/labelImg-master/libs/labelFile.py:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Tzutalin
2 | # Create by TzuTaLin
3 |
4 | try:
5 | from PyQt5.QtGui import QImage
6 | except ImportError:
7 | from PyQt4.QtGui import QImage
8 |
9 | from base64 import b64encode, b64decode
10 | from pascal_voc_io import PascalVocWriter
11 | import os.path
12 | import sys
13 |
14 |
15 | class LabelFileError(Exception):
16 | pass
17 |
18 |
19 | class LabelFile(object):
20 | # It might be changed as window creates
21 | suffix = '.lif'
22 |
23 | def __init__(self, filename=None):
24 | self.shapes = ()
25 | self.imagePath = None
26 | self.imageData = None
27 | if filename is not None:
28 | self.load(filename)
29 |
30 | def savePascalVocFormat(self, filename, shapes, imagePath, imageData,
31 | lineColor=None, fillColor=None, databaseSrc=None):
32 | imgFolderPath = os.path.dirname(imagePath)
33 | imgFolderName = os.path.split(imgFolderPath)[-1]
34 | imgFileName = os.path.basename(imagePath)
35 | imgFileNameWithoutExt = os.path.splitext(imgFileName)[0]
36 | # Read from file path because self.imageData might be empty if saving to
37 | # Pascal format
38 | image = QImage()
39 | image.load(imagePath)
40 | imageShape = [image.height(), image.width(),
41 | 1 if image.isGrayscale() else 3]
42 | writer = PascalVocWriter(imgFolderName, imgFileName,
43 | imageShape)
44 | for shape in shapes:
45 | points = shape['points']
46 | label = shape['label']
47 | bndbox = LabelFile.convertPoints2BndBox(points)
48 | writer.addBndBox(bndbox[0], bndbox[1], bndbox[2], bndbox[3], label)
49 |
50 | writer.save(targetFile=filename)
51 | return
52 |
53 | @staticmethod
54 | def isLabelFile(filename):
55 | fileSuffix = os.path.splitext(filename)[1].lower()
56 | return fileSuffix == LabelFile.suffix
57 |
58 | @staticmethod
59 | def convertPoints2BndBox(points):
60 | xmin = float('inf')
61 | ymin = float('inf')
62 | xmax = float('-inf')
63 | ymax = float('-inf')
64 | for p in points:
65 | x = p[0]
66 | y = p[1]
67 | xmin = min(x, xmin)
68 | ymin = min(y, ymin)
69 | xmax = max(x, xmax)
70 | ymax = max(y, ymax)
71 |
72 | # Martin Kersner, 2015/11/12
73 | # 0-valued coordinates of BB caused an error while
74 | # training faster-rcnn object detector.
75 | if (xmin < 1):
76 | xmin = 1
77 |
78 | if (ymin < 1):
79 | ymin = 1
80 |
81 | return (int(xmin), int(ymin), int(xmax), int(ymax))
82 |
--------------------------------------------------------------------------------
/labelImg-master/libs/labelFile.py~:
--------------------------------------------------------------------------------
1 | # Copyright (c) 2016 Tzutalin
2 | # Create by TzuTaLin
3 |
4 | try:
5 | from PyQt5.QtGui import QImage
6 | except ImportError:
7 | from PyQt4.QtGui import QImage
8 |
9 | from base64 import b64encode, b64decode
10 | from pascal_voc_io import PascalVocWriter
11 | import os.path
12 | import sys
13 |
14 |
15 | class LabelFileError(Exception):
16 | pass
17 |
18 |
19 | class LabelFile(object):
20 | # It might be changed as window creates
21 | suffix = '.lif'
22 |
23 | def __init__(self, filename=None):
24 | self.shapes = ()
25 | self.imagePath = None
26 | self.imageData = None
27 | if filename is not None:
28 | self.load(filename)
29 |
30 | def savePascalVocFormat(self, filename, shapes, imagePath, imageData,
31 | lineColor=None, fillColor=None, databaseSrc=None):
32 | imgFolderPath = os.path.dirname(imagePath)
33 | imgFolderName = os.path.split(imgFolderPath)[-1]
34 | imgFileName = os.path.basename(imagePath)
35 | imgFileNameWithoutExt = os.path.splitext(imgFileName)[0]
36 | # Read from file path because self.imageData might be empty if saving to
37 | # Pascal format
38 | image = QImage()
39 | image.load(imagePath)
40 | imageShape = [image.height(), image.width(),
41 | 1 if image.isGrayscale() else 3]
42 | writer = PascalVocWriter(imgFolderName, imgFileName,
43 | imageShape, localImgPath=imagePath)
44 | for shape in shapes:
45 | points = shape['points']
46 | label = shape['label']
47 | bndbox = LabelFile.convertPoints2BndBox(points)
48 | writer.addBndBox(bndbox[0], bndbox[1], bndbox[2], bndbox[3], label)
49 |
50 | writer.save(targetFile=filename)
51 | return
52 |
53 | @staticmethod
54 | def isLabelFile(filename):
55 | fileSuffix = os.path.splitext(filename)[1].lower()
56 | return fileSuffix == LabelFile.suffix
57 |
58 | @staticmethod
59 | def convertPoints2BndBox(points):
60 | xmin = float('inf')
61 | ymin = float('inf')
62 | xmax = float('-inf')
63 | ymax = float('-inf')
64 | for p in points:
65 | x = p[0]
66 | y = p[1]
67 | xmin = min(x, xmin)
68 | ymin = min(y, ymin)
69 | xmax = max(x, xmax)
70 | ymax = max(y, ymax)
71 |
72 | # Martin Kersner, 2015/11/12
73 | # 0-valued coordinates of BB caused an error while
74 | # training faster-rcnn object detector.
75 | if (xmin < 1):
76 | xmin = 1
77 |
78 | if (ymin < 1):
79 | ymin = 1
80 |
81 | return (int(xmin), int(ymin), int(xmax), int(ymax))
82 |
--------------------------------------------------------------------------------
/labelImg-master/README.md:
--------------------------------------------------------------------------------
1 | # LabelImg
2 |
3 | [](https://travis-ci.org/tzutalin/labelImg)
4 |
5 | LabelImg is a graphical image annotation tool.
6 |
7 | It is written in Python and uses Qt for its graphical interface.
8 |
9 | Annotations are saved as XML files in PASCAL VOC format, the format used by [ImageNet](http://www.image-net.org/).
10 |
11 | 
12 |
13 | [Watch a demo video by author tzutalin](https://youtu.be/p0nR2YsCY_U)
14 |
15 | ## Get it
16 |
17 | ### Download prebuilt binaries
18 |
19 | * Windows
20 | * [Download LabelImg 1.2.2 for Windows](https://raw.githubusercontent.com/tzutalin/LabelImg/gh-pages/windows/windows_v1.2.2.zip)
21 | * [Download LabelImg 1.2.1 for Windows](https://raw.githubusercontent.com/tzutalin/LabelImg/gh-pages/windows/windows_v1.2.1.zip)
22 | * [Download LabelImg 1.2 for Windows](https://raw.githubusercontent.com/tzutalin/LabelImg/gh-pages/windows/windows_v1.2.zip)
23 | * Linux
24 | * [Download LabelImg 1.2.2 for Linux](https://raw.githubusercontent.com/tzutalin/LabelImg/gh-pages/linux/linux_v1.2.2.zip)
25 | * [Download LabelImg 1.2.1 for Linux](https://raw.githubusercontent.com/tzutalin/LabelImg/gh-pages/linux/linux_v1.2.1.zip)
26 | * [Download LabelImg 1.2 for Linux](https://raw.githubusercontent.com/tzutalin/LabelImg/gh-pages/linux/linux_v1.2.zip)
27 | * OS X
28 | * Binaries for OS X are not yet available. Help would be appreciated. At present it must be [built from source](#os-x).
29 |
30 | ### Build from source
31 |
32 | Linux/Ubuntu/Mac requires at least [Python 2.6](http://www.python.org/getit/) and has been tested with [PyQt
33 | 4.8](http://www.riverbankcomputing.co.uk/software/pyqt/intro).
34 |
35 | #### Ubuntu Linux
36 |
37 | sudo apt-get install pyqt4-dev-tools
38 | sudo pip install lxml
39 | make all
40 | ./labelImg.py
41 |
42 | #### OS X
43 |
44 | brew install qt qt4
45 | brew install libxml2
46 | make all
47 | ./labelImg.py
48 |
49 | #### Windows
50 |
51 | Download and setup [Python 2.6 or later](https://www.python.org/downloads/windows/), [PyQt4](https://www.riverbankcomputing.com/software/pyqt/download) and [install lxml](http://lxml.de/installation.html).
52 |
53 | Open cmd and go to [labelImg]
54 |
55 | pyrcc4 -o resources.py resources.qrc
56 | python labelImg.py
57 |
58 |
59 | ## Usage
60 |
61 | ### Steps
62 |
63 | 1. Build and launch using the instructions above.
64 | 2. Click 'Change default saved annotation folder' in Menu/File
65 | 3. Click 'Open Dir'
66 | 4. Click 'Create RectBox'
67 | 5. Click and release left mouse to select a region to annotate the rect box
68 | 6. You can use right mouse to drag the rect box to copy or move it
69 |
70 | The annotation will be saved to the folder you specify.
71 |
72 | You can refer to the below hotkeys to speed up your workflow.
73 |
74 | ### Create pre-defined classes
75 |
76 | You can edit the [data/predefined_classes.txt](https://github.com/tzutalin/labelImg/blob/master/data/predefined_classes.txt) to load pre-defined classes
77 |
78 | ### Hotkeys
79 |
80 | | | |
81 | |----------|------------------------------------------|
82 | | Ctrl + u | Load all of the images from a directory |
83 | | Ctrl + r | Change the default annotation target dir |
84 | | Ctrl + s | Save |
85 | | Ctrl + d | Copy the current label and rect box |
86 | | w | Create a rect box |
87 | | d | Next image |
88 | | a | Previous image |
89 | | del | Delete the selected rect box |
90 | | Ctrl++ | Zoom in |
91 | | Ctrl-- | Zoom out |
92 |
93 | ### How to contribute
94 | Send a pull request
95 |
96 | ### License
97 | [License](LICENSE.md)
98 |
99 | ### Related
100 | 1. [ImageNet Utils](https://github.com/tzutalin/ImageNet_Utils) to download image, create a label text for machine learning, etc
101 |
102 |
--------------------------------------------------------------------------------
/labelImg-master/libs/pascal_voc_io.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf8 -*-
3 | import _init_path
4 | import sys
5 | from xml.etree import ElementTree
6 | from xml.etree.ElementTree import Element, SubElement
7 | from lxml import etree
8 | import codecs
9 |
10 | XML_EXT = '.xml'
11 |
12 |
13 | class PascalVocWriter:
14 |
15 | def __init__(self, foldername, filename, imgSize, databaseSrc='Unknown'):
16 | self.foldername = foldername
17 | self.filename = filename
18 | self.databaseSrc = databaseSrc
19 | self.imgSize = imgSize
20 | self.boxlist = []
21 | #self.localImgPath = localImgPath
22 |
23 | def prettify(self, elem):
24 | """
25 | Return a pretty-printed XML string for the Element.
26 | """
27 | rough_string = ElementTree.tostring(elem, 'utf8')
28 | root = etree.fromstring(rough_string)
29 | return etree.tostring(root, pretty_print=True)
30 |
31 | def genXML(self):
32 | """
33 | Return XML root
34 | """
35 | # Check conditions
36 | if self.filename is None or \
37 | self.foldername is None or \
38 | self.imgSize is None:
39 | return None
40 |
41 | top = Element('annotation')
42 | folder = SubElement(top, 'folder')
43 | folder.text = self.foldername
44 |
45 | filename = SubElement(top, 'filename')
46 | filename.text = self.filename
47 |
48 | #localImgPath = SubElement(top, 'path')
49 | #localImgPath.text = self.localImgPath
50 |
51 | source = SubElement(top, 'source')
52 | database = SubElement(source, 'database')
53 | database.text = self.databaseSrc
54 |
55 | size_part = SubElement(top, 'size')
56 | width = SubElement(size_part, 'width')
57 | height = SubElement(size_part, 'height')
58 | depth = SubElement(size_part, 'depth')
59 | width.text = str(self.imgSize[1])
60 | height.text = str(self.imgSize[0])
61 | if len(self.imgSize) == 3:
62 | depth.text = str(self.imgSize[2])
63 | else:
64 | depth.text = '1'
65 |
66 | segmented = SubElement(top, 'segmented')
67 | segmented.text = '0'
68 | return top
69 |
70 | def addBndBox(self, xmin, ymin, xmax, ymax, name):
71 | bndbox = {'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax}
72 | bndbox['name'] = name
73 | self.boxlist.append(bndbox)
74 |
75 | def appendObjects(self, top):
76 | for each_object in self.boxlist:
77 | object_item = SubElement(top, 'object')
78 | name = SubElement(object_item, 'name')
79 | try:
80 | name.text = unicode(each_object['name'])
81 | except NameError:
82 | # Py3: NameError: name 'unicode' is not defined
83 | name.text = each_object['name']
84 | pose = SubElement(object_item, 'pose')
85 | pose.text = "Unspecified"
86 | truncated = SubElement(object_item, 'truncated')
87 | truncated.text = "0"
88 | difficult = SubElement(object_item, 'difficult')
89 | difficult.text = "0"
90 | bndbox = SubElement(object_item, 'bndbox')
91 | xmin = SubElement(bndbox, 'xmin')
92 | xmin.text = str(each_object['xmin'])
93 | ymin = SubElement(bndbox, 'ymin')
94 | ymin.text = str(each_object['ymin'])
95 | xmax = SubElement(bndbox, 'xmax')
96 | xmax.text = str(each_object['xmax'])
97 | ymax = SubElement(bndbox, 'ymax')
98 | ymax.text = str(each_object['ymax'])
99 |
100 | def save(self, targetFile=None):
101 | root = self.genXML()
102 | self.appendObjects(root)
103 | out_file = None
104 | if targetFile is None:
105 | out_file = codecs.open(
106 | self.filename + XML_EXT, 'w', encoding='utf-8')
107 | else:
108 | out_file = codecs.open(targetFile, 'w', encoding='utf-8')
109 |
110 | prettifyResult = self.prettify(root)
111 | out_file.write(prettifyResult.decode('utf8'))
112 | out_file.close()
113 |
114 |
115 | class PascalVocReader:
116 |
117 | def __init__(self, filepath):
118 | # shapes type:
119 | # [labbel, [(x1,y1), (x2,y2), (x3,y3), (x4,y4)], color, color]
120 | self.shapes = []
121 | self.filepath = filepath
122 | self.parseXML()
123 |
124 | def getShapes(self):
125 | return self.shapes
126 |
127 | def addShape(self, label, bndbox):
128 | xmin = int(bndbox.find('xmin').text)
129 | ymin = int(bndbox.find('ymin').text)
130 | xmax = int(bndbox.find('xmax').text)
131 | ymax = int(bndbox.find('ymax').text)
132 | points = [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]
133 | self.shapes.append((label, points, None, None))
134 |
135 | def parseXML(self):
136 | assert self.filepath.endswith('.xml'), "Unsupport file format"
137 | parser = etree.XMLParser(encoding='utf-8')
138 | xmltree = ElementTree.parse(self.filepath, parser=parser).getroot()
139 | filename = xmltree.find('filename').text
140 |
141 | for object_iter in xmltree.findall('object'):
142 | bndbox = object_iter.find("bndbox")
143 | label = object_iter.find('name').text
144 | self.addShape(label, bndbox)
145 | return True
146 |
147 |
148 | # tempParseReader = PascalVocReader('test.xml')
149 | # print tempParseReader.getShapes()
150 | """
151 | # Test
152 | tmp = PascalVocWriter('temp','test', (10,20,3))
153 | tmp.addBndBox(10,10,20,30,'chair')
154 | tmp.addBndBox(1,1,600,600,'car')
155 | tmp.save()
156 | """
157 |
--------------------------------------------------------------------------------
/labelImg-master/libs/pascal_voc_io.py~:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | # -*- coding: utf8 -*-
3 | import _init_path
4 | import sys
5 | from xml.etree import ElementTree
6 | from xml.etree.ElementTree import Element, SubElement
7 | from lxml import etree
8 | import codecs
9 |
10 | XML_EXT = '.xml'
11 |
12 |
13 | class PascalVocWriter:
14 |
15 | def __init__(self, foldername, filename, imgSize, databaseSrc='Unknown', localImgPath=None):
16 | self.foldername = foldername
17 | self.filename = filename
18 | self.databaseSrc = databaseSrc
19 | self.imgSize = imgSize
20 | self.boxlist = []
21 | self.localImgPath = localImgPath
22 |
23 | def prettify(self, elem):
24 | """
25 | Return a pretty-printed XML string for the Element.
26 | """
27 | rough_string = ElementTree.tostring(elem, 'utf8')
28 | root = etree.fromstring(rough_string)
29 | return etree.tostring(root, pretty_print=True)
30 |
31 | def genXML(self):
32 | """
33 | Return XML root
34 | """
35 | # Check conditions
36 | if self.filename is None or \
37 | self.foldername is None or \
38 | self.imgSize is None:
39 | return None
40 |
41 | top = Element('annotation')
42 | folder = SubElement(top, 'folder')
43 | folder.text = self.foldername
44 |
45 | filename = SubElement(top, 'filename')
46 | filename.text = self.filename
47 |
48 | localImgPath = SubElement(top, 'path')
49 | localImgPath.text = self.localImgPath
50 |
51 | source = SubElement(top, 'source')
52 | database = SubElement(source, 'database')
53 | database.text = self.databaseSrc
54 |
55 | size_part = SubElement(top, 'size')
56 | width = SubElement(size_part, 'width')
57 | height = SubElement(size_part, 'height')
58 | depth = SubElement(size_part, 'depth')
59 | width.text = str(self.imgSize[1])
60 | height.text = str(self.imgSize[0])
61 | if len(self.imgSize) == 3:
62 | depth.text = str(self.imgSize[2])
63 | else:
64 | depth.text = '1'
65 |
66 | segmented = SubElement(top, 'segmented')
67 | segmented.text = '0'
68 | return top
69 |
70 | def addBndBox(self, xmin, ymin, xmax, ymax, name):
71 | bndbox = {'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax}
72 | bndbox['name'] = name
73 | self.boxlist.append(bndbox)
74 |
75 | def appendObjects(self, top):
76 | for each_object in self.boxlist:
77 | object_item = SubElement(top, 'object')
78 | name = SubElement(object_item, 'name')
79 | try:
80 | name.text = unicode(each_object['name'])
81 | except NameError:
82 | # Py3: NameError: name 'unicode' is not defined
83 | name.text = each_object['name']
84 | pose = SubElement(object_item, 'pose')
85 | pose.text = "Unspecified"
86 | truncated = SubElement(object_item, 'truncated')
87 | truncated.text = "0"
88 | difficult = SubElement(object_item, 'difficult')
89 | difficult.text = "0"
90 | bndbox = SubElement(object_item, 'bndbox')
91 | xmin = SubElement(bndbox, 'xmin')
92 | xmin.text = str(each_object['xmin'])
93 | ymin = SubElement(bndbox, 'ymin')
94 | ymin.text = str(each_object['ymin'])
95 | xmax = SubElement(bndbox, 'xmax')
96 | xmax.text = str(each_object['xmax'])
97 | ymax = SubElement(bndbox, 'ymax')
98 | ymax.text = str(each_object['ymax'])
99 |
100 | def save(self, targetFile=None):
101 | root = self.genXML()
102 | self.appendObjects(root)
103 | out_file = None
104 | if targetFile is None:
105 | out_file = codecs.open(
106 | self.filename + XML_EXT, 'w', encoding='utf-8')
107 | else:
108 | out_file = codecs.open(targetFile, 'w', encoding='utf-8')
109 |
110 | prettifyResult = self.prettify(root)
111 | out_file.write(prettifyResult.decode('utf8'))
112 | out_file.close()
113 |
114 |
115 | class PascalVocReader:
116 |
117 | def __init__(self, filepath):
118 | # shapes type:
119 | # [labbel, [(x1,y1), (x2,y2), (x3,y3), (x4,y4)], color, color]
120 | self.shapes = []
121 | self.filepath = filepath
122 | self.parseXML()
123 |
124 | def getShapes(self):
125 | return self.shapes
126 |
127 | def addShape(self, label, bndbox):
128 | xmin = int(bndbox.find('xmin').text)
129 | ymin = int(bndbox.find('ymin').text)
130 | xmax = int(bndbox.find('xmax').text)
131 | ymax = int(bndbox.find('ymax').text)
132 | points = [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)]
133 | self.shapes.append((label, points, None, None))
134 |
135 | def parseXML(self):
136 | assert self.filepath.endswith('.xml'), "Unsupport file format"
137 | parser = etree.XMLParser(encoding='utf-8')
138 | xmltree = ElementTree.parse(self.filepath, parser=parser).getroot()
139 | filename = xmltree.find('filename').text
140 |
141 | for object_iter in xmltree.findall('object'):
142 | bndbox = object_iter.find("bndbox")
143 | label = object_iter.find('name').text
144 | self.addShape(label, bndbox)
145 | return True
146 |
147 |
148 | # tempParseReader = PascalVocReader('test.xml')
149 | # print tempParseReader.getShapes()
150 | """
151 | # Test
152 | tmp = PascalVocWriter('temp','test', (10,20,3))
153 | tmp.addBndBox(10,10,20,30,'chair')
154 | tmp.addBndBox(1,1,600,600,'car')
155 | tmp.save()
156 | """
157 |
--------------------------------------------------------------------------------
/labelImg-master/libs/shape.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/python
2 | # -*- coding: utf-8 -*-
3 |
4 |
5 | try:
6 | from PyQt5.QtGui import *
7 | from PyQt5.QtCore import *
8 | except ImportError:
9 | from PyQt4.QtGui import *
10 | from PyQt4.QtCore import *
11 |
12 | from lib import distance
13 |
14 | DEFAULT_LINE_COLOR = QColor(0, 255, 0, 128)
15 | DEFAULT_FILL_COLOR = QColor(255, 0, 0, 128)
16 | DEFAULT_SELECT_LINE_COLOR = QColor(255, 255, 255)
17 | DEFAULT_SELECT_FILL_COLOR = QColor(0, 128, 255, 155)
18 | DEFAULT_VERTEX_FILL_COLOR = QColor(0, 255, 0, 255)
19 | DEFAULT_HVERTEX_FILL_COLOR = QColor(255, 0, 0)
20 |
21 |
22 | class Shape(object):
23 | P_SQUARE, P_ROUND = range(2)
24 |
25 | MOVE_VERTEX, NEAR_VERTEX = range(2)
26 |
27 | # The following class variables influence the drawing
28 | # of _all_ shape objects.
29 | line_color = DEFAULT_LINE_COLOR
30 | fill_color = DEFAULT_FILL_COLOR
31 | select_line_color = DEFAULT_SELECT_LINE_COLOR
32 | select_fill_color = DEFAULT_SELECT_FILL_COLOR
33 | vertex_fill_color = DEFAULT_VERTEX_FILL_COLOR
34 | hvertex_fill_color = DEFAULT_HVERTEX_FILL_COLOR
35 | point_type = P_ROUND
36 | point_size = 8
37 | scale = 1.0
38 |
39 | def __init__(self, label=None, line_color=None):
40 | self.label = label
41 | self.points = []
42 | self.fill = False
43 | self.selected = False
44 |
45 | self._highlightIndex = None
46 | self._highlightMode = self.NEAR_VERTEX
47 | self._highlightSettings = {
48 | self.NEAR_VERTEX: (4, self.P_ROUND),
49 | self.MOVE_VERTEX: (1.5, self.P_SQUARE),
50 | }
51 |
52 | self._closed = False
53 |
54 | if line_color is not None:
55 | # Override the class line_color attribute
56 | # with an object attribute. Currently this
57 | # is used for drawing the pending line a different color.
58 | self.line_color = line_color
59 |
60 | def close(self):
61 | self._closed = True
62 |
63 | def reachMaxPoints(self):
64 | if len(self.points) >= 4:
65 | return True
66 | return False
67 |
68 | def addPoint(self, point):
69 | if self.points and point == self.points[0]:
70 | self.close()
71 | else:
72 | self.points.append(point)
73 |
74 | def popPoint(self):
75 | if self.points:
76 | return self.points.pop()
77 | return None
78 |
79 | def isClosed(self):
80 | return self._closed
81 |
82 | def setOpen(self):
83 | self._closed = False
84 |
85 | def paint(self, painter):
86 | if self.points:
87 | color = self.select_line_color if self.selected else self.line_color
88 | pen = QPen(color)
89 | # Try using integer sizes for smoother drawing(?)
90 | pen.setWidth(max(1, int(round(2.0 / self.scale))))
91 | painter.setPen(pen)
92 |
93 | line_path = QPainterPath()
94 | vrtx_path = QPainterPath()
95 |
96 | line_path.moveTo(self.points[0])
97 | # Uncommenting the following line will draw 2 paths
98 | # for the 1st vertex, and make it non-filled, which
99 | # may be desirable.
100 | #self.drawVertex(vrtx_path, 0)
101 |
102 | for i, p in enumerate(self.points):
103 | line_path.lineTo(p)
104 | self.drawVertex(vrtx_path, i)
105 | if self.isClosed():
106 | line_path.lineTo(self.points[0])
107 |
108 | painter.drawPath(line_path)
109 | painter.drawPath(vrtx_path)
110 | painter.fillPath(vrtx_path, self.vertex_fill_color)
111 | if self.fill:
112 | color = self.select_fill_color if self.selected else self.fill_color
113 | painter.fillPath(line_path, color)
114 |
115 | def drawVertex(self, path, i):
116 | d = self.point_size / self.scale
117 | shape = self.point_type
118 | point = self.points[i]
119 | if i == self._highlightIndex:
120 | size, shape = self._highlightSettings[self._highlightMode]
121 | d *= size
122 | if self._highlightIndex is not None:
123 | self.vertex_fill_color = self.hvertex_fill_color
124 | else:
125 | self.vertex_fill_color = Shape.vertex_fill_color
126 | if shape == self.P_SQUARE:
127 | path.addRect(point.x() - d / 2, point.y() - d / 2, d, d)
128 | elif shape == self.P_ROUND:
129 | path.addEllipse(point, d / 2.0, d / 2.0)
130 | else:
131 | assert False, "unsupported vertex shape"
132 |
133 | def nearestVertex(self, point, epsilon):
134 | for i, p in enumerate(self.points):
135 | if distance(p - point) <= epsilon:
136 | return i
137 | return None
138 |
139 | def containsPoint(self, point):
140 | return self.makePath().contains(point)
141 |
142 | def makePath(self):
143 | path = QPainterPath(self.points[0])
144 | for p in self.points[1:]:
145 | path.lineTo(p)
146 | return path
147 |
148 | def boundingRect(self):
149 | return self.makePath().boundingRect()
150 |
151 | def moveBy(self, offset):
152 | self.points = [p + offset for p in self.points]
153 |
154 | def moveVertexBy(self, i, offset):
155 | self.points[i] = self.points[i] + offset
156 |
157 | def highlightVertex(self, i, action):
158 | self._highlightIndex = i
159 | self._highlightMode = action
160 |
161 | def highlightClear(self):
162 | self._highlightIndex = None
163 |
164 | def copy(self):
165 | shape = Shape("%s" % self.label)
166 | shape.points = [p for p in self.points]
167 | shape.fill = self.fill
168 | shape.selected = self.selected
169 | shape._closed = self._closed
170 | if self.line_color != Shape.line_color:
171 | shape.line_color = self.line_color
172 | if self.fill_color != Shape.fill_color:
173 | shape.fill_color = self.fill_color
174 | return shape
175 |
176 | def __len__(self):
177 | return len(self.points)
178 |
179 | def __getitem__(self, key):
180 | return self.points[key]
181 |
182 | def __setitem__(self, key, value):
183 | self.points[key] = value
184 |
--------------------------------------------------------------------------------
/labelImg-master/.travis.yml:
--------------------------------------------------------------------------------
1 | # vim: set ts=2 et:
2 |
3 | # run xvfb with 32-bit color
4 | # xvfb-run -s '-screen 0 1600x1200x24+32' command_goes_here
5 |
6 | matrix:
7 | include:
8 |
9 | # Python 2.7 + QT4
10 | - os: linux
11 | dist: trusty
12 | sudo: required
13 | language: generic
14 | python: "2.7"
15 | env:
16 | - QT=4
17 | addons:
18 | apt:
19 | packages:
20 | - cmake
21 | - python-qt4
22 | - pyqt4-dev-tools
23 | - xvfb
24 | before_install:
25 | - sudo pip install lxml
26 | - make qt4py2
27 | - xvfb-run make testpy2
28 |
29 | # Python 2.7 + QT4
30 | - os: linux
31 | dist: trusty
32 | sudo: required
33 | language: generic
34 | python: "2.7"
35 | env:
36 | - QT=4
37 | - CONDA=4.2.0
38 | addons:
39 | apt:
40 | packages:
41 | - cmake
42 | #- python-qt4
43 | #- pyqt4-dev-tools
44 | - xvfb
45 | before_install:
46 | # ref: https://www.continuum.io/downloads
47 | - curl -O https://repo.continuum.io/archive/Anaconda2-4.2.0-Linux-x86_64.sh
48 | # ref: http://conda.pydata.org/docs/help/silent.html
49 | - /bin/bash Anaconda2-4.2.0-Linux-x86_64.sh -b -p $HOME/anaconda2
50 | - export PATH="$HOME/anaconda2/bin:$PATH"
51 | # ref: http://stackoverflow.com/questions/21637922/how-to-install-pyqt4-in-anaconda
52 | - conda create -y -n labelImg-py2qt4 python=2.7
53 | - source activate labelImg-py2qt4
54 | - conda install -y pyqt=4
55 | - conda install -y lxml
56 | - make qt4py2
57 | - xvfb-run make testpy2
58 |
59 | # Python 2 + QT5
60 | # disabled; can't get it to work
61 | #- os: linux
62 | # dist: trusty
63 | # sudo: required
64 | # language: generic
65 | # python: "2.7"
66 | # env:
67 | # - QT=5
68 | # addons:
69 | # apt:
70 | # packages:
71 | # - cmake
72 | # - pyqt5-dev-tools
73 | # - xvfb
74 | # before_install:
75 | # - sudo apt-get update
76 | # - sudo apt-get install -y python-pip
77 | # - sudo pip install lxml
78 | # - pyrcc5 --help || true # does QT5 support python2 out of the box?
79 | # - make qt5py3
80 | # - xvfb-run make testpy2
81 |
82 | # Python 3 + QT4
83 | - os: linux
84 | dist: trusty
85 | sudo: required
86 | language: generic
87 | python: "3.5"
88 | env:
89 | - QT=4
90 | addons:
91 | apt:
92 | packages:
93 | - cmake
94 | - python3-pyqt4
95 | - pyqt4-dev-tools
96 | - xvfb
97 | before_install:
98 | - sudo apt-get update
99 | - sudo apt-get install -y python3-pip
100 | - sudo pip3 install lxml
101 | - make qt4py3
102 | - xvfb-run make testpy3
103 |
104 | # Python 3 + QT5
105 | - os: linux
106 | dist: trusty
107 | sudo: required
108 | language: generic
109 | python: "3.5"
110 | env:
111 | - QT=5
112 | addons:
113 | apt:
114 | packages:
115 | - cmake
116 | - pyqt5-dev-tools
117 | - xvfb
118 | before_install:
119 | - sudo apt-get update
120 | - sudo apt-get install -y python3-pip
121 | - sudo pip3 install lxml
122 | - make qt5py3
123 | - xvfb-run make testpy3
124 |
125 | # Python 3 + QT5
126 | - os: linux
127 | dist: trusty
128 | sudo: required
129 | language: generic
130 | python: "3.5"
131 | env:
132 | - QT=5
133 | - CONDA=4.2.0
134 | addons:
135 | apt:
136 | packages:
137 | - cmake
138 | - xvfb
139 | before_install:
140 | # ref: https://www.continuum.io/downloads
141 | - curl -O https://repo.continuum.io/archive/Anaconda3-4.2.0-Linux-x86_64.sh
142 | # ref: http://conda.pydata.org/docs/help/silent.html
143 | - /bin/bash Anaconda3-4.2.0-Linux-x86_64.sh -b -p $HOME/anaconda3
144 | - export PATH="$HOME/anaconda3/bin:$PATH"
145 | # ref: http://stackoverflow.com/questions/21637922/how-to-install-pyqt4-in-anaconda
146 | - conda create -y -n labelImg-py3qt5 python=3.5
147 | - source activate labelImg-py3qt5
148 | - conda install -y pyqt=5
149 | - conda install -y lxml
150 | - make qt5py3
151 | - xvfb-run make testpy3
152 |
153 | # OS X 10.10 Python 3 + QT5
154 | - os: osx
155 | osx_image: xcode6.4 # Xcode 6.4, OS X 10.10
156 | sudo: required
157 | language: generic
158 | python: "3.6"
159 | env:
160 | - QT=5
161 | before_install:
162 | #- brew update
163 | - brew install libxml2
164 | - brew install pyqt5
165 | - which python3 pip3
166 | - python3 --version
167 | #- sudo -H pip3 install --user --upgrade lxml # pyqt5 installs python3.x, which installs pip3
168 | - sudo -H easy_install-3.6 lxml || true
169 | - python3 -c 'import sys; print(sys.path)'
170 | - python3 -c 'import lxml'
171 | - make qt5py3
172 | - python3 -c 'help("modules")'
173 | - make testpy3 # FIXME: does not work, segfault on travis-ci
174 |
175 | # OS X 10.11 Python 3 + QT5
176 | - os: osx
177 | osx_image: xcode8 # Xcode 8, OS X 10.11
178 | sudo: required
179 | language: generic
180 | python: "3.6"
181 | env:
182 | - QT=5
183 | before_install:
184 | #- brew update
185 | - brew install libxml2
186 | - brew install pyqt5
187 | - which python3 pip3
188 | - python3 --version
189 | #- sudo -H pip3 install --user --upgrade lxml # pyqt5 installs python3.x, which installs pip3
190 | - sudo -H easy_install-3.6 lxml || true
191 | - python3 -c 'import sys; print(sys.path)'
192 | - python3 -c 'import lxml'
193 | - make qt5py3
194 | - python3 -c 'help("modules")'
195 | - make testpy3 # FIXME: does not work, segfault on travis-ci
196 |
197 | # OS X 10.12 Python 3 + QT5
198 | - os: osx
199 | osx_image: xcode8.2 # OS X 10.12
200 | sudo: required
201 | language: generic
202 | python: "3.6"
203 | env:
204 | - QT=5
205 | before_install:
206 | #- brew update
207 | - brew install libxml2
208 | - brew install pyqt5
209 | - which python3 pip3
210 | - python3 --version
211 | #- sudo -H pip3 install --user --upgrade lxml # pyqt5 installs python3.x, which installs pip3
212 | - sudo -H easy_install-3.6 lxml || true
213 | - python3 -c 'import sys; print(sys.path)'
214 | - python3 -c 'import lxml'
215 | - make qt5py3
216 | - python3 -c 'help("modules")'
217 | #- make testpy3 # FIXME: does not work, segfault on travis-ci
218 | # just make sure the app runs... :-/
219 | - ( python3 labelImg.py ) & sleep 10; kill $!
220 |
221 | # XXX: building QT4 from source takes forever...
222 |
223 | # OS X 10.11 Python 2 + QT4
224 | #- os: osx
225 | # osx_image: xcode7.3 # OS X 10.11
226 | # sudo: required
227 | # language: generic
228 | # python: "2.7"
229 | # env:
230 | # - QT=4
231 | # before_install:
232 | # - brew install libxml2
233 | # # build PyQT4...
234 | # - curl -L -O https://sourceforge.net/projects/pyqt/files/sip/sip-4.19/sip-4.19.tar.gz
235 | # - tar zxvf sip-4.19.tar.gz
236 | # - (cd sip-4.19 && python configure.py -d /Library/Python/2.7/site-packages --arch x86_64 && make && sudo make install)
237 | # # NOTE: produces insane amounts of output...
238 | # - brew install -v cartr/qt4/qt --with-qt3support --without-webkit | sed -e's/ .* / ... /'
239 | # - brew linkapps qt
240 | # - curl -L -O http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12/PyQt4_gpl_mac-4.12.tar.gz
241 | # - tar zxvf PyQt4_gpl_mac-4.12.tar.gz
242 | # - cd PyQt4_gpl_mac-4.12
243 | # - python configure.py --help
244 | # - python configure.py -d /Library/Python/2.7/site-packages --use-arch=x86_64 --confirm-license
245 | # - make
246 | # - sudo make install
247 | # - cd -
248 | # - which python pip
249 | # - python --version
250 | # - sudo -H easy_install-2.7 lxml || true
251 | # - python -c 'import sys; print(sys.path)'
252 | # - python -c 'import lxml'
253 | # - make qt4py2
254 | # - python -c 'help("modules")'
255 | # - make testpy2
256 |
257 | # OS X 10.11 Python 3 + QT4
258 | #- os: osx
259 | # osx_image: xcode7.3 # OS X 10.11
260 | # sudo: required
261 | # language: generic
262 | # python: "3.6"
263 | # env:
264 | # - QT=4
265 | # before_install:
266 | # - brew install libxml2
267 | # - brew install python3
268 | # - which python pip python3 pip3 || true
269 | # - curl -L -O https://sourceforge.net/projects/pyqt/files/sip/sip-4.19/sip-4.19.tar.gz
270 | # - tar zxvf sip-4.19.tar.gz
271 | # - ( cd sip-4.19 && python3 configure.py -d /Library/Python/3.6/site-packages --arch x86_64 && make && sudo make install )
272 | # # NOTE: produces insane amounts of output...
273 | # - brew install -v cartr/qt4/qt --with-qt3support --without-webkit | sed -e's/ .* / ... /'
274 | # - brew linkapps qt
275 | # - curl -L -O http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12/PyQt4_gpl_mac-4.12.tar.gz
276 | # - tar zxvf PyQt4_gpl_mac-4.12.tar.gz
277 | # - cd PyQt4_gpl_mac-4.12
278 | # - python3 configure.py --help
279 | # - python3 configure.py -d /Library/Python/3.6/site-packages --use-arch=x86_64 --confirm-license
280 | # - make
281 | # - sudo make install
282 | # - cd -
283 | # - which python3 pip3
284 | # - python3 --version
285 | # - sudo -H easy_install-3.6 lxml || true
286 | # - python3 -c 'import sys; print(sys.path)'
287 | # - python3 -c 'import lxml'
288 | # - make qt4py3
289 | # - python3 -c 'help("modules")'
290 | # - make testpy3
291 |
292 | script:
293 | - exit 0
294 |
--------------------------------------------------------------------------------
/labelImg-master/icons/open.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/labelImg-master/libs/canvas.py:
--------------------------------------------------------------------------------
1 |
2 | try:
3 | from PyQt5.QtGui import *
4 | from PyQt5.QtCore import *
5 | from PyQt5.QtWidgets import *
6 | except ImportError:
7 | from PyQt4.QtGui import *
8 | from PyQt4.QtCore import *
9 |
10 | #from PyQt4.QtOpenGL import *
11 |
12 | from shape import Shape
13 | from lib import distance
14 |
15 | CURSOR_DEFAULT = Qt.ArrowCursor
16 | CURSOR_POINT = Qt.PointingHandCursor
17 | CURSOR_DRAW = Qt.CrossCursor
18 | CURSOR_MOVE = Qt.ClosedHandCursor
19 | CURSOR_GRAB = Qt.OpenHandCursor
20 |
21 | # class Canvas(QGLWidget):
22 |
23 |
24 | class Canvas(QWidget):
25 | zoomRequest = pyqtSignal(int)
26 | scrollRequest = pyqtSignal(int, int)
27 | newShape = pyqtSignal()
28 | selectionChanged = pyqtSignal(bool)
29 | shapeMoved = pyqtSignal()
30 | drawingPolygon = pyqtSignal(bool)
31 |
32 | CREATE, EDIT = list(range(2))
33 |
34 | epsilon = 11.0
35 |
36 | def __init__(self, *args, **kwargs):
37 | super(Canvas, self).__init__(*args, **kwargs)
38 | # Initialise local state.
39 | self.mode = self.EDIT
40 | self.shapes = []
41 | self.current = None
42 | self.selectedShape = None # save the selected shape here
43 | self.selectedShapeCopy = None
44 | self.lineColor = QColor(0, 0, 255)
45 | self.line = Shape(line_color=self.lineColor)
46 | self.prevPoint = QPointF()
47 | self.offsets = QPointF(), QPointF()
48 | self.scale = 1.0
49 | self.pixmap = QPixmap()
50 | self.visible = {}
51 | self._hideBackround = False
52 | self.hideBackround = False
53 | self.hShape = None
54 | self.hVertex = None
55 | self._painter = QPainter()
56 | self._cursor = CURSOR_DEFAULT
57 | # Menus:
58 | self.menus = (QMenu(), QMenu())
59 | # Set widget options.
60 | self.setMouseTracking(True)
61 | self.setFocusPolicy(Qt.WheelFocus)
62 |
63 | def enterEvent(self, ev):
64 | self.overrideCursor(self._cursor)
65 |
66 | def leaveEvent(self, ev):
67 | self.restoreCursor()
68 |
69 | def focusOutEvent(self, ev):
70 | self.restoreCursor()
71 |
72 | def isVisible(self, shape):
73 | return self.visible.get(shape, True)
74 |
75 | def drawing(self):
76 | return self.mode == self.CREATE
77 |
78 | def editing(self):
79 | return self.mode == self.EDIT
80 |
81 | def setEditing(self, value=True):
82 | self.mode = self.EDIT if value else self.CREATE
83 | if not value: # Create
84 | self.unHighlight()
85 | self.deSelectShape()
86 |
87 | def unHighlight(self):
88 | if self.hShape:
89 | self.hShape.highlightClear()
90 | self.hVertex = self.hShape = None
91 |
92 | def selectedVertex(self):
93 | return self.hVertex is not None
94 |
95 | def mouseMoveEvent(self, ev):
96 | """Update line with last point and current coordinates."""
97 | pos = self.transformPos(ev.pos())
98 |
99 | self.restoreCursor()
100 |
101 | # Polygon drawing.
102 | if self.drawing():
103 | self.overrideCursor(CURSOR_DRAW)
104 | if self.current:
105 | color = self.lineColor
106 | if self.outOfPixmap(pos):
107 | # Don't allow the user to draw outside the pixmap.
108 | # Project the point to the pixmap's edges.
109 | pos = self.intersectionPoint(self.current[-1], pos)
110 | elif len(self.current) > 1 and self.closeEnough(pos, self.current[0]):
111 | # Attract line to starting point and colorise to alert the
112 | # user:
113 | pos = self.current[0]
114 | color = self.current.line_color
115 | self.overrideCursor(CURSOR_POINT)
116 | self.current.highlightVertex(0, Shape.NEAR_VERTEX)
117 | self.line[1] = pos
118 | self.line.line_color = color
119 | self.repaint()
120 | self.current.highlightClear()
121 | return
122 |
123 | # Polygon copy moving.
124 | if Qt.RightButton & ev.buttons():
125 | if self.selectedShapeCopy and self.prevPoint:
126 | self.overrideCursor(CURSOR_MOVE)
127 | self.boundedMoveShape(self.selectedShapeCopy, pos)
128 | self.repaint()
129 | elif self.selectedShape:
130 | self.selectedShapeCopy = self.selectedShape.copy()
131 | self.repaint()
132 | return
133 |
134 | # Polygon/Vertex moving.
135 | if Qt.LeftButton & ev.buttons():
136 | if self.selectedVertex():
137 | self.boundedMoveVertex(pos)
138 | self.shapeMoved.emit()
139 | self.repaint()
140 | elif self.selectedShape and self.prevPoint:
141 | self.overrideCursor(CURSOR_MOVE)
142 | self.boundedMoveShape(self.selectedShape, pos)
143 | self.shapeMoved.emit()
144 | self.repaint()
145 | return
146 |
147 | # Just hovering over the canvas, 2 posibilities:
148 | # - Highlight shapes
149 | # - Highlight vertex
150 | # Update shape/vertex fill and tooltip value accordingly.
151 | self.setToolTip("Image")
152 | for shape in reversed([s for s in self.shapes if self.isVisible(s)]):
153 | # Look for a nearby vertex to highlight. If that fails,
154 | # check if we happen to be inside a shape.
155 | index = shape.nearestVertex(pos, self.epsilon)
156 | if index is not None:
157 | if self.selectedVertex():
158 | self.hShape.highlightClear()
159 | self.hVertex, self.hShape = index, shape
160 | shape.highlightVertex(index, shape.MOVE_VERTEX)
161 | self.overrideCursor(CURSOR_POINT)
162 | self.setToolTip("Click & drag to move point")
163 | self.setStatusTip(self.toolTip())
164 | self.update()
165 | break
166 | elif shape.containsPoint(pos):
167 | if self.selectedVertex():
168 | self.hShape.highlightClear()
169 | self.hVertex, self.hShape = None, shape
170 | self.setToolTip(
171 | "Click & drag to move shape '%s'" % shape.label)
172 | self.setStatusTip(self.toolTip())
173 | self.overrideCursor(CURSOR_GRAB)
174 | self.update()
175 | break
176 | else: # Nothing found, clear highlights, reset state.
177 | if self.hShape:
178 | self.hShape.highlightClear()
179 | self.update()
180 | self.hVertex, self.hShape = None, None
181 |
182 | def mousePressEvent(self, ev):
183 | pos = self.transformPos(ev.pos())
184 |
185 | if ev.button() == Qt.LeftButton:
186 | if self.drawing():
187 | self.handleDrawing(pos)
188 | else:
189 | self.selectShapePoint(pos)
190 | self.prevPoint = pos
191 | self.repaint()
192 | elif ev.button() == Qt.RightButton and self.editing():
193 | self.selectShapePoint(pos)
194 | self.prevPoint = pos
195 | self.repaint()
196 |
197 | def mouseReleaseEvent(self, ev):
198 | if ev.button() == Qt.RightButton:
199 | menu = self.menus[bool(self.selectedShapeCopy)]
200 | self.restoreCursor()
201 | if not menu.exec_(self.mapToGlobal(ev.pos()))\
202 | and self.selectedShapeCopy:
203 | # Cancel the move by deleting the shadow copy.
204 | self.selectedShapeCopy = None
205 | self.repaint()
206 | elif ev.button() == Qt.LeftButton and self.selectedShape:
207 | self.overrideCursor(CURSOR_GRAB)
208 | elif ev.button() == Qt.LeftButton:
209 | pos = self.transformPos(ev.pos())
210 | if self.drawing():
211 | self.handleDrawing(pos)
212 |
213 | def endMove(self, copy=False):
214 | assert self.selectedShape and self.selectedShapeCopy
215 | shape = self.selectedShapeCopy
216 | #del shape.fill_color
217 | #del shape.line_color
218 | if copy:
219 | self.shapes.append(shape)
220 | self.selectedShape.selected = False
221 | self.selectedShape = shape
222 | self.repaint()
223 | else:
224 | shape.label = self.selectedShape.label
225 | self.deleteSelected()
226 | self.shapes.append(shape)
227 | self.selectedShapeCopy = None
228 |
229 | def hideBackroundShapes(self, value):
230 | self.hideBackround = value
231 | if self.selectedShape:
232 | # Only hide other shapes if there is a current selection.
233 | # Otherwise the user will not be able to select a shape.
234 | self.setHiding(True)
235 | self.repaint()
236 |
237 | def handleDrawing(self, pos):
238 | if self.current and self.current.reachMaxPoints() is False:
239 | initPos = self.current[0]
240 | minX = initPos.x()
241 | minY = initPos.y()
242 | targetPos = self.line[1]
243 | maxX = targetPos.x()
244 | maxY = targetPos.y()
245 | self.current.addPoint(QPointF(maxX, minY))
246 | self.current.addPoint(targetPos)
247 | self.current.addPoint(QPointF(minX, maxY))
248 | self.current.addPoint(initPos)
249 | self.line[0] = self.current[-1]
250 | if self.current.isClosed():
251 | self.finalise()
252 | elif not self.outOfPixmap(pos):
253 | self.current = Shape()
254 | self.current.addPoint(pos)
255 | self.line.points = [pos, pos]
256 | self.setHiding()
257 | self.drawingPolygon.emit(True)
258 | self.update()
259 |
260 | def setHiding(self, enable=True):
261 | self._hideBackround = self.hideBackround if enable else False
262 |
263 | def canCloseShape(self):
264 | return self.drawing() and self.current and len(self.current) > 2
265 |
266 | def mouseDoubleClickEvent(self, ev):
267 | # We need at least 4 points here, since the mousePress handler
268 | # adds an extra one before this handler is called.
269 | if self.canCloseShape() and len(self.current) > 3:
270 | self.current.popPoint()
271 | self.finalise()
272 |
273 | def selectShape(self, shape):
274 | self.deSelectShape()
275 | shape.selected = True
276 | self.selectedShape = shape
277 | self.setHiding()
278 | self.selectionChanged.emit(True)
279 | self.update()
280 |
281 | def selectShapePoint(self, point):
282 | """Select the first shape created which contains this point."""
283 | self.deSelectShape()
284 | if self.selectedVertex(): # A vertex is marked for selection.
285 | index, shape = self.hVertex, self.hShape
286 | shape.highlightVertex(index, shape.MOVE_VERTEX)
287 | return
288 | for shape in reversed(self.shapes):
289 | if self.isVisible(shape) and shape.containsPoint(point):
290 | shape.selected = True
291 | self.selectedShape = shape
292 | self.calculateOffsets(shape, point)
293 | self.setHiding()
294 | self.selectionChanged.emit(True)
295 | return
296 |
297 | def calculateOffsets(self, shape, point):
298 | rect = shape.boundingRect()
299 | x1 = rect.x() - point.x()
300 | y1 = rect.y() - point.y()
301 | x2 = (rect.x() + rect.width()) - point.x()
302 | y2 = (rect.y() + rect.height()) - point.y()
303 | self.offsets = QPointF(x1, y1), QPointF(x2, y2)
304 |
305 | def boundedMoveVertex(self, pos):
306 | index, shape = self.hVertex, self.hShape
307 | point = shape[index]
308 | if self.outOfPixmap(pos):
309 | pos = self.intersectionPoint(point, pos)
310 |
311 | shiftPos = pos - point
312 | shape.moveVertexBy(index, shiftPos)
313 |
314 | lindex = (index + 1) % 4
315 | rindex = (index + 3) % 4
316 | lshift = None
317 | rshift = None
318 | if index % 2 == 0:
319 | rshift = QPointF(shiftPos.x(), 0)
320 | lshift = QPointF(0, shiftPos.y())
321 | else:
322 | lshift = QPointF(shiftPos.x(), 0)
323 | rshift = QPointF(0, shiftPos.y())
324 | shape.moveVertexBy(rindex, rshift)
325 | shape.moveVertexBy(lindex, lshift)
326 |
327 | def boundedMoveShape(self, shape, pos):
328 | if self.outOfPixmap(pos):
329 | return False # No need to move
330 | o1 = pos + self.offsets[0]
331 | if self.outOfPixmap(o1):
332 | pos -= QPointF(min(0, o1.x()), min(0, o1.y()))
333 | o2 = pos + self.offsets[1]
334 | if self.outOfPixmap(o2):
335 | pos += QPointF(min(0, self.pixmap.width() - o2.x()),
336 | min(0, self.pixmap.height() - o2.y()))
337 | # The next line tracks the new position of the cursor
338 | # relative to the shape, but also results in making it
339 | # a bit "shaky" when nearing the border and allows it to
340 | # go outside of the shape's area for some reason. XXX
341 | #self.calculateOffsets(self.selectedShape, pos)
342 | dp = pos - self.prevPoint
343 | if dp:
344 | shape.moveBy(dp)
345 | self.prevPoint = pos
346 | return True
347 | return False
348 |
349 | def deSelectShape(self):
350 | if self.selectedShape:
351 | self.selectedShape.selected = False
352 | self.selectedShape = None
353 | self.setHiding(False)
354 | self.selectionChanged.emit(False)
355 | self.update()
356 |
357 | def deleteSelected(self):
358 | if self.selectedShape:
359 | shape = self.selectedShape
360 | self.shapes.remove(self.selectedShape)
361 | self.selectedShape = None
362 | self.update()
363 | return shape
364 |
365 | def copySelectedShape(self):
366 | if self.selectedShape:
367 | shape = self.selectedShape.copy()
368 | self.deSelectShape()
369 | self.shapes.append(shape)
370 | shape.selected = True
371 | self.selectedShape = shape
372 | self.boundedShiftShape(shape)
373 | return shape
374 |
375 | def boundedShiftShape(self, shape):
376 | # Try to move in one direction, and if it fails in another.
377 | # Give up if both fail.
378 | point = shape[0]
379 | offset = QPointF(2.0, 2.0)
380 | self.calculateOffsets(shape, point)
381 | self.prevPoint = point
382 | if not self.boundedMoveShape(shape, point - offset):
383 | self.boundedMoveShape(shape, point + offset)
384 |
385 | def paintEvent(self, event):
386 | if not self.pixmap:
387 | return super(Canvas, self).paintEvent(event)
388 |
389 | p = self._painter
390 | p.begin(self)
391 | p.setRenderHint(QPainter.Antialiasing)
392 | p.setRenderHint(QPainter.HighQualityAntialiasing)
393 | p.setRenderHint(QPainter.SmoothPixmapTransform)
394 |
395 | p.scale(self.scale, self.scale)
396 | p.translate(self.offsetToCenter())
397 |
398 | p.drawPixmap(0, 0, self.pixmap)
399 | Shape.scale = self.scale
400 | for shape in self.shapes:
401 | if (shape.selected or not self._hideBackround) and self.isVisible(shape):
402 | shape.fill = shape.selected or shape == self.hShape
403 | shape.paint(p)
404 | if self.current:
405 | self.current.paint(p)
406 | self.line.paint(p)
407 | if self.selectedShapeCopy:
408 | self.selectedShapeCopy.paint(p)
409 |
410 | # Paint rect
411 | if self.current is not None and len(self.line) == 2:
412 | leftTop = self.line[0]
413 | rightBottom = self.line[1]
414 | rectWidth = rightBottom.x() - leftTop.x()
415 | rectHeight = rightBottom.y() - leftTop.y()
416 | color = QColor(0, 220, 0)
417 | p.setPen(color)
418 | brush = QBrush(Qt.BDiagPattern)
419 | p.setBrush(brush)
420 | p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight)
421 |
422 | p.end()
423 |
424 | def transformPos(self, point):
425 | """Convert from widget-logical coordinates to painter-logical coordinates."""
426 | return point / self.scale - self.offsetToCenter()
427 |
428 | def offsetToCenter(self):
429 | s = self.scale
430 | area = super(Canvas, self).size()
431 | w, h = self.pixmap.width() * s, self.pixmap.height() * s
432 | aw, ah = area.width(), area.height()
433 | x = (aw - w) / (2 * s) if aw > w else 0
434 | y = (ah - h) / (2 * s) if ah > h else 0
435 | return QPointF(x, y)
436 |
437 | def outOfPixmap(self, p):
438 | w, h = self.pixmap.width(), self.pixmap.height()
439 | return not (0 <= p.x() <= w and 0 <= p.y() <= h)
440 |
441 | def finalise(self):
442 | assert self.current
443 | self.current.close()
444 | self.shapes.append(self.current)
445 | self.current = None
446 | self.setHiding(False)
447 | self.newShape.emit()
448 | self.update()
449 |
450 | def closeEnough(self, p1, p2):
451 | #d = distance(p1 - p2)
452 | #m = (p1-p2).manhattanLength()
453 | # print "d %.2f, m %d, %.2f" % (d, m, d - m)
454 | return distance(p1 - p2) < self.epsilon
455 |
456 | def intersectionPoint(self, p1, p2):
457 | # Cycle through each image edge in clockwise fashion,
458 | # and find the one intersecting the current line segment.
459 | # http://paulbourke.net/geometry/lineline2d/
460 | size = self.pixmap.size()
461 | points = [(0, 0),
462 | (size.width(), 0),
463 | (size.width(), size.height()),
464 | (0, size.height())]
465 | x1, y1 = p1.x(), p1.y()
466 | x2, y2 = p2.x(), p2.y()
467 | d, i, (x, y) = min(self.intersectingEdges((x1, y1), (x2, y2), points))
468 | x3, y3 = points[i]
469 | x4, y4 = points[(i + 1) % 4]
470 | if (x, y) == (x1, y1):
471 | # Handle cases where previous point is on one of the edges.
472 | if x3 == x4:
473 | return QPointF(x3, min(max(0, y2), max(y3, y4)))
474 | else: # y3 == y4
475 | return QPointF(min(max(0, x2), max(x3, x4)), y3)
476 | return QPointF(x, y)
477 |
478 | def intersectingEdges(self, x1y1, x2y2, points):
479 | """For each edge formed by `points', yield the intersection
480 | with the line segment `(x1,y1) - (x2,y2)`, if it exists.
481 | Also return the distance of `(x2,y2)' to the middle of the
482 | edge along with its index, so that the one closest can be chosen."""
483 | x1, y1 = x1y1
484 | x2, y2 = x2y2
485 | for i in range(4):
486 | x3, y3 = points[i]
487 | x4, y4 = points[(i + 1) % 4]
488 | denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1)
489 | nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)
490 | nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)
491 | if denom == 0:
492 | # This covers two cases:
493 | # nua == nub == 0: Coincident
494 | # otherwise: Parallel
495 | continue
496 | ua, ub = nua / denom, nub / denom
497 | if 0 <= ua <= 1 and 0 <= ub <= 1:
498 | x = x1 + ua * (x2 - x1)
499 | y = y1 + ua * (y2 - y1)
500 | m = QPointF((x3 + x4) / 2, (y3 + y4) / 2)
501 | d = distance(m - QPointF(x2, y2))
502 | yield d, i, (x, y)
503 |
504 | # These two, along with a call to adjustSize are required for the
505 | # scroll area.
506 | def sizeHint(self):
507 | return self.minimumSizeHint()
508 |
509 | def minimumSizeHint(self):
510 | if self.pixmap:
511 | return self.scale * self.pixmap.size()
512 | return super(Canvas, self).minimumSizeHint()
513 |
514 | def wheelEvent(self, ev):
515 | if ev.orientation() == Qt.Vertical:
516 | mods = ev.modifiers()
517 | if Qt.ControlModifier == int(mods):
518 | self.zoomRequest.emit(ev.delta())
519 | else:
520 | self.scrollRequest.emit(ev.delta(),
521 | Qt.Horizontal if (Qt.ShiftModifier == int(mods))
522 | else Qt.Vertical)
523 | else:
524 | self.scrollRequest.emit(ev.delta(), Qt.Horizontal)
525 | ev.accept()
526 |
527 | def keyPressEvent(self, ev):
528 | key = ev.key()
529 | if key == Qt.Key_Escape and self.current:
530 | print('ESC press')
531 | self.current = None
532 | self.drawingPolygon.emit(False)
533 | self.update()
534 | elif key == Qt.Key_Return and self.canCloseShape():
535 | self.finalise()
536 |
537 | def setLastLabel(self, text):
538 | assert text
539 | self.shapes[-1].label = text
540 | return self.shapes[-1]
541 |
542 | def undoLastLine(self):
543 | assert self.shapes
544 | self.current = self.shapes.pop()
545 | self.current.setOpen()
546 | self.line.points = [self.current[-1], self.current[0]]
547 | self.drawingPolygon.emit(True)
548 |
549 | def resetAllLines(self):
550 | assert self.shapes
551 | self.current = self.shapes.pop()
552 | self.current.setOpen()
553 | self.line.points = [self.current[-1], self.current[0]]
554 | self.drawingPolygon.emit(True)
555 | self.current = None
556 | self.drawingPolygon.emit(False)
557 | self.update()
558 |
559 | def loadPixmap(self, pixmap):
560 | self.pixmap = pixmap
561 | self.shapes = []
562 | self.repaint()
563 |
564 | def loadShapes(self, shapes):
565 | self.shapes = list(shapes)
566 | self.current = None
567 | self.repaint()
568 |
569 | def setShapeVisible(self, shape, value):
570 | self.visible[shape] = value
571 | self.repaint()
572 |
573 | def overrideCursor(self, cursor):
574 | self.restoreCursor()
575 | self._cursor = cursor
576 | QApplication.setOverrideCursor(cursor)
577 |
578 | def restoreCursor(self):
579 | QApplication.restoreOverrideCursor()
580 |
581 | def resetState(self):
582 | self.restoreCursor()
583 | self.pixmap = None
584 | self.update()
585 |
--------------------------------------------------------------------------------
/labelImg-master/icons/done.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
401 |
--------------------------------------------------------------------------------
/labelImg-master/icons/save.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
680 |
--------------------------------------------------------------------------------
/labelImg-master/icons/labels.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------