├── .gitignore ├── .travis.yml ├── HISTORY.rst ├── MANIFEST.in ├── Makefile ├── README.rst ├── __init__.py ├── build-tools ├── .gitignore ├── build-for-pypi.sh ├── build-ubuntu-binary.sh ├── build-windows-binary.sh └── envsetup.sh ├── data └── predefined_classes.txt ├── demo ├── demo.png ├── demo.xml ├── demo2.png ├── demo2.xml ├── demo3.jpg ├── demo3.xml ├── demo4.png ├── demo4.xml ├── demo_v2.5.gif ├── roLabelImg.gif └── rolabelimgdemo_v2.1.png ├── icons ├── cancel.png ├── close.png ├── color.png ├── color_line.png ├── copy.png ├── delete.png ├── done.png ├── done.svg ├── edit.png ├── expert1.png ├── expert2.png ├── eye.png ├── feBlend-icon.png ├── file.png ├── fit-width.png ├── fit-window.png ├── fit.png ├── help.png ├── labels.png ├── labels.svg ├── new.png ├── next.png ├── objects.png ├── open.png ├── open.svg ├── prev.png ├── quit.png ├── robjects.png ├── save-as.png ├── save-as.svg ├── save.png ├── save.svg ├── undo-cross.png ├── undo.png ├── zoom-in.png ├── zoom-out.png └── zoom.png ├── issue_template.md ├── libs ├── __init__.py ├── canvas.py ├── colorDialog.py ├── labelDialog.py ├── labelFile.py ├── lib.py ├── pascal_voc_io.py ├── shape.py ├── toolBar.py ├── ustr.py └── zoomWidget.py ├── resources.py ├── resources.qrc ├── roLabelImg.py ├── setup.cfg ├── setup.py └── tests ├── .gitignore ├── test.bmp ├── test_io.py ├── test_qt.py └── 臉書.jpg /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | */__pycache__ 3 | .vscode 4 | __pycache__ 5 | *.DS_Store 6 | labelImg.py 7 | roLabelImg.py.bak 8 | CONTRIBUTING.rst 9 | LICENSE -------------------------------------------------------------------------------- /.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 | # XXX: building QT4 from source takes forever... 153 | 154 | # OS X 10.11 Python 2 + QT4 155 | #- os: osx 156 | # osx_image: xcode7.3 # OS X 10.11 157 | # sudo: required 158 | # language: generic 159 | # python: "2.7" 160 | # env: 161 | # - QT=4 162 | # before_install: 163 | # - brew install libxml2 164 | # # build PyQT4... 165 | # - curl -L -O https://sourceforge.net/projects/pyqt/files/sip/sip-4.19/sip-4.19.tar.gz 166 | # - tar zxvf sip-4.19.tar.gz 167 | # - (cd sip-4.19 && python configure.py -d /Library/Python/2.7/site-packages --arch x86_64 && make && sudo make install) 168 | # # NOTE: produces insane amounts of output... 169 | # - brew install -v cartr/qt4/qt --with-qt3support --without-webkit | sed -e's/ .* / ... /' 170 | # - brew linkapps qt 171 | # - curl -L -O http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12/PyQt4_gpl_mac-4.12.tar.gz 172 | # - tar zxvf PyQt4_gpl_mac-4.12.tar.gz 173 | # - cd PyQt4_gpl_mac-4.12 174 | # - python configure.py --help 175 | # - python configure.py -d /Library/Python/2.7/site-packages --use-arch=x86_64 --confirm-license 176 | # - make 177 | # - sudo make install 178 | # - cd - 179 | # - which python pip 180 | # - python --version 181 | # - sudo -H easy_install-2.7 lxml || true 182 | # - python -c 'import sys; print(sys.path)' 183 | # - python -c 'import lxml' 184 | # - make qt4py2 185 | # - python -c 'help("modules")' 186 | # - make testpy2 187 | 188 | # OS X 10.11 Python 3 + QT4 189 | #- os: osx 190 | # osx_image: xcode7.3 # OS X 10.11 191 | # sudo: required 192 | # language: generic 193 | # python: "3.6" 194 | # env: 195 | # - QT=4 196 | # before_install: 197 | # - brew install libxml2 198 | # - brew install python3 199 | # - which python pip python3 pip3 || true 200 | # - curl -L -O https://sourceforge.net/projects/pyqt/files/sip/sip-4.19/sip-4.19.tar.gz 201 | # - tar zxvf sip-4.19.tar.gz 202 | # - ( cd sip-4.19 && python3 configure.py -d /Library/Python/3.6/site-packages --arch x86_64 && make && sudo make install ) 203 | # # NOTE: produces insane amounts of output... 204 | # - brew install -v cartr/qt4/qt --with-qt3support --without-webkit | sed -e's/ .* / ... /' 205 | # - brew linkapps qt 206 | # - curl -L -O http://sourceforge.net/projects/pyqt/files/PyQt4/PyQt-4.12/PyQt4_gpl_mac-4.12.tar.gz 207 | # - tar zxvf PyQt4_gpl_mac-4.12.tar.gz 208 | # - cd PyQt4_gpl_mac-4.12 209 | # - python3 configure.py --help 210 | # - python3 configure.py -d /Library/Python/3.6/site-packages --use-arch=x86_64 --confirm-license 211 | # - make 212 | # - sudo make install 213 | # - cd - 214 | # - which python3 pip3 215 | # - python3 --version 216 | # - sudo -H easy_install-3.6 lxml || true 217 | # - python3 -c 'import sys; print(sys.path)' 218 | # - python3 -c 'import lxml' 219 | # - make qt4py3 220 | # - python3 -c 'help("modules")' 221 | # - make testpy3 222 | 223 | script: 224 | - exit 0 225 | -------------------------------------------------------------------------------- /HISTORY.rst: -------------------------------------------------------------------------------- 1 | ======= 2 | History 3 | ======= 4 | 5 | 6 | v3.0 (2020) - developing 7 | ------------------------ 8 | * This version will reorganize the code to make it more readable. 9 | * This version will only support Python3 and PyQt5. 10 | * One Qt issue on Mac leaves in this version: Mac PyQt5 menubar not active until unfocusing-refocusing the app. 11 | * This bug may be from Qt, the temperal solution is listed as the link. 12 | * https://stackoverflow.com/questions/48738805/mac-pyqt5-menubar-not-active-until-unfocusing-refocusing-the-app 13 | * If anyone who have better solution, that will be much helpful. 14 | * A small demo in .gif can be found under ./demo as demo_v2.5.gif and https://youtu.be/7D5lvol_QRA 15 | 16 | 17 | v2.5 (2017) - roLabelImg 18 | ------------------------ 19 | * This is the version developed in 2017. 20 | * Please find this version in branch v2.5-2017 21 | 22 | 1.3.2 (2017-05-18) 23 | ------------------ 24 | 25 | * Fix issues 26 | 27 | 28 | 1.3.1 (2017-05-11) 29 | ------------------ 30 | 31 | * Fix issues 32 | 33 | 1.3.0 (2017-04-22) 34 | ------------------ 35 | 36 | * Fix issues 37 | * Add difficult tag 38 | * Create new files for pypi 39 | 40 | 1.2.3 (2017-04-22) 41 | ------------------ 42 | 43 | * Fix issues 44 | 45 | 1.2.2 (2017-01-09) 46 | ------------------ 47 | 48 | * Fix issues 49 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include HISTORY.rst 2 | include README.rst 3 | 4 | include resources.qrc 5 | 6 | recursive-include data * 7 | recursive-include icons * 8 | recursive-include libs * 9 | 10 | recursive-exclude build-tools * 11 | recursive-exclude tests * 12 | recursive-exclude * __pycache__ 13 | recursive-exclude * *.py[co] 14 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | roLabelImg 2 | ========== 3 | 4 | .. image:: https://img.shields.io/pypi/v/labelimg.svg 5 | :target: https://pypi.python.org/pypi/labelimg 6 | 7 | .. image:: https://img.shields.io/travis/tzutalin/labelImg.svg 8 | :target: https://travis-ci.org/tzutalin/labelImg 9 | 10 | roLabelImg is a graphical image annotation tool can label ROTATED rectangle regions, which is rewrite from 'labelImg'. 11 | 12 | The original version 'labelImg''s link is here. 13 | 14 | It is written in Python and uses Qt for its graphical interface. 15 | 16 | `Watch a demo by author cgvict` 17 | 18 | .. image:: https://raw.githubusercontent.com/cgvict/roLabelImg/master/demo/demo4.png 19 | :alt: Demo Image 20 | 21 | .. image:: https://raw.githubusercontent.com/cgvict/roLabelImg/master/demo/demo_v2.5.gif 22 | 23 | https://youtu.be/7D5lvol_QRA 24 | 25 | Annotations are saved as XML files almost like PASCAL VOC format, the format used by `ImageNet `__. 26 | 27 | 28 | XML Format 29 | ------------------ 30 | 31 | .. code:: 32 | 33 | 34 | hsrc 35 | 100000001 36 | /Users/haoyou/Library/Mobile Documents/com~apple~CloudDocs/OneDrive/hsrc/100000001.bmp 37 | 38 | Unknown 39 | 40 | 41 | 1166 42 | 753 43 | 3 44 | 45 | 0 46 | 47 | bndbox 48 | ship 49 | Unspecified 50 | 0 51 | 0 52 | 53 | 178 54 | 246 55 | 974 56 | 504 57 | 58 | 59 | 60 | robndbox 61 | ship 62 | Unspecified 63 | 0 64 | 0 65 | 66 | 580.7887 67 | 343.2913 68 | 775.0449 69 | 170.2159 70 | 2.889813 71 | 72 | 73 | 74 | 75 | 76 | 77 | Installation 78 | ------------------ 79 | 80 | Download prebuilt binaries of original 'labelImg' 81 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 82 | 83 | - `Windows & Linux `__ 84 | 85 | - OS X. Binaries for OS X are not yet available. Help would be appreciated. At present, it must be `built from source <#os-x>`__. 86 | 87 | Build from source 88 | ~~~~~~~~~~~~~~~~~ 89 | 90 | Linux/Ubuntu/Mac requires at least `Python 91 | 2.6 `__ and has been tested with `PyQt 92 | 4.8 `__. 93 | 94 | 95 | Ubuntu Linux 96 | ^^^^^^^^^^^^ 97 | 98 | .. code:: 99 | 100 | sudo apt-get install pyqt4-dev-tools 101 | sudo pip install lxml 102 | make all 103 | ./roLabelImg.py 104 | ./roLabelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE] 105 | 106 | OS X 107 | ^^^^ 108 | 109 | .. code:: 110 | 111 | brew install qt qt4 112 | brew install libxml2 113 | make all 114 | ./roLabelImg.py 115 | ./roLabelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE] 116 | 117 | Windows 118 | ^^^^^^^ 119 | 120 | Download and setup `Python 2.6 or 121 | later `__, 122 | `PyQt4 `__ 123 | and `install lxml `__. 124 | 125 | Open cmd and go to `roLabelImg <#roLabelimg>`__ directory 126 | 127 | .. code:: 128 | 129 | pyrcc4 -o resources.py resources.qrc 130 | python roLabelImg.py 131 | python roLabelImg.py [IMAGE_PATH] [PRE-DEFINED CLASS FILE] 132 | 133 | Use Docker 134 | ~~~~~~~~~~~~~~~~~ 135 | .. code:: 136 | 137 | docker pull tzutalin/py2qt4 138 | 139 | docker run -it \ 140 | --user $(id -u) \ 141 | -e DISPLAY=unix$DISPLAY \ 142 | --workdir=$(pwd) \ 143 | --volume="/home/$USER:/home/$USER" \ 144 | --volume="/etc/group:/etc/group:ro" \ 145 | --volume="/etc/passwd:/etc/passwd:ro" \ 146 | --volume="/etc/shadow:/etc/shadow:ro" \ 147 | --volume="/etc/sudoers.d:/etc/sudoers.d:ro" \ 148 | -v /tmp/.X11-unix:/tmp/.X11-unix \ 149 | tzutalin/py2qt4 150 | 151 | You can pull the image which has all of the installed and required dependencies. 152 | 153 | Usage 154 | ----- 155 | 156 | Steps 157 | ~~~~~ 158 | 159 | 1. Build and launch using the instructions above. 160 | 2. Click 'Change default saved annotation folder' in Menu/File 161 | 3. Click 'Open Dir' 162 | 4. Click 'Create RectBox' 163 | 5. Click and release left mouse to select a region to annotate the rect 164 | box 165 | 6. You can use right mouse to drag the rect box to copy or move it 166 | 167 | The annotation will be saved to the folder you specify. 168 | 169 | You can refer to the below hotkeys to speed up your workflow. 170 | 171 | Create pre-defined classes 172 | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 173 | 174 | You can edit the 175 | `data/predefined\_classes.txt `__ 176 | to load pre-defined classes 177 | 178 | Hotkeys 179 | ~~~~~~~ 180 | 181 | +------------+--------------------------------------------+ 182 | | Ctrl + u | Load all of the images from a directory | 183 | +------------+--------------------------------------------+ 184 | | Ctrl + r | Change the default annotation target dir | 185 | +------------+--------------------------------------------+ 186 | | Ctrl + s | Save | 187 | +------------+--------------------------------------------+ 188 | | Ctrl + d | Copy the current label and rect box | 189 | +------------+--------------------------------------------+ 190 | | Space | Flag the current image as verified | 191 | +------------+--------------------------------------------+ 192 | | w | Create a rect box | 193 | +------------+--------------------------------------------+ 194 | | e | Create a Rotated rect box | 195 | +------------+--------------------------------------------+ 196 | | d | Next image | 197 | +------------+--------------------------------------------+ 198 | | a | Previous image | 199 | +------------+--------------------------------------------+ 200 | | r | Hidden/Show Rotated Rect boxes | 201 | +------------+--------------------------------------------+ 202 | | n | Hidden/Show Normal Rect boxes | 203 | +------------+--------------------------------------------+ 204 | | del | Delete the selected rect box | 205 | +------------+--------------------------------------------+ 206 | | Ctrl++ | Zoom in | 207 | +------------+--------------------------------------------+ 208 | | Ctrl-- | Zoom out | 209 | +------------+--------------------------------------------+ 210 | | ↑→↓← | Keyboard arrows to move selected rect box | 211 | +------------+--------------------------------------------+ 212 | | zxcv | Keyboard to rotate selected rect box | 213 | +------------+--------------------------------------------+ 214 | 215 | How to contribute 216 | ~~~~~~~~~~~~~~~~~ 217 | 218 | Send a pull request 219 | 220 | License 221 | ~~~~~~~ 222 | `Free software: MIT license `_ 223 | 224 | 225 | Related 226 | ~~~~~~~ 227 | 228 | 1. `ImageNet Utils `__ to 229 | download image, create a label text for machine learning, etc 230 | 2. `Docker hub to run it `__ 231 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/__init__.py -------------------------------------------------------------------------------- /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 | windows_v* 12 | linux_v* 13 | -------------------------------------------------------------------------------- /build-tools/build-for-pypi.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Packaging 3 | cd ..;sudo python setup.py sdist;sudo python setup.py install 4 | 5 | # Release 6 | # python setup.py register 7 | # python setup.py sdist upload 8 | -------------------------------------------------------------------------------- /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 -p ../ 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 | -------------------------------------------------------------------------------- /build-tools/build-windows-binary.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | ### Window requires pyinstall v2.1 3 | wine msiexec -i python-2.7.8.msi 4 | wine pywin32-218.win32-py2.7.exe 5 | wine PyQt4-4.11.4-gpl-Py2.7-Qt4.8.7-x32.exe 6 | wine lxml-3.7.3.win32-py2.7.exe 7 | 8 | THIS_SCRIPT_PATH=`readlink -f $0` 9 | THIS_SCRIPT_DIR=`dirname ${THIS_SCRIPT_PATH}` 10 | cd pyinstaller 11 | git checkout v2.1 12 | cd ${THIS_SCRIPT_DIR} 13 | 14 | . venv_wine/bin/activate 15 | rm -r build 16 | rm -r dist 17 | rm labelImg.spec 18 | wine c:/Python27/python.exe pyinstaller/pyinstaller.py --hidden-import=xml \ 19 | --hidden-import=xml.etree \ 20 | --hidden-import=xml.etree.ElementTree \ 21 | --hidden-import=lxml.etree \ 22 | -D -F -n labelImg -c "../labelImg.py" -p ../libs -p ../ 23 | 24 | FOLDER=$(git describe --abbrev=0 --tags) 25 | FOLDER="windows_"$FOLDER 26 | rm -rf "$FOLDER" 27 | mkdir "$FOLDER" 28 | cp dist/labelImg.exe $FOLDER 29 | cp -rf ../data $FOLDER/data 30 | zip "$FOLDER.zip" -r $FOLDER 31 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /data/predefined_classes.txt: -------------------------------------------------------------------------------- 1 | ship 2 | plane 3 | dog 4 | person 5 | cat 6 | tv 7 | car 8 | -------------------------------------------------------------------------------- /demo/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/demo/demo.png -------------------------------------------------------------------------------- /demo/demo.xml: -------------------------------------------------------------------------------- 1 | 2 | demo 3 | demo 4 | /Users/haoyou/Codes/labelImg/demo/demo.png 5 | 6 | Unknown 7 | 8 | 9 | 512 10 | 290 11 | 3 12 | 13 | 0 14 | 15 | bndbox 16 | dog 17 | Unspecified 18 | 0 19 | 0 20 | 21 | 241 22 | 52 23 | 383 24 | 107 25 | 26 | 27 | 28 | bndbox 29 | person 30 | Unspecified 31 | 0 32 | 0 33 | 34 | 69 35 | 63 36 | 214 37 | 93 38 | 39 | 40 | 41 | robndbox 42 | dog 43 | Unspecified 44 | 0 45 | 0 46 | 47 | 157.0 48 | 170.0 49 | 166.0 50 | 56.0 51 | 3.039229 52 | 53 | 54 | 55 | robndbox 56 | dog 57 | Unspecified 58 | 0 59 | 0 60 | 61 | 252.5622 62 | 174.811 63 | 125.0 64 | 37.0 65 | 2.434159 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /demo/demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/demo/demo2.png -------------------------------------------------------------------------------- /demo/demo2.xml: -------------------------------------------------------------------------------- 1 | 2 | demo 3 | demo2 4 | /Users/haoyou/Codes/labelImg/demo/demo2.png 5 | 6 | Unknown 7 | 8 | 9 | 555 10 | 312 11 | 3 12 | 13 | 0 14 | 15 | robndbox 16 | person 17 | Unspecified 18 | 0 19 | 0 20 | 21 | 256.6015 22 | 155.1881 23 | 77.571 24 | 185.3599 25 | 3.070897 26 | 27 | 28 | 29 | robndbox 30 | person 31 | Unspecified 32 | 0 33 | 0 34 | 35 | 322.6694 36 | 180.9422 37 | 45.6029 38 | 126.3188 39 | 3.052234 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /demo/demo3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/demo/demo3.jpg -------------------------------------------------------------------------------- /demo/demo3.xml: -------------------------------------------------------------------------------- 1 | 2 | demo 3 | demo3 4 | /Users/haoyou/Codes/labelImg/demo/demo3.jpg 5 | 6 | Unknown 7 | 8 | 9 | 799 10 | 401 11 | 3 12 | 13 | 0 14 | 15 | bndbox 16 | dog 17 | Unspecified 18 | 0 19 | 0 20 | 21 | 354 22 | 162 23 | 499 24 | 269 25 | 26 | 27 | 28 | bndbox 29 | cat 30 | Unspecified 31 | 0 32 | 0 33 | 34 | 160 35 | 141 36 | 381 37 | 258 38 | 39 | 40 | 41 | robndbox 42 | person 43 | Unspecified 44 | 0 45 | 0 46 | 47 | 398.245 48 | 137.3459 49 | 48.3577 50 | 226.2906 51 | 1.134448 52 | 53 | 54 | 55 | robndbox 56 | tv 57 | Unspecified 58 | 0 59 | 0 60 | 61 | 570.4236 62 | 241.9665 63 | 234.4684 64 | 78.8656 65 | 3.098264 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /demo/demo4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/demo/demo4.png -------------------------------------------------------------------------------- /demo/demo4.xml: -------------------------------------------------------------------------------- 1 | 2 | hsrc 3 | 100000002 4 | /Users/haoyou/Library/Mobile Documents/com~apple~CloudDocs/OneDrive/hsrc/100000002.bmp 5 | 6 | Unknown 7 | 8 | 9 | 1146 10 | 818 11 | 3 12 | 13 | 0 14 | 15 | robndbox 16 | ship 17 | Unspecified 18 | 0 19 | 0 20 | 21 | 528.0957 22 | 392.4731 23 | 207.4283 24 | 783.2257 25 | 2.632232 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo/demo_v2.5.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/demo/demo_v2.5.gif -------------------------------------------------------------------------------- /demo/roLabelImg.gif: -------------------------------------------------------------------------------- 1 | 404: Not Found 2 | -------------------------------------------------------------------------------- /demo/rolabelimgdemo_v2.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/demo/rolabelimgdemo_v2.1.png -------------------------------------------------------------------------------- /icons/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/cancel.png -------------------------------------------------------------------------------- /icons/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/close.png -------------------------------------------------------------------------------- /icons/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/color.png -------------------------------------------------------------------------------- /icons/color_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/color_line.png -------------------------------------------------------------------------------- /icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/copy.png -------------------------------------------------------------------------------- /icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/delete.png -------------------------------------------------------------------------------- /icons/done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/done.png -------------------------------------------------------------------------------- /icons/done.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 27 | 29 | 36 | 40 | 44 | 48 | 52 | 56 | 57 | 64 | 68 | 72 | 76 | 80 | 84 | 85 | 92 | 96 | 100 | 104 | 108 | 112 | 113 | 121 | 125 | 129 | 133 | 137 | 141 | 142 | 143 | 145 | 147 | begin='' id='W5M0MpCehiHzreSzNTczkc9d' 148 | 150 | 151 | 153 | 154 | Adobe PDF library 5.00 155 | 156 | 158 | 160 | 162 | 163 | 2003-12-22T22:34:35+02:00 164 | 165 | 2004-04-17T21:25:50Z 166 | 167 | Adobe Illustrator 10.0 168 | 169 | 2004-01-19T17:51:02+01:00 170 | 171 | 172 | 174 | 175 | JPEG 176 | 177 | 256 178 | 179 | 256 180 | 181 | /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA 182 | AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK 183 | DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f 184 | Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER 185 | AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA 186 | AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB 187 | UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 188 | 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ 189 | qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy 190 | obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 191 | 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo 192 | +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 193 | FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F 194 | XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX 195 | Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY 196 | 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 342 | 343 | 344 | 345 | 346 | 348 | 349 | uuid:4b4d592f-95b8-4bcd-a892-74a536c5e52f 350 | 351 | 353 | 354 | image/svg+xml 355 | 356 | 357 | 359 | test.ai 360 | 361 | 362 | 363 | 364 | 365 | end='w' 366 | 367 | 372 | 376 | 380 | 384 | 388 | 392 | 396 | 400 | 401 | -------------------------------------------------------------------------------- /icons/edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/edit.png -------------------------------------------------------------------------------- /icons/expert1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/expert1.png -------------------------------------------------------------------------------- /icons/expert2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/expert2.png -------------------------------------------------------------------------------- /icons/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/eye.png -------------------------------------------------------------------------------- /icons/feBlend-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/feBlend-icon.png -------------------------------------------------------------------------------- /icons/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/file.png -------------------------------------------------------------------------------- /icons/fit-width.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/fit-width.png -------------------------------------------------------------------------------- /icons/fit-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/fit-window.png -------------------------------------------------------------------------------- /icons/fit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/fit.png -------------------------------------------------------------------------------- /icons/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/help.png -------------------------------------------------------------------------------- /icons/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/labels.png -------------------------------------------------------------------------------- /icons/labels.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 31 | 33 | 34 | 35 | 36 | 43 | 47 | 51 | 55 | 59 | 63 | 64 | 65 | 66 | 73 | 77 | 81 | 85 | 89 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 107 | 111 | 115 | 119 | 123 | 127 | 128 | 129 | 137 | 141 | 145 | 149 | 153 | 157 | 158 | 159 | 166 | 170 | 174 | 178 | 182 | 186 | 190 | 194 | 198 | 199 | 200 | 201 | 202 | 203 | 211 | 215 | 219 | 223 | 227 | 231 | 232 | 233 | 241 | 245 | 249 | 253 | 257 | 261 | 265 | 269 | 273 | 274 | 275 | 282 | 286 | 290 | 294 | 298 | 302 | 306 | 310 | 314 | 318 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 399 | 411 | 413 | 414 | 415 | 421 | 422 | 428 | 429 | 432 | 439 | 440 | 441 | 442 | begin='' id='W5M0MpCehiHzreSzNTczkc9d' 444 | 446 | 447 | 449 | 451 | 452 | Adobe PDF library 5.00 453 | 454 | 456 | 458 | 460 | 461 | 2004-01-26T11:58:28+02:00 462 | 463 | 2004-03-28T20:41:40Z 464 | 465 | Adobe Illustrator 10.0 466 | 467 | 2004-02-16T23:58:32+01:00 468 | 469 | 470 | 472 | 473 | JPEG 474 | 475 | 256 476 | 477 | 256 478 | 479 | /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA 480 | AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK 481 | DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f 482 | Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER 483 | AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA 484 | AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB 485 | UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 486 | 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ 487 | qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy 488 | obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 489 | 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo 490 | +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 491 | FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYqlvmDzFo 492 | 3l7TJdT1e5W1tItuTbszHoiKN2Y+AxV4j5g/5ydvTcMnl/SYlgU0Se/LOzDxMcTIF/4M4qk//QzP 493 | nv8A5YNL/wCRVx/2UYq7/oZnz3/ywaX/AMirj/soxV3/AEMz57/5YNL/AORVx/2UYq7/AKGZ89/8 494 | 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 677 | f//Z 678 | 679 | 680 | 681 | 682 | 684 | 685 | uuid:4ee3f24b-6ed2-4a2e-8f7a-50b762c8da8b 686 | 687 | 689 | 690 | image/svg+xml 691 | 692 | 693 | 695 | mime.ai 696 | 697 | 698 | 699 | image/svg+xml 702 | end='w' 704 | 705 | 800 | Labels 819 | -------------------------------------------------------------------------------- /icons/new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/new.png -------------------------------------------------------------------------------- /icons/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/next.png -------------------------------------------------------------------------------- /icons/objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/objects.png -------------------------------------------------------------------------------- /icons/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/open.png -------------------------------------------------------------------------------- /icons/open.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | image/svg+xml 42 | 43 | 45 | 52 | 54 | 55 | 56 | 58 | 60 | 67 | 68 | 70 | 77 | 81 | 85 | 89 | 92 | 95 | 98 | 101 | 104 | 105 | 109 | 116 | 120 | 124 | 128 | 132 | 135 | 138 | 141 | 144 | 147 | 150 | 153 | 154 | 158 | 165 | 169 | 173 | 177 | 181 | 185 | 188 | 191 | 194 | 195 | 199 | 206 | 210 | 214 | 218 | 222 | 226 | 229 | 232 | 235 | 236 | 240 | 247 | 251 | 255 | 258 | 261 | 264 | 265 | 269 | 276 | 280 | 284 | 288 | 291 | 294 | 297 | 300 | 303 | 304 | 308 | 315 | 319 | 323 | 327 | 330 | 333 | 336 | 339 | 342 | 343 | 347 | 354 | 358 | 362 | 365 | 368 | 371 | 372 | 376 | 383 | 387 | 391 | 395 | 399 | 403 | 407 | 411 | 415 | 418 | 421 | 424 | 425 | 429 | 433 | 440 | 444 | 448 | 451 | 454 | 457 | 458 | 462 | 466 | 473 | 477 | 481 | 484 | 487 | 490 | 491 | 495 | 502 | 506 | 510 | 514 | 517 | 520 | 523 | 526 | 529 | 530 | 534 | 541 | 545 | 549 | 552 | 555 | 558 | 559 | 563 | 567 | 574 | 575 | 576 | 577 | -------------------------------------------------------------------------------- /icons/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/prev.png -------------------------------------------------------------------------------- /icons/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/quit.png -------------------------------------------------------------------------------- /icons/robjects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/robjects.png -------------------------------------------------------------------------------- /icons/save-as.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/save-as.png -------------------------------------------------------------------------------- /icons/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/save.png -------------------------------------------------------------------------------- /icons/save.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 27 | 29 | 31 | 33 | begin='' id='W5M0MpCehiHzreSzNTczkc9d' 34 | 36 | 37 | 39 | 40 | Adobe PDF library 5.00 41 | 42 | 44 | 46 | 48 | 49 | 2004-02-04T02:08:51+02:00 50 | 51 | 2004-03-29T09:20:16Z 52 | 53 | Adobe Illustrator 10.0 54 | 55 | 2004-02-29T14:54:28+01:00 56 | 57 | 58 | 60 | 61 | JPEG 62 | 63 | 256 64 | 65 | 256 66 | 67 | /9j/4AAQSkZJRgABAgEASABIAAD/7QAsUGhvdG9zaG9wIDMuMAA4QklNA+0AAAAAABAASAAAAAEA 68 | AQBIAAAAAQAB/+4ADkFkb2JlAGTAAAAAAf/bAIQABgQEBAUEBgUFBgkGBQYJCwgGBggLDAoKCwoK 69 | DBAMDAwMDAwQDA4PEA8ODBMTFBQTExwbGxscHx8fHx8fHx8fHwEHBwcNDA0YEBAYGhURFRofHx8f 70 | Hx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8fHx8f/8AAEQgBAAEAAwER 71 | AAIRAQMRAf/EAaIAAAAHAQEBAQEAAAAAAAAAAAQFAwIGAQAHCAkKCwEAAgIDAQEBAQEAAAAAAAAA 72 | AQACAwQFBgcICQoLEAACAQMDAgQCBgcDBAIGAnMBAgMRBAAFIRIxQVEGE2EicYEUMpGhBxWxQiPB 73 | UtHhMxZi8CRygvElQzRTkqKyY3PCNUQnk6OzNhdUZHTD0uIIJoMJChgZhJRFRqS0VtNVKBry4/PE 74 | 1OT0ZXWFlaW1xdXl9WZ2hpamtsbW5vY3R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo+Ck5SVlpeYmZ 75 | qbnJ2en5KjpKWmp6ipqqusra6voRAAICAQIDBQUEBQYECAMDbQEAAhEDBCESMUEFURNhIgZxgZEy 76 | obHwFMHR4SNCFVJicvEzJDRDghaSUyWiY7LCB3PSNeJEgxdUkwgJChgZJjZFGidkdFU38qOzwygp 77 | 0+PzhJSktMTU5PRldYWVpbXF1eX1RlZmdoaWprbG1ub2R1dnd4eXp7fH1+f3OEhYaHiImKi4yNjo 78 | +DlJWWl5iZmpucnZ6fkqOkpaanqKmqq6ytrq+v/aAAwDAQACEQMRAD8A9U4q7FXYq7FXYq7FXYq7 79 | FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7F 80 | XYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FX 81 | Yq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXYq7FXY 82 | q7FXzd+b/wDzlWum3k+h+QxFc3EJMdzrkoEkKuNiLZPsyU/nb4fAEb50vZ/YXEBPLsP5v62meXue 83 | A3v5mfmprl080vmLVriXdjHBcTIi17rFCVRfoXOghocEBQhH5NJmepUf8Tfmj/1dtb/6SLv/AJqy 84 | f5fD/Nj8gjxPN3+JvzR/6u2t/wDSRd/81Y/l8P8ANj8gviebv8Tfmj/1dtb/AOki7/5qx/L4f5sf 85 | kF8Tzd/ib80f+rtrf/SRd/8ANWP5fD/Nj8gviebv8Tfmj/1dtb/6SLv/AJqx/L4f5sfkF8Tzd/ib 86 | 80f+rtrf/SRd/wDNWP5fD/Nj8gviebv8Tfmj/wBXbW/+ki7/AOasfy+H+bH5BfE83f4m/NH/AKu2 87 | t/8ASRd/81Y/l8P82PyC+J5u/wATfmj/ANXbW/8ApIu/+asfy+H+bH5BfE83f4m/NH/q7a3/ANJF 88 | 3/zVj+Xw/wA2PyC+J5u/xN+aP/V21v8A6SLv/mrH8vh/mx+QXxPN3+JvzR/6u2t/9JF3/wA1Y/l8 89 | P82PyC+J5u/xN+aP/V21v/pIu/8AmrH8vh/mx+QXxPN3+JvzR/6u2t/9JF3/AM1Y/l8P82PyC+J5 90 | u/xN+aP/AFdtb/6SLv8A5qx/L4f5sfkF8Tzd/ib80f8Aq7a3/wBJF3/zVj+Xw/zY/IL4nm7/ABN+ 91 | aP8A1dtb/wCki7/5qx/L4f5sfkF8Tzd/ib80f+rtrf8A0kXf/NWP5fD/ADY/IL4nm7/E35o/9XbW 92 | /wDpIu/+asfy+H+bH5BfE82j5t/M+Aes2ta3EI/i9U3N2vGnfly2x/LYT/DH5BePzZ15C/5yh/Mb 93 | y7cxRaxcHzDpQIEsF2f9IC9zHc058v8AX5D9ea/VdiYcg9I4JeXL5NkchD688jeefLvnby/DrmhT 94 | +rayEpLE4CywygAtFKtTxYV+RG4qDnH6nTTwT4JjdyIytkGY6XYq7FXYq7FXYq7FXjX/ADlH+YV1 95 | 5W8hppunymHU/MMj2qSqaMltGoNwynxPNE/2WbrsPSDLl4pfTDf49GvJKg+VPy+8lP5ivecqM9rG 96 | 4jWFaqZpTvw57cVUULGvcfMdtYFk7Ac3Ua3VHGAI/XLk+jNK/LfSLS0SK4JYqDSGCkUCV3PBVAPX 97 | vtXwzWT7TlfoAA+11f5Xi3mTIo608meV/wBL2lnLbSSLcc/92sB8Kk70IOU5+0s4xSmCPT5NuDRY 98 | pZBEjmyu2/KnydcFgliF4ip5TT/wY5ov5f1f877B+p2/8kaf+b9pVv8AlT3lL/lkT/kdcf1w/wAv 99 | az+d9kf1I/kjTfzftLR/J/yl/wAsif8AI65/rj/L2s/nfZH9S/yRpv5v2lafyg8p/wDLKn/I65/r 100 | h/l3Wfzvsj+pf5J03837S0fyh8p/8sqf8jrn+uP8u6z+d9kf1L/JOm/m/aWj+UXlP/llj/5HXP8A 101 | XH+XdZ/O+yP6l/knTfzftLX/ACqPyn/yzR/8jrn+uH+XNb/O+yP6l/knTd32lr/lUflX/lmj/wCR 102 | 1z/XB/Lmt/nfZH9S/wAk6bu+0u/5VD5W/wCWaP8A5HXP9cf5d1n877I/qX+SdN/N+0u/5VB5Y/5Z 103 | ov8Akdc/1x/l3Wfzvsj+pf5J03837S7/AJU/5a/5Zov+R1z/AFx/l3Wfzvsj+pf5J03837S7/lT3 104 | lv8A5Zov+R1z/XB/L2s/nfZH9S/yRpv5v2l3/KnfLv8AyzRf8jrn+uP8vaz+d9kf1L/JGm/m/aXf 105 | 8qc8v/8ALNF/yOuf64/y9rP532R/Uv8AJGm/m/aXf8qb0H/lmh/5HXP9cf5f1n877I/qX+SNN/N+ 106 | 0u/5U1oP/LND/wAjrn+uD+X9Z/O+wfqT/JGn/m/aVk/5P6BDBJM1rEVjUswE1xWg8KnH/RBq/wCd 107 | 9g/Uv8kaf+b9pYp5i8oeXLOGBoLQo0j8SRJIe3+Uxza9ldq6jNKQnLkO4Ov1/Z2HGAYj7SkreXdK 108 | IoEZD/Mrmo+Vaj8M3I1eR1fgRee/mD+W8NxE91ZIPrhq0UygL6rbt6ctNubfssevy6XwmJjbYjo5 109 | ml1csUhGRuB+xJP+cfvzGvfJvny1T1T+iNXdLTUbcn4SWNIpPZkduvgTmq7Z0gy4Sf4obj9L0WOV 110 | F93xSJLGsiGqOAyn2O+cK5K7FXYq7FXYq7FXYq+R/wDnM65lbzjoFsT+6i05pEG/2pJ2VvbpGM6/ 111 | 2cH7uR/pfocfNzb/ACCs7caXZzBAJPQuJS3fn9ZMXL/gNs2uvkRirvl+h0GffUm+kfx972EnNKyU 112 | LXfzNpZ/4y/8QOOo/wAWn8PvbdN/fRei6SPjl/1R+vOWDvyjyMsQsIwoWkYVWEYULSMKFhGSVrFV 113 | wOBVwOBVwOBK4HFVwOBK4HAq4HAlcDgVQ1I/7jrn/jE36siUh5X5uH+j23tL/DN52F9U/c6vtX6Q 114 | x0nOidEgNZodNmBAP2aE9jzG4+jL9P8AWGrL9JfNGuSmDzPqEsICGK9maNRsF4ykgCnhmRKArhel 115 | 08iccT5B+iHk+4afQbcsalBx+8Bv+Ns8wdknWKuxV2KuxV2KuxV8hf8AOZn/ACneif8AbLH/AFES 116 | 52Hs7/dS/rfoDj5uaO/IUf7gbI/8ulx/1GnNlr/7v/O/Q6DN/jEv6v6nqxOahksshXzJpv8Az0/4 117 | gcjqf8Xn8PvbdL/exei6SPjk/wBUfrzlw9AmBGTYrSMKrCMKFpGFVhGFC0jChYRklaxVcDgVcDgV 118 | cDgSuBxVcDgSuBwKuBwJUdRP+4+5/wCMTfqyJSHlvmwf6Lb+0n8M3XYX1S9zq+1fpDwzzXoX1nzD 119 | eT8a82U1/wBgBm1y6fikS6qGfhFJt5T076lomoJSnOSM/dTMzQYuCTj6rJxh4h5k/wCUi1T/AJjJ 120 | /wDk62bM83fab+6j/VH3P0N8jf8AHBj+Y/5NpnlztGQYq7FXYq7FXYq7FXyF/wA5mf8AKd6J/wBs 121 | sf8AURLnYezv91L+t+gOPm5ph+Q4/wCddsj/AMutx/1Gtmx1/wBH+d+h0Gb/ABiX9X9T1InNUl2n 122 | b+Y9P/56f8QOQ1X+Lz+H3t+l/vYvRtJH7yT/AFR+vOWDv0xIySFhGSQtIwqsIwoWkYVWEYULSMKF 123 | hGSVrFVwOBVwOBVwOBK4HFVwOBK4HAqjf/8AHPuf+MTfqyEkh5j5rH+iQ/65/Uc3XYf1y9zre1Pp 124 | DDpbGzkcu8QZ26k50weeMQoXVvDDZyrEgQNQkD5jLMX1BhMbPmrzN/ykmrf8xlx/ydbMp6XTf3cf 125 | 6o+5+hnkb/jgx/Mf8m0zy52bIMVdirsVdirsVdir5C/5zM/5TvRP+2WP+oiXOw9nf7qX9b9AcfNz 126 | TL8iR/zrFif+Xa4/6jWzYa76f879Doc/9/L3fqenE5rEL9KFfMNh85P+IHK9X/cT+H3uRpP72L0f 127 | SR+8k/1f45yzv0xIwqtIwoWEZJC0jCqwjChaRhVYRhQtIwoWEZJWsVXA4FXA4FXA4ErgcVXA4EqV 128 | 9/vBc/8AGJv1ZCXJIea+ah/ocfsx/wCInNx2H9cvcHW9qfQGIE507z6HvN7dx8v1jLMfNhPk+Z/N 129 | H/KTav8A8xtx/wAnWzJek0/93H+qPufoX5G/44MfzH/JtM8vdmyDFXYq7FXYq7FXYq+Qv+czP+U7 130 | 0T/tlj/qIlzsPZ3+6l/W/QHHzc0z/Isf86nYH/l3uP8AqNbM/W8v879Doc/9/L3fqelk5rkK2j76 131 | /ZfN/wDiBynWf3Evx1cjSf3oej6UP3r/AOr/ABzl3fpliq0jCq0jChYRkkLSMKrCMKFpGFVhGFC0 132 | jChYRklaxVcDgVcDgVcDgSuBxVTvP94rn/jE36shPkyDzjzUP9BX5n/iJzbdifXL4Ou7U+gfFhhO 133 | dS86pXG8TD5frycebGXJ8z+av+Un1j/mNuf+TrZkh6TT/wB3H+qPufoV5G/44MfzH/JtM8vdmyDF 134 | XYq7FXYq7FXYq+Qv+czP+U70T/tlj/qIlzsPZ3+6l/W/QHHzc01/I0f86fp5/wCKLj/qNbM7W8v8 135 | 79Dos/8AfH3fqejE5gMEVoe+u2fzf/iByjW/3Evx1cnR/wB4Ho+l/wB4/wAv45y7v0xxV2KrSMKr 136 | SMKFhGSQtIwqsIwoWkYVWEYULSMKFhGSVrFVwOBVwOBVwOBKy6P+h3H/ABib9WQnySHnnmkf6APY 137 | t/xE5texPrPwdf2n9A+LByc6t5xTfcEZIIL5p82f8pTrP/Mdc/8AJ5syRyek0/8Adx9w+5+hPkb/ 138 | AI4MfzH/ACbTPL3ZsgxV2KuxV2KuxV2KvkL/AJzM/wCU70T/ALZY/wCoiXOw9nf7qX9b9AcfNzTf 139 | 8jx/zpWnH/im4/6jHzO1n6f0Oi1H98fd+p6ETmE1o3y/vrdr82/4gcxtd/cycrR/3gej6b/eP8v4 140 | 5y7v0wxV2KuxVaRhVaRhQsIySFpGFVhGFC0jCqwjChaRhQsIyStYquBwKuBwKtuT/olx/wAYm/Vk 141 | J8mUXn/mkf7jj/sv+InNp2L/AHh+Dr+0/oHxYGTnWvONDdgMUPmnzb/yletf8x9z/wAnmzIjyelw 142 | f3cfcH6EeRv+ODH8x/ybTPMHZMgxV2KuxV2KuxV2KvkL/nMz/lO9E/7ZY/6iJc7D2d/upf1v0Bx8 143 | 3NOPyRH/ADo2mn/im4/6jHzN1fP4/odHqP70+5n5OYjUmHlzfWrb5t/xA5ia7+5k5Wi/vA9H07+8 144 | f5fxzmHfo/FXYq7FXYqtIwqtIwoWEZJC0jCqwjChaRhVYRhQtIwoWEZJWsVXA4Fan/3luP8AjE36 145 | shk5MosD80D/AHGt8m/4gc2XY394fg4Haf0fN56TnXvNLod5VHz/AFYJclD5p83/APKWa3/zH3X/ 146 | ACebMiPIPS4P7uPuD9CPI3/HBj+Y/wCTaZ5g7JkGKuxV2KuxV2KuxV8hf85mf8p3on/bLH/URLnY 147 | ezv91L+t+gOPm5p1+SYp5B0w/wDFVx/1GPmZq/q+P6HR6n+9PuZ0TmM0pr5Y31iD5t/xA5h6/wDu 148 | i5mi/vA9G0/7b/LOYd8jsVdirsVdirsVWkYVWkYULCMkhaRhVYRhQtIwqsIwoWkYULCMkrWKul/3 149 | mn/4xt+rK8nJMebB/NA/3Fyf6r/8QObHsb+8Pw+9we0/o+bzgnOxeZVLXe4QfP8AUcjPkmPN81ec 150 | f+Uu1z/toXX/ACebL4fSHpcH0R9wfoP5G/44MfzH/JtM8xdkyDFXYq7FXYq7FXYq+Qv+czP+U70T 151 | /tlj/qIlzsPZ3+6l/W/QHHzc08/JUf8AIPNLP/Fdx/1GSZl6r6z7/wBDpNT/AHh9zNicocdOPKu+ 152 | rQ/M/wDEGzB7Q/ui5uh+sPRbEhXappt3zmXfI3mn8w+/FXeon8w+/FWvUj/mH3jFXepH/MPvGKu9 153 | WP8AnH3jFXepF/Ov3jFVpeP+dfvGG1Wl4/51+8YbQtLJ/Mv3jDa0tJT+ZfvGHiCKWnj/ADL/AMEP 154 | 64eILS08f5l/4If1w8QRS0qP5l/4If1w8YWlpUfzL/wS/wBceMIorCn+Uv8AwS/1w8YXhKyai289 155 | WXeNgPiB3I+eRnIEJiGFeZx/uKm/1H/4gc2PY/8AefL73B7S+j5vNCc7N5dWsN7uMfP/AIichl+k 156 | so83zX5z/wCUw13/ALaF1/yffL8f0j3PS4foj7g/QbyN/wAcGP5j/k2meYuyZBirsVdirsVdirsV 157 | fIX/ADmZ/wAp3on/AGyx/wBREudh7O/3Uv636A4+bmnv5Lj/AJBxpZ/yLj/qMkzK1X1n3/odJqv7 158 | w+5mZOVOOmvly5jtrwTyAlIzuFpXdSO9Mw9bjM4cI6uVpJiMrLK/8T2H++5fuX/mrNL/ACdk7x+P 159 | g7b85DuLX+JbD/fcv3L/AM1Y/wAnZO8fj4L+ch3Fr/Elj/vuX7l/5qx/k7J3j8fBfzkO4tf4jsf9 160 | 9y/cv/NWP8nZO8fj4L+ch3Fo+YrH/fcv3L/zVj/J2TvH4+C/nIdxW/4hsv5JPuX/AJqx/k7J3j8f 161 | BfzkO4tfp+y/kk+5f+asf5Oyd4/HwX85DuLX6es/5JPuX/mrH+TsnePx8F/OQ7i1+nbP+ST7l/5q 162 | x/k7J3j8fBfzkO4tfpy0/kk+5f64/wAnZO8fj4L+ch3Fr9N2n8kn3L/XH+TsnePx8F/OQ7i0datf 163 | 5JPuX+uP8nZO8fj4L+ch3Fb+mLX+R/uH9cf5Oyd4/HwX85DuLX6Xtv5H+4f1x/k7J3j8fBfzkO4t 164 | fpa2/lf7h/XH+TsnePx8F/OQ7i0dVt/5X+4f1x/k7J3j8fBfzkO4tHVLf+V/uH9cf5Oyd4/HwX85 165 | DuKW6/dxz6XcKgYFY5DvT+Q++bDs7TSx5Bdbkfe4etzicNvN5sTnWPOojTN7+If63/ETleb6Cyhz 166 | fNnnX/lMte/7aN3/AMn3y/H9I9z02H6B7g/QXyN/xwY/mP8Ak2meYuxZBirsVdirsVdirsVfIX/O 167 | Zn/Kd6J/2yx/1ES52Hs7/dS/rfoDj5uaf/kyP+QZ6Uf8m4/6jJMytT/eH8dHS6r6z7mXk5W4rSyy 168 | JXgxWvWhIxMQVEiOTjdXH+/X/wCCOPAO5eM9603Vz/v1/wDgjh4I9y8Z71pu7n/fz/8ABHDwR7kc 169 | Z71pu7r/AH8//BH+uHw49y8cu9aby6/39J/wR/rh8OPcEccu9ab27/3/ACf8E39cPhx7gjjl3rTe 170 | 3f8Av+T/AINv64fDj3BfEl3rTfXn+/5P+Db+uHw49wR4ku8rTfXv/LRJ/wAG39cPhR7gviS7ytN/ 171 | e/8ALRJ/wbf1w+FHuCPEl3ladQvv+WiX/g2/rh8KPcEeJLvK06hff8tMv/Bt/XD4Ue4L4ku8rTqN 172 | /wD8tMv/AAbf1w+FDuCPEl3ladRv/wDlpl/4Nv64fBh3D5L4ku8rTqWof8tUv/Bt/XD4MO4fJHiy 173 | 7ytOp6h/y1Tf8jG/rh8GHcPkjxZd5aOp6j/y1Tf8jG/rh8GHcPkviy7ypvqN+6lWuZWVhRlLsQQe 174 | xFcIwwHQfJByS7yhScta0Xo++pQj/W/4icq1H0Fnj+p82+d/+Uz1/wD7aN3/AMn3y7F9I9z02H6B 175 | 7g/QTyN/xwY/mP8Ak2meZOxZBirsVdirsVdirsVfIX/OZn/Kd6J/2yx/1ES52Hs7/dS/rfoDj5ub 176 | IfybH/ILtJPtcf8AUZLmTqP70/jo6XVfWWVE5FxFpOFVpOFDCLz82fLtrdz2slteGSCRonKpFQlC 177 | VNKyDbbLRjLLgKgfzh8tf8s17/wEX/VXD4ZXwytP5weWv+Wa9/4CL/qrjwFHhlo/m95b/wCWa8/4 178 | CL/qrh4Cvhlo/m75b/5Zrz/gIv8Aqrh4V8Mrf+Vt+XD/AMe15/wEX/VXCIFHhF3/ACtjy6f+Pa8/ 179 | 4CL/AKqZMYijwy1/ytXy8f8Aj3u/+Ai/6qZYNPJHhl3/ACtPy+f+Pe7/AOAj/wCqmTGll5I8Mtf8 180 | rQ0A/wDHvd/8BH/1UywaKfkjwy7/AJWboR/497r/AICP/qpkx2fPvCOAtf8AKytDP+6Lr/gI/wDq 181 | pkx2bk7x+PgjgLY/MXRT0guf+Bj/AOa8P8nZO8fj4LwFseftIPSG4/4FP+a8f5Pn3j8fBHAUTY+b 182 | dOvbqO2iimWSQkKXVQNhXejHwyGTSSiLNIMSE4JzGYLCcKFpOFCN0PfVYB/rf8QOU6n+7LZi+oPm 183 | 7zx/ymvmD/tpXn/J98uxfQPcHpsX0D3B+gfkb/jgx/Mf8m0zzJ2LIMVdirsVdirsVdir5C/5zM/5 184 | TvRP+2WP+oiXOw9nf7qX9b9AcfNzZF+To/5BVpB9rj/qMlzI1H98fx0dNq/qLJycXDWk4ULScKEq 185 | /IbT7OTVvMty0S/Wm1BoRPQcxHVmKqT0BPXNL25M3EdKd52bEUS9s/RNv/O/3j+maC3Zu/RNv/O/ 186 | 3j+mNq79E2/87/eP6Y2rv0Tb/wA7/eP6Y2rv0Tb/AM7/AHj+mNq79E2/87/eP6Y2rv0Tb/zv94/p 187 | jau/RNv/ADv94/pjau/RNv8Azv8AeP6Y2rv0Tb/zv94/pjau/RNv/O/3j+mNq80/PXTbMeUJmaMP 188 | LbyQvBKwBZC8gRqEU6qc6L2YyyjqwAdpA38nA7RiDiJ7nzykeekEvOpz5cSmsWx9z/xE5jak+gsZ 189 | cmeE5qWhaThQtJwqj/L2+sW4/wBf/iDZRq/7s/jq2YfqD5v89f8AKb+Yf+2nef8AUQ+W4foHuD02 190 | L6R7n6BeRv8Ajgx/Mf8AJtM8zdiyDFXYq7FXYq7FXYq+Qv8AnMz/AJTvRP8Atlj/AKiJc7D2d/up 191 | f1v0Bx83Nkn5Pj/kEujn/mI/6jJcvz/35/HR02r+osjJyThLScKFhOSQgvyCamo+YR46o3/G2aHt 192 | z6o+533Zv0l7pmhdk7FXYq7FXYq7FXYq7FXYq7FXYq8w/PPfytdr7wf8nRm/9m/8bj7pfc4PaP8A 193 | cn4PntI89IJebTXQUpqlufc/8ROY+c+gsZcmZk5rWhaThVaThQmPlrfW7Yf6/wDybbMfWf3R/HVt 194 | wfWHzh58/wCU58xf9tO8/wCoh8twfRH3B6fH9I9z9AfI3/HBj+Y/5NpnmbsGQYq7FXYq7FXYq7FX 195 | yF/zmZ/yneif9ssf9REudh7O/wB1L+t+gOPm5sm/KEf8gh0Y+9x/1GTZdm/vz+OgdPrOZT8nLHAW 196 | E5JC0nCqX/kO9NT8wf8AbUb/AI2zQ9ufVH3O+7N+kvdPUzQ07Jg/5n+a7ny3o9zq0CGY20cREHMx 197 | hvUnEfUA9OVemZmh03jZRC6u/utpz5eCBl3PIv8AoY3V/wDq1j/pKf8A5ozoR7NxP8f2ftdf/KR/ 198 | m/ay/wDLf81dQ826lcW0tsbQWypJyWZpOXJuNKELmu7U7JGliJCXFZ7nJ0ur8UkVVPZvUzR05rvU 199 | xpXepjSu9TGld6mNK71MaV3qY0rzP8625eXrlf8AjB/ydGb32c/xuPul9zg9o/3J+DwdI89FJebT 200 | PRkpqEJ9z+o5RmPpLCXJlJOYLStJwoWE4UJp5V31+1H/ABk/5NtmNrf7o/D727T/AFh84efv+U68 201 | x/8AbUvf+oh8swf3cfcHp8f0j3P0B8jf8cGP5j/k2meaOwZBirsVdirsVdirsVfIX/OZn/Kd6J/2 202 | yx/1ES52Hs7/AHUv636A4+bmyf8AKMf8gc0U/wCVcf8AUZNl2b/GD+OgdPrOZTsnLnXrScKrScKE 203 | s/I1qanr3/bTb/jbND22PVH3O/7N+kvb/UzROyeYfny9fJmoj/iu2/6i0zbdiD/CofH/AHJcTW/3 204 | R+H3vmQDPQ4wefep/kEeOuah/wAYov8Ak5nOe1Eaxw/rH7nZdmfUfc+l/UziXcu9TFXepirvUxV3 205 | qYq71MVd6mKvOPzhblolwPaH/k5m79nv8aj7j9zgdo/3J+DxdI89BJebTDTEpeRH3P6jlOQ7MZck 206 | /JzFaFhOFC0nCqbeUd/MVoP+Mn/Jpsxdf/cy+H3hu031h84/mB/ynnmT/tqXv/UQ+Waf+7j/AFR9 207 | z0+P6R7n6AeRv+ODH8x/ybTPNHYMgxV2KuxV2KuxV2KvkL/nMz/lO9E/7ZY/6iJc7D2d/upf1v0B 208 | x83NlP5TD/kC+iH/AC7n/qMmy3L/AIzL8dA6jWcym5OZDrlpOFC0nChKfyUbjqmue+pN/wAbZpO3 209 | h6of1Xf9m/SXtXqZz9Oyeafnm9fKOoD/AIrt/wDqKXNz2CP8Lh/nf7kuJrv7o/D73zaFz0mMHnre 210 | nfkWeOt33/GKP/k5nMe1kaxQ/rH7nZ9l/Ufc+j/UzhKdy71MaV3qY0rvUxpXepjSu9TGld6mNK8/ 211 | /NduWlzL7Rf8nM3XYH+NR+P3OD2l/cn4PJEjzvSXmkbYpS4Q/wCfTKpnZjLkmpOUtC0nCq0nJITj 212 | ybv5lsx/xk/5NPmH2h/cy+H3hv0394Hzl+YP/KfeZf8Atq3v/US+Waf+7j/VH3PTw+kPv/yN/wAc 213 | GP5j/k2meaOwZBirsVdirsVdirsVfIX/ADmZ/wAp3on/AGyx/wBREudh7O/3Uv636A4+bmyv8qB/ 214 | yBPRD/xZc/8AUZNlmT/GpfjoHUa1MycynWrScKFhOFUn/JxuOqa1/wBtJv8AjbNR7QD1Q/qu+7M+ 215 | kvZfUznKdm83/Ox+XlW/H/Fdv/1Erm69nh/hkP8AO/3JcTXf3J+H3vncLnp8YvOPSvyUHDWL0+Mc 216 | f/E85P2u/uof1j9ztOy/qPufQ3qZwVO6d6mNK71MaV3qY0rvUxpXepjSu9TGlYJ+ZjcrGUe0X/E8 217 | 3HYX+Mx+P3OB2l/cn4PNEjzuSXmkVbpSRTlZLGXJFk5FpWk5JC0nChOvJG/miyH/ABl/5MvmF2l/ 218 | cS+H3hyNL/eD8dHzn+Yf/Kf+Zv8AtrX3/US+T0391H+qPueoh9Iff3kb/jgx/Mf8m0zzVz2QYq7F 219 | XYq7FXYq7FXyF/zmZ/yneif9ssf9REudh7O/3Uv636A4+bmyz8qv/JHaGf8Aiy5/6jJ8nk/xuXu/ 220 | QHUa1MCczHWLCcKrScKEk/KN+Gqaz/20W/42zV+0Y3x/1Xfdl/SXr31gZzVO0Yv520E+YLSSwbms 221 | EyIHkjKhgUk9Tbl8hmXodXLTZRliATG+fmKas2IZImJ6sFH5J2Q/3ddffF/TOh/0W5/5kPt/W4P8 222 | lw7ynvlX8v18vXbz25mkMoVX9QpQBWrtxAzV9pdsZNXERkAOHutyNPpI4iSDzei/WBmnpy3fWBjS 223 | u+sDGld9YGNK76wMaV31gY0rvrAxpWGfmA4kt5B/kx/8Tzbdi/4wPj9zgdpf3J+DAkjztCXmldEp 224 | vkbYy5Licm0LScKFhOFU98ib+a7H/nr/AMmXzB7T/wAXl8PvDkaT+8H46PnT8xf/ACYPmf8A7a19 225 | /wBRL5PTf3Uf6o+56iHIPv3yN/xwY/mP+TaZ5q57IMVdirsVdirsVdir5C/5zMB/x1oh7fosf9RE 226 | udh7O/3Uv636A4+bmyz8qv8AyRuh07S3Ffb/AEyfJz/xuXu/QHUa3kjSczXWLScKFpOFDH/ywfhq 227 | OsH/AJf2/W2a72lG+P8AqO+7L+kvT/rXvnMU7R31r3xpXfWvfGld9a98aV31r3xpXfWvfGld9a98 228 | aV31r3xpXfWvfGld9a98aV31r3xpWM+bpPUiYeyf8Szadj/4wPj9zg9pf3J+DFUjzsCXmVVkpGTg 229 | id2MuSHJy9oWE4VWk4UJ95CqfNljQbD1a/8AIl8wO1P8Xl8PvDkaP+8H46PnX8xf/Jg+Z/8AtrX3 230 | /US+T0v91H+qPuephyD798jf8cGP5j/k2meaueyDFXYq7FXYq7FXYq+b/wDnMvyrcXGj6F5ngQtH 231 | YSSWV6QK8VuOLxMfBQ8bLXxYZ0vs7nAlLGeu4+DTmHVif/OOXm+xvdGvfImoTiO5LvdaSXbZlIDS 232 | RINt0ZfUp1ILeGbPtDGYTGUfF12pxcQZ/fafeWUhjuIytDQPT4W+Ry3FljMWC6acDHmhCcta1hOF 233 | Uo/KW39fzBf2/X1dQYU/4LNf7UHfH/Ud92V9Je4/4U/yPwzkuN2tO/wp/kfhjxrTv8Kf5H4Y8a07 234 | /Cn+R+GPGtO/wp/kfhjxrTv8Kf5H4Y8a07/Cn+R+GPGtO/wp/kfhjxrTv8Kf5H4Y8a07/Cn+R+GP 235 | GtO/wp/kfhjxrTz78wrH6lf/AFelKxI1Pmx/pm27GN5x8fucDtP+5PwYmkedcS8wuuEpbufb+OMD 236 | 6mMuSWE5ltK0nChyJJK4jjUu7bKqgkk+wGJIAsqBfJldi1p5F0G982+Yf3BjjMdlZsQsskjbqig/ 237 | tvxoB2FSds0Wu1H5iQxY9+8u20OlINl82eV7HUPNvny1WWs1zqF4bm8cDqC5lmb2rvT3zK1mUYMB 238 | PdGh9wd/AWafoD5TtzBo6L2LEj5ABf8AjXPPHLTjFXYq7FXYq7FXYql/mDQdL8waLeaLqsIuNPv4 239 | mhuIj3Vu4PZlO6nsd8sxZZY5CUeYQRb4V/NL8oPNv5a656pEs2kiX1NL1uDko+FqpzZf7qVdtvHd 240 | Sc7vQ9o49TGuUusfxzDjTgQmOjf85K/mRp1klrMbLUymy3F5C5loBQAtDJCG+ZFfE4z7KxSN7j3O 241 | OcUSj/8Aoaf8wf8Aq36T/wAibn/soyH8kYu+X2fqR4Ad/wBDT/mD/wBW/Sf+RNz/ANlGP8kYu+X2 242 | fqXwAoN/zkl5puryK6v9OtRJACIHsXmtXUk9SzvcfgBlObsSEuUiPfv+puxejkjP+hnPMn++bz/u 243 | JS/9U8xv9Dw/n/7H9rd4rv8AoZzzJ/vm8/7iUv8A1Tx/0PD+f/sf2r4rv+hnPMn++bz/ALiUv/VP 244 | H/Q8P5/+x/aviu/6Gc8yf75vP+4lL/1Tx/0PD+f/ALH9q+K7/oZzzJ/vm8/7iUv/AFTx/wBDw/n/ 245 | AOx/aviu/wChnPMn++bz/uJS/wDVPH/Q8P5/+x/aviu/6Gc8yf75vP8AuJS/9U8f9Dw/n/7H9q+K 246 | 7/oZzzJ/vm8/7iUv/VPH/Q8P5/8Asf2r4rv+hnPMn++bz/uJS/8AVPH/AEPD+f8A7H9q+K7/AKGc 247 | 8yf75vP+4lL/ANU8f9Dw/n/7H9q+K7/oZzzJ/vm8/wC4lL/1Tx/0PD+f/sf2r4qEm/5yR8yi8jvr 248 | awikvEBQyahNLdjgRSg4mBh1/mPyy7D2FCJ3kT7hX62vJLjFK3/Q0/5g/wDVv0n/AJE3P/ZRmT/J 249 | GLvl9n6nH8AO/wChp/zB/wCrfpP/ACJuf+yjH+SMXfL7P1L4Ad/0NP8AmD/1b9J/5E3P/ZRj/JGL 250 | vl9n6l8AO/6Gn/MH/q36T/yJuf8Asox/kjF3y+z9S+AGj/zlP+YJH/HP0ke/o3P/AGUY/wAkYu+X 251 | 2fqXwQwPXvM/nfz/AKxF9emm1O7qRa2cS0jiDHf040AVR0qx32+I5lxhi08L2iO9tjCtg+ifyJ/J 252 | ubQF+u36q+tXajmRusEXXiD+vxNPAE8f2r2l+YlUfoH2+f6nKhCn0XBCkEKQxiiRgKv0ZqGxfirs 253 | VdirsVdirsVdiqhfWFlf2slpewpcW0o4yQyKGVh7g4QSNwryzXP+cZ/yy1G4a4i0xIGY1McTyQrX 254 | 5RMo/wCFzYY+1tTAUJn40fvYHGEp/wChVPy+/wCWAf8ASXdf1yf8tar+f9kf1L4cXf8AQqn5ff8A 255 | LAP+ku6/rj/LWq/n/ZH9S+HF3/Qqn5ff8sA/6S7r+uP8tar+f9kf1L4cXf8AQqn5ff8ALAP+ku6/ 256 | rj/LWq/n/ZH9S+HF3/Qqn5ff8sA/6S7r+uP8tar+f9kf1L4cXf8AQqn5ff8ALAP+ku6/rj/LWq/n 257 | /ZH9S+HF3/Qqn5ff8sA/6S7r+uP8tar+f9kf1L4cXf8AQqn5ff8ALAP+ku6/rj/LWq/n/ZH9S+HF 258 | 3/Qqn5ff8sA/6S7r+uP8tar+f9kf1L4cXf8AQqn5ff8ALAP+ku6/rj/LWq/n/ZH9S+HF3/Qqn5ff 259 | 8sA/6S7r+uP8tar+f9kf1L4cXf8AQqn5ff8ALAP+ku6/rj/LWq/n/ZH9S+HF3/Qqn5ff8sA/6S7r 260 | +uP8tar+f9kf1L4cXf8AQqn5ff8ALAP+ku6/rj/LWq/n/ZH9S+HF3/Qqn5ff8sA/6S7r+uP8tar+ 261 | f9kf1L4cXf8AQqn5ff8ALAP+ku6/rj/LWq/n/ZH9S+HF3/Qqn5ff8sA/6S7r+uP8tar+f9kf1L4c 262 | Xf8AQqn5ff8ALAP+ku6/rj/LWq/n/ZH9S+HF3/Qqn5ff8sA/6S7r+uP8tar+f9kf1L4cW1/5xW/L 263 | 9WDCwWo33urkj7icT2zqv5/2R/UvhxZl5Z/KLy9oKcLG1t7RduRgT42p4sQN/c5g5tRkym5yMmQA 264 | DNrOytrSL04E4j9o9ST7nKUq+KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2K 265 | uxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Ku 266 | xV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2Kux 267 | V2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV2KuxV//2Q== 268 | 269 | 270 | 271 | 272 | 274 | 275 | uuid:f3c53255-be8a-4b04-817b-695bf2c54c8b 276 | 277 | 279 | 280 | image/svg+xml 281 | 282 | 283 | 285 | filesave.ai 286 | 287 | 288 | 289 | 290 | 291 | end='w' 292 | 293 | 295 | 299 | 303 | 307 | 311 | 315 | 323 | 327 | 331 | 335 | 339 | 343 | 344 | 348 | 352 | 359 | 363 | 367 | 371 | 375 | 379 | 380 | 384 | 391 | 395 | 399 | 403 | 407 | 411 | 412 | 416 | 420 | 428 | 432 | 436 | 440 | 444 | 448 | 449 | 453 | 457 | 464 | 468 | 472 | 476 | 480 | 484 | 488 | 492 | 496 | 497 | 501 | 509 | 513 | 517 | 521 | 525 | 529 | 533 | 537 | 541 | 542 | 549 | 556 | 560 | 564 | 568 | 572 | 576 | 577 | 581 | 589 | 593 | 597 | 601 | 605 | 609 | 613 | 617 | 621 | 625 | 629 | 633 | 634 | 638 | 646 | 650 | 654 | 658 | 662 | 666 | 667 | 674 | 678 | 679 | 680 | -------------------------------------------------------------------------------- /icons/undo-cross.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/undo-cross.png -------------------------------------------------------------------------------- /icons/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/undo.png -------------------------------------------------------------------------------- /icons/zoom-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/zoom-in.png -------------------------------------------------------------------------------- /icons/zoom-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/zoom-out.png -------------------------------------------------------------------------------- /icons/zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/icons/zoom.png -------------------------------------------------------------------------------- /issue_template.md: -------------------------------------------------------------------------------- 1 | 5 | 6 | ### issue1 7 | 8 | - **OS:** mac os 9 | 10 | - **PyQt version:** 5 11 | 12 | - **Error:** Segmentation fault: 11 13 | 14 | - **Solution**: open labelImg.py, go to line 175, comment this line. 15 | ``` 16 | self.filedock.setWidget(fileListContainer) 17 | 18 | self.zoomWidget = ZoomWidget() 19 | # self.colorDialog = ColorDialog(parent=self) 20 | 21 | self.canvas = Canvas() 22 | ``` -------------------------------------------------------------------------------- /libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/libs/__init__.py -------------------------------------------------------------------------------- /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 | import math 15 | 16 | CURSOR_DEFAULT = Qt.ArrowCursor 17 | CURSOR_POINT = Qt.PointingHandCursor 18 | CURSOR_DRAW = Qt.CrossCursor 19 | CURSOR_MOVE = Qt.ClosedHandCursor 20 | CURSOR_GRAB = Qt.OpenHandCursor 21 | 22 | # class Canvas(QGLWidget): 23 | 24 | 25 | class Canvas(QWidget): 26 | zoomRequest = pyqtSignal(int) 27 | scrollRequest = pyqtSignal(int, int) 28 | newShape = pyqtSignal() 29 | selectionChanged = pyqtSignal(bool) 30 | shapeMoved = pyqtSignal() 31 | drawingPolygon = pyqtSignal(bool) 32 | 33 | hideRRect = pyqtSignal(bool) 34 | hideNRect = pyqtSignal(bool) 35 | status = pyqtSignal(str) 36 | 37 | CREATE, EDIT = list(range(2)) 38 | 39 | epsilon = 11.0 40 | 41 | 42 | 43 | def __init__(self, *args, **kwargs): 44 | super(Canvas, self).__init__(*args, **kwargs) 45 | # Initialise local state. 46 | self.mode = self.EDIT 47 | self.shapes = [] 48 | self.current = None 49 | self.selectedShape = None # save the selected shape here 50 | self.selectedShapeCopy = None 51 | self.lineColor = QColor(0, 0, 255) 52 | self.line = Shape(line_color=self.lineColor) 53 | self.prevPoint = QPointF() 54 | self.offsets = QPointF(), QPointF() 55 | self.scale = 1.0 56 | self.pixmap = QPixmap() 57 | self.visible = {} 58 | self._hideBackround = False 59 | self.hideBackround = False 60 | self.hShape = None 61 | self.hVertex = None 62 | self._painter = QPainter() 63 | self._cursor = CURSOR_DEFAULT 64 | # Menus: 65 | self.menus = (QMenu(), QMenu()) 66 | # Set widget options. 67 | self.setMouseTracking(True) 68 | self.setFocusPolicy(Qt.WheelFocus) 69 | self.verified = False 70 | # judge can draw rotate rect 71 | self.canDrawRotatedRect = True 72 | self.hideRotated = False 73 | self.hideNormal = False 74 | self.canOutOfBounding = False 75 | self.showCenter = False 76 | 77 | def enterEvent(self, ev): 78 | self.overrideCursor(self._cursor) 79 | 80 | def leaveEvent(self, ev): 81 | self.restoreCursor() 82 | 83 | def focusOutEvent(self, ev): 84 | self.restoreCursor() 85 | 86 | def isVisible(self, shape): 87 | return self.visible.get(shape, True) 88 | 89 | def drawing(self): 90 | return self.mode == self.CREATE 91 | 92 | def editing(self): 93 | return self.mode == self.EDIT 94 | 95 | def setEditing(self, value=True): 96 | self.mode = self.EDIT if value else self.CREATE 97 | if not value: # Create 98 | self.unHighlight() 99 | self.deSelectShape() 100 | 101 | def unHighlight(self): 102 | if self.hShape: 103 | self.hShape.highlightClear() 104 | self.hVertex = self.hShape = None 105 | 106 | def selectedVertex(self): 107 | return self.hVertex is not None 108 | 109 | def mouseMoveEvent(self, ev): 110 | """Update line with last point and current coordinates.""" 111 | pos = self.transformPos(ev.pos()) 112 | 113 | self.restoreCursor() 114 | 115 | # Polygon drawing. 116 | if self.drawing(): 117 | 118 | self.overrideCursor(CURSOR_DRAW) 119 | if self.current: 120 | color = self.lineColor 121 | if self.outOfPixmap(pos): 122 | # Don't allow the user to draw outside the pixmap. 123 | # Project the point to the pixmap's edges. 124 | pos = self.intersectionPoint(self.current[-1], pos) 125 | elif len(self.current) > 1 and self.closeEnough(pos, self.current[0]): 126 | # Attract line to starting point and colorise to alert the 127 | # user: 128 | pos = self.current[0] 129 | color = self.current.line_color 130 | self.overrideCursor(CURSOR_POINT) 131 | self.current.highlightVertex(0, Shape.NEAR_VERTEX) 132 | self.line[1] = pos 133 | self.line.line_color = color 134 | self.repaint() 135 | self.current.highlightClear() 136 | self.status.emit("width is %d, height is %d." % (pos.x()-self.line[0].x(), pos.y()-self.line[0].y())) 137 | return 138 | 139 | # Polygon copy moving. 140 | if Qt.RightButton & ev.buttons(): 141 | # print("right button") 142 | # if self.selectedShapeCopy and self.prevPoint: 143 | # print("select shape copy") 144 | # self.overrideCursor(CURSOR_MOVE) 145 | # self.boundedMoveShape(self.selectedShapeCopy, pos) 146 | # self.repaint() 147 | # elif self.selectedShape: 148 | # print("select shape") 149 | # self.selectedShapeCopy = self.selectedShape.copy() 150 | # self.repaint() 151 | if self.selectedVertex() and self.selectedShape.isRotated: 152 | self.boundedRotateShape(pos) 153 | self.shapeMoved.emit() 154 | self.repaint() 155 | self.status.emit("(%d,%d)." % (pos.x(), pos.y())) 156 | return 157 | 158 | # Polygon/Vertex moving. 159 | if Qt.LeftButton & ev.buttons(): 160 | if self.selectedVertex(): 161 | # if self.outOfPixmap(pos): 162 | # print("chule ") 163 | # return 164 | # else: 165 | # print("meiyou chujie") 166 | self.boundedMoveVertex(pos) 167 | self.shapeMoved.emit() 168 | self.repaint() 169 | elif self.selectedShape and self.prevPoint: 170 | self.overrideCursor(CURSOR_MOVE) 171 | self.boundedMoveShape(self.selectedShape, pos) 172 | self.shapeMoved.emit() 173 | self.repaint() 174 | self.status.emit("(%d,%d)." % (pos.x(), pos.y())) 175 | return 176 | 177 | # Just hovering over the canvas, 2 posibilities: 178 | # - Highlight shapes 179 | # - Highlight vertex 180 | # Update shape/vertex fill and tooltip value accordingly. 181 | self.setToolTip("Image") 182 | for shape in reversed([s for s in self.shapes if self.isVisible(s)]): 183 | # Look for a nearby vertex to highlight. If that fails, 184 | # check if we happen to be inside a shape. 185 | index = shape.nearestVertex(pos, self.epsilon) 186 | if index is not None: 187 | if self.selectedVertex(): 188 | self.hShape.highlightClear() 189 | self.hVertex, self.hShape = index, shape 190 | shape.highlightVertex(index, shape.MOVE_VERTEX) 191 | self.overrideCursor(CURSOR_POINT) 192 | # self.setToolTip("Click & drag to move point.") 193 | # self.setStatusTip(self.toolTip()) 194 | self.update() 195 | break 196 | elif shape.containsPoint(pos): 197 | if self.selectedVertex(): 198 | self.hShape.highlightClear() 199 | self.hVertex, self.hShape = None, shape 200 | # self.setToolTip( 201 | # "Click & drag to move shape '%s'" % shape.label) 202 | # self.setStatusTip(self.toolTip()) 203 | self.overrideCursor(CURSOR_GRAB) 204 | self.update() 205 | break 206 | else: # Nothing found, clear highlights, reset state. 207 | if self.hShape: 208 | self.hShape.highlightClear() 209 | self.update() 210 | self.hVertex, self.hShape = None, None 211 | 212 | self.status.emit("(%d,%d)." % (pos.x(), pos.y())) 213 | 214 | 215 | def mousePressEvent(self, ev): 216 | pos = self.transformPos(ev.pos()) 217 | # print('sldkfj %d %d' % (pos.x(), pos.y())) 218 | if ev.button() == Qt.LeftButton: 219 | self.hideBackroundShapes(True) 220 | if self.drawing(): 221 | self.handleDrawing(pos) 222 | else: 223 | self.selectShapePoint(pos) 224 | self.prevPoint = pos 225 | self.repaint() 226 | elif ev.button() == Qt.RightButton and self.editing(): 227 | self.selectShapePoint(pos) 228 | self.hideBackroundShapes(True) 229 | # if self.selectedShape is not None: 230 | # print('point is (%d, %d)' % (pos.x(), pos.y())) 231 | # self.selectedShape.rotate(10) 232 | 233 | self.prevPoint = pos 234 | self.repaint() 235 | 236 | def mouseReleaseEvent(self, ev): 237 | self.hideBackroundShapes(False) 238 | if ev.button() == Qt.RightButton and not self.selectedVertex(): 239 | menu = self.menus[bool(self.selectedShapeCopy)] 240 | self.restoreCursor() 241 | if not menu.exec_(self.mapToGlobal(ev.pos()))\ 242 | and self.selectedShapeCopy: 243 | # Cancel the move by deleting the shadow copy. 244 | self.selectedShapeCopy = None 245 | self.repaint() 246 | elif ev.button() == Qt.LeftButton and self.selectedShape: 247 | self.overrideCursor(CURSOR_GRAB) 248 | elif ev.button() == Qt.LeftButton: 249 | pos = self.transformPos(ev.pos()) 250 | if self.drawing(): 251 | self.handleDrawing(pos) 252 | 253 | def endMove(self, copy=False): 254 | assert self.selectedShape and self.selectedShapeCopy 255 | shape = self.selectedShapeCopy 256 | #del shape.fill_color 257 | #del shape.line_color 258 | if copy: 259 | self.shapes.append(shape) 260 | self.selectedShape.selected = False 261 | self.selectedShape = shape 262 | self.repaint() 263 | else: 264 | self.selectedShape.points = [p for p in shape.points] 265 | self.selectedShapeCopy = None 266 | 267 | def hideBackroundShapes(self, value): 268 | # print("hideBackroundShapes") 269 | self.hideBackround = value 270 | if self.selectedShape: 271 | # Only hide other shapes if there is a current selection. 272 | # Otherwise the user will not be able to select a shape. 273 | self.setHiding(True) 274 | self.repaint() 275 | 276 | def handleDrawing(self, pos): 277 | if self.current and self.current.reachMaxPoints() is False: 278 | initPos = self.current[0] 279 | minX = initPos.x() 280 | minY = initPos.y() 281 | targetPos = self.line[1] 282 | maxX = targetPos.x() 283 | maxY = targetPos.y() 284 | self.current.addPoint(QPointF(maxX, minY)) 285 | self.current.addPoint(targetPos) 286 | self.current.addPoint(QPointF(minX, maxY)) 287 | self.current.addPoint(initPos) 288 | self.line[0] = self.current[-1] 289 | if self.current.isClosed(): 290 | self.finalise() 291 | elif not self.outOfPixmap(pos): 292 | self.current = Shape() 293 | self.current.addPoint(pos) 294 | self.line.points = [pos, pos] 295 | self.setHiding() 296 | self.drawingPolygon.emit(True) 297 | self.update() 298 | 299 | def setHiding(self, enable=True): 300 | self._hideBackround = self.hideBackround if enable else False 301 | 302 | def canCloseShape(self): 303 | return self.drawing() and self.current and len(self.current) > 2 304 | 305 | def mouseDoubleClickEvent(self, ev): 306 | # We need at least 4 points here, since the mousePress handler 307 | # adds an extra one before this handler is called. 308 | if self.canCloseShape() and len(self.current) > 3: 309 | self.current.popPoint() 310 | self.finalise() 311 | 312 | def selectShape(self, shape): 313 | self.deSelectShape() 314 | shape.selected = True 315 | self.selectedShape = shape 316 | self.setHiding() 317 | self.selectionChanged.emit(True) 318 | self.update() 319 | 320 | def selectShapePoint(self, point): 321 | """Select the first shape created which contains this point.""" 322 | self.deSelectShape() 323 | if self.selectedVertex(): # A vertex is marked for selection. 324 | index, shape = self.hVertex, self.hShape 325 | shape.highlightVertex(index, shape.MOVE_VERTEX) 326 | 327 | shape.selected = True 328 | self.selectedShape = shape 329 | self.calculateOffsets(shape, point) 330 | self.setHiding() 331 | self.selectionChanged.emit(True) 332 | 333 | return 334 | for shape in reversed(self.shapes): 335 | if self.isVisible(shape) and shape.containsPoint(point): 336 | shape.selected = True 337 | self.selectedShape = shape 338 | self.calculateOffsets(shape, point) 339 | self.setHiding() 340 | self.selectionChanged.emit(True) 341 | return 342 | 343 | def calculateOffsets(self, shape, point): 344 | rect = shape.boundingRect() 345 | x1 = rect.x() - point.x() 346 | y1 = rect.y() - point.y() 347 | x2 = (rect.x() + rect.width()) - point.x() 348 | y2 = (rect.y() + rect.height()) - point.y() 349 | self.offsets = QPointF(x1, y1), QPointF(x2, y2) 350 | 351 | def boundedMoveVertex(self, pos): 352 | # print("Moving Vertex") 353 | index, shape = self.hVertex, self.hShape 354 | point = shape[index] 355 | 356 | if not self.canOutOfBounding and self.outOfPixmap(pos): 357 | return 358 | # pos = self.intersectionPoint(point, pos) 359 | 360 | # print("index is %d" % index) 361 | sindex = (index + 2) % 4 362 | # get the other 3 points after transformed 363 | p2,p3,p4 = self.getAdjointPoints(shape.direction, shape[sindex], pos, index) 364 | 365 | pcenter = (pos+p3)/2 366 | if self.canOutOfBounding and self.outOfPixmap(pcenter): 367 | return 368 | # if one pixal out of map , do nothing 369 | if not self.canOutOfBounding and (self.outOfPixmap(p2) or 370 | self.outOfPixmap(p3) or 371 | self.outOfPixmap(p4)): 372 | return 373 | 374 | # move 4 pixal one by one 375 | shape.moveVertexBy(index, pos - point) 376 | lindex = (index + 1) % 4 377 | 378 | rindex = (index + 3) % 4 379 | shape[lindex] = p2 380 | # shape[sindex] = p3 381 | shape[rindex] = p4 382 | shape.close() 383 | 384 | # calculate the height and weight, and show it 385 | w = math.sqrt((p4.x()-p3.x()) ** 2 + (p4.y()-p3.y()) ** 2) 386 | h = math.sqrt((p3.x()-p2.x()) ** 2 + (p3.y()-p2.y()) ** 2) 387 | self.status.emit("width is %d, height is %d." % (w,h)) 388 | 389 | 390 | def getAdjointPoints(self, theta, p3, p1, index): 391 | # p3 = center 392 | # p3 = 2*center-p1 393 | a1 = math.tan(theta) 394 | if (a1 == 0): 395 | if index % 2 == 0: 396 | p2 = QPointF(p3.x(), p1.y()) 397 | p4 = QPointF(p1.x(), p3.y()) 398 | else: 399 | p4 = QPointF(p3.x(), p1.y()) 400 | p2 = QPointF(p1.x(), p3.y()) 401 | else: 402 | a3 = a1 403 | a2 = - 1/a1 404 | a4 = - 1/a1 405 | b1 = p1.y() - a1 * p1.x() 406 | b2 = p1.y() - a2 * p1.x() 407 | b3 = p3.y() - a1 * p3.x() 408 | b4 = p3.y() - a2 * p3.x() 409 | 410 | if index % 2 == 0: 411 | p2 = self.getCrossPoint(a1,b1,a4,b4) 412 | p4 = self.getCrossPoint(a2,b2,a3,b3) 413 | else: 414 | p4 = self.getCrossPoint(a1,b1,a4,b4) 415 | p2 = self.getCrossPoint(a2,b2,a3,b3) 416 | 417 | return p2,p3,p4 418 | 419 | def getCrossPoint(self,a1,b1,a2,b2): 420 | x = (b2-b1)/(a1-a2) 421 | y = (a1*b2 - a2*b1)/(a1-a2) 422 | return QPointF(x,y) 423 | 424 | def boundedRotateShape(self, pos): 425 | # print("Rotate Shape2") 426 | # judge if some vertex is out of pixma 427 | index, shape = self.hVertex, self.hShape 428 | point = shape[index] 429 | 430 | angle = self.getAngle(shape.center,pos,point) 431 | # for i, p in enumerate(shape.points): 432 | # if self.outOfPixmap(shape.rotatePoint(p,angle)): 433 | # # print("out of pixmap") 434 | # return 435 | if not self.rotateOutOfBound(angle): 436 | shape.rotate(angle) 437 | self.prevPoint = pos 438 | 439 | def getAngle(self, center, p1, p2): 440 | dx1 = p1.x() - center.x(); 441 | dy1 = p1.y() - center.y(); 442 | 443 | dx2 = p2.x() - center.x(); 444 | dy2 = p2.y() - center.y(); 445 | 446 | c = math.sqrt(dx1*dx1 + dy1*dy1) * math.sqrt(dx2*dx2 + dy2*dy2) 447 | if c == 0: return 0 448 | y = (dx1*dx2+dy1*dy2)/c 449 | if y>1: return 0 450 | angle = math.acos(y) 451 | 452 | if (dx1*dy2-dx2*dy1)>0: 453 | return angle 454 | else: 455 | return -angle 456 | 457 | def boundedMoveShape(self, shape, pos): 458 | if shape.isRotated and self.canOutOfBounding: 459 | c = shape.center 460 | dp = pos - self.prevPoint 461 | dc = c + dp 462 | if dc.x() < 0: 463 | dp -= QPointF(min(0,dc.x()), 0) 464 | if dc.y() < 0: 465 | dp -= QPointF(0, min(0,dc.y())) 466 | if dc.x() >= self.pixmap.width(): 467 | dp += QPointF(min(0, self.pixmap.width() - 1 - dc.x()), 0) 468 | if dc.y() >= self.pixmap.height(): 469 | dp += QPointF(0, min(0, self.pixmap.height() - 1 - dc.y())) 470 | 471 | else: 472 | if self.outOfPixmap(pos): 473 | return False # No need to move 474 | o1 = pos + self.offsets[0] 475 | if self.outOfPixmap(o1): 476 | pos -= QPointF(min(0, o1.x()), min(0, o1.y())) 477 | o2 = pos + self.offsets[1] 478 | if self.outOfPixmap(o2): 479 | pos += QPointF(min(0, self.pixmap.width() - 1 - o2.x()), 480 | min(0, self.pixmap.height() - 1 - o2.y())) 481 | dp = pos - self.prevPoint 482 | # The next line tracks the new position of the cursor 483 | # relative to the shape, but also results in making it 484 | # a bit "shaky" when nearing the border and allows it to 485 | # go outside of the shape's area for some reason. XXX 486 | #self.calculateOffsets(self.selectedShape, pos) 487 | 488 | if dp: 489 | shape.moveBy(dp) 490 | self.prevPoint = pos 491 | shape.close() 492 | return True 493 | return False 494 | 495 | 496 | def boundedMoveShape2(self, shape, pos): 497 | if self.outOfPixmap(pos): 498 | return False # No need to move 499 | o1 = pos + self.offsets[0] 500 | if self.outOfPixmap(o1): 501 | pos -= QPointF(min(0, o1.x()), min(0, o1.y())) 502 | o2 = pos + self.offsets[1] 503 | if self.outOfPixmap(o2): 504 | pos += QPointF(min(0, self.pixmap.width() - o2.x()), 505 | min(0, self.pixmap.height() - o2.y())) 506 | # The next line tracks the new position of the cursor 507 | # relative to the shape, but also results in making it 508 | # a bit "shaky" when nearing the border and allows it to 509 | # go outside of the shape's area for some reason. XXX 510 | #self.calculateOffsets(self.selectedShape, pos) 511 | dp = pos - self.prevPoint 512 | if dp: 513 | shape.moveBy(dp) 514 | self.prevPoint = pos 515 | shape.close() 516 | return True 517 | return False 518 | 519 | def deSelectShape(self): 520 | if self.selectedShape: 521 | self.selectedShape.selected = False 522 | self.selectedShape = None 523 | self.setHiding(False) 524 | self.selectionChanged.emit(False) 525 | self.update() 526 | 527 | def deleteSelected(self): 528 | if self.selectedShape: 529 | shape = self.selectedShape 530 | self.shapes.remove(self.selectedShape) 531 | self.selectedShape = None 532 | self.update() 533 | return shape 534 | 535 | def copySelectedShape(self): 536 | if self.selectedShape: 537 | shape = self.selectedShape.copy() 538 | self.deSelectShape() 539 | self.shapes.append(shape) 540 | shape.selected = True 541 | self.selectedShape = shape 542 | self.boundedShiftShape(shape) 543 | return shape 544 | 545 | def boundedShiftShape(self, shape): 546 | # Try to move in one direction, and if it fails in another. 547 | # Give up if both fail. 548 | point = shape[0] 549 | offset = QPointF(2.0, 2.0) 550 | self.calculateOffsets(shape, point) 551 | self.prevPoint = point 552 | if not self.boundedMoveShape(shape, point - offset): 553 | self.boundedMoveShape(shape, point + offset) 554 | 555 | def paintEvent(self, event): 556 | if not self.pixmap: 557 | return super(Canvas, self).paintEvent(event) 558 | 559 | p = self._painter 560 | p.begin(self) 561 | p.setRenderHint(QPainter.Antialiasing) 562 | p.setRenderHint(QPainter.HighQualityAntialiasing) 563 | p.setRenderHint(QPainter.SmoothPixmapTransform) 564 | 565 | p.scale(self.scale, self.scale) 566 | p.translate(self.offsetToCenter()) 567 | 568 | p.drawPixmap(0, 0, self.pixmap) 569 | Shape.scale = self.scale 570 | for shape in self.shapes: 571 | if (shape.selected or not self._hideBackround) and self.isVisible(shape): 572 | if (shape.isRotated and not self.hideRotated) or (not shape.isRotated and not self.hideNormal): 573 | shape.fill = shape.selected or shape == self.hShape 574 | shape.paint(p) 575 | elif self.showCenter: 576 | shape.fill = shape.selected or shape == self.hShape 577 | shape.paintNormalCenter(p) 578 | 579 | if self.current: 580 | self.current.paint(p) 581 | self.line.paint(p) 582 | if self.selectedShapeCopy: 583 | self.selectedShapeCopy.paint(p) 584 | 585 | # Paint rect 586 | if self.current is not None and len(self.line) == 2: 587 | leftTop = self.line[0] 588 | rightBottom = self.line[1] 589 | rectWidth = rightBottom.x() - leftTop.x() 590 | rectHeight = rightBottom.y() - leftTop.y() 591 | color = QColor(0, 220, 0) 592 | p.setPen(color) 593 | brush = QBrush(Qt.BDiagPattern) 594 | p.setBrush(brush) 595 | p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight) 596 | 597 | #draw dialog line of rectangle 598 | p.setPen(self.lineColor) 599 | p.drawLine(leftTop.x(),rightBottom.y(),rightBottom.x(),leftTop.y()) 600 | 601 | self.setAutoFillBackground(True) 602 | if self.verified: 603 | pal = self.palette() 604 | pal.setColor(self.backgroundRole(), QColor(184, 239, 38, 128)) 605 | self.setPalette(pal) 606 | else: 607 | pal = self.palette() 608 | pal.setColor(self.backgroundRole(), QColor(232, 232, 232, 255)) 609 | self.setPalette(pal) 610 | 611 | p.end() 612 | 613 | def transformPos(self, point): 614 | """Convert from widget-logical coordinates to painter-logical coordinates.""" 615 | return point / self.scale - self.offsetToCenter() 616 | 617 | def offsetToCenter(self): 618 | s = self.scale 619 | area = super(Canvas, self).size() 620 | w, h = self.pixmap.width() * s, self.pixmap.height() * s 621 | aw, ah = area.width(), area.height() 622 | x = (aw - w) / (2 * s) if aw > w else 0 623 | y = (ah - h) / (2 * s) if ah > h else 0 624 | return QPointF(x, y) 625 | 626 | def outOfPixmap(self, p): 627 | w, h = self.pixmap.width(), self.pixmap.height() 628 | return not (0 <= p.x() < w and 0 <= p.y() < h) 629 | 630 | def finalise(self): 631 | assert self.current 632 | self.current.isRotated = self.canDrawRotatedRect 633 | # print(self.canDrawRotatedRect) 634 | self.current.close() 635 | self.shapes.append(self.current) 636 | self.current = None 637 | self.setHiding(False) 638 | self.newShape.emit() 639 | self.update() 640 | 641 | def closeEnough(self, p1, p2): 642 | #d = distance(p1 - p2) 643 | #m = (p1-p2).manhattanLength() 644 | # print "d %.2f, m %d, %.2f" % (d, m, d - m) 645 | return distance(p1 - p2) < self.epsilon 646 | 647 | def intersectionPoint(self, p1, p2): 648 | # Cycle through each image edge in clockwise fashion, 649 | # and find the one intersecting the current line segment. 650 | # http://paulbourke.net/geometry/lineline2d/ 651 | size = self.pixmap.size() 652 | points = [(0, 0), 653 | (size.width(), 0), 654 | (size.width(), size.height()), 655 | (0, size.height())] 656 | x1, y1 = p1.x(), p1.y() 657 | x2, y2 = p2.x(), p2.y() 658 | d, i, (x, y) = min(self.intersectingEdges((x1, y1), (x2, y2), points)) 659 | x3, y3 = points[i] 660 | x4, y4 = points[(i + 1) % 4] 661 | if (x, y) == (x1, y1): 662 | # Handle cases where previous point is on one of the edges. 663 | if x3 == x4: 664 | return QPointF(x3, min(max(0, y2), max(y3, y4))) 665 | else: # y3 == y4 666 | return QPointF(min(max(0, x2), max(x3, x4)), y3) 667 | return QPointF(x, y) 668 | 669 | def intersectingEdges(self, x1y1, x2y2, points): 670 | """For each edge formed by `points', yield the intersection 671 | with the line segment `(x1,y1) - (x2,y2)`, if it exists. 672 | Also return the distance of `(x2,y2)' to the middle of the 673 | edge along with its index, so that the one closest can be chosen.""" 674 | x1, y1 = x1y1 675 | x2, y2 = x2y2 676 | for i in range(4): 677 | x3, y3 = points[i] 678 | x4, y4 = points[(i + 1) % 4] 679 | denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) 680 | nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) 681 | nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) 682 | if denom == 0: 683 | # This covers two cases: 684 | # nua == nub == 0: Coincident 685 | # otherwise: Parallel 686 | continue 687 | ua, ub = nua / denom, nub / denom 688 | if 0 <= ua <= 1 and 0 <= ub <= 1: 689 | x = x1 + ua * (x2 - x1) 690 | y = y1 + ua * (y2 - y1) 691 | m = QPointF((x3 + x4) / 2, (y3 + y4) / 2) 692 | d = distance(m - QPointF(x2, y2)) 693 | print("return=",d,i,(x,y)) 694 | yield d, i, (x, y) 695 | 696 | # These two, along with a call to adjustSize are required for the 697 | # scroll area. 698 | def sizeHint(self): 699 | return self.minimumSizeHint() 700 | 701 | def minimumSizeHint(self): 702 | if self.pixmap: 703 | return self.scale * self.pixmap.size() 704 | return super(Canvas, self).minimumSizeHint() 705 | 706 | def wheelEvent(self, ev): 707 | qt_version = 4 if hasattr(ev, "delta") else 5 708 | if qt_version == 4: 709 | if ev.orientation() == Qt.Vertical: 710 | v_delta = ev.delta() 711 | h_delta = 0 712 | else: 713 | h_delta = ev.delta() 714 | v_delta = 0 715 | else: 716 | delta = ev.angleDelta() 717 | h_delta = delta.x() 718 | v_delta = delta.y() 719 | # print('scrolling vdelta is %d, hdelta is %d' % (v_delta, h_delta)) 720 | mods = ev.modifiers() 721 | if Qt.ControlModifier == int(mods) and v_delta: 722 | self.zoomRequest.emit(v_delta) 723 | else: 724 | v_delta and self.scrollRequest.emit(v_delta, Qt.Vertical) 725 | h_delta and self.scrollRequest.emit(h_delta, Qt.Horizontal) 726 | ev.accept() 727 | 728 | def keyPressEvent(self, ev): 729 | key = ev.key() 730 | 731 | if key == Qt.Key_Escape and self.current: 732 | print('ESC press') 733 | self.current = None 734 | self.drawingPolygon.emit(False) 735 | self.update() 736 | elif key == Qt.Key_Return and self.canCloseShape(): 737 | self.finalise() 738 | elif key == Qt.Key_Left and self.selectedShape: 739 | self.moveOnePixel('Left') 740 | elif key == Qt.Key_Right and self.selectedShape: 741 | self.moveOnePixel('Right') 742 | elif key == Qt.Key_Up and self.selectedShape: 743 | self.moveOnePixel('Up') 744 | elif key == Qt.Key_Down and self.selectedShape: 745 | self.moveOnePixel('Down') 746 | elif key == Qt.Key_Z and self.selectedShape and\ 747 | self.selectedShape.isRotated and not self.rotateOutOfBound(0.1): 748 | self.selectedShape.rotate(0.1) 749 | self.shapeMoved.emit() 750 | self.update() 751 | elif key == Qt.Key_X and self.selectedShape and\ 752 | self.selectedShape.isRotated and not self.rotateOutOfBound(0.01): 753 | self.selectedShape.rotate(0.01) 754 | self.shapeMoved.emit() 755 | self.update() 756 | elif key == Qt.Key_C and self.selectedShape and\ 757 | self.selectedShape.isRotated and not self.rotateOutOfBound(-0.01): 758 | self.selectedShape.rotate(-0.01) 759 | self.shapeMoved.emit() 760 | self.update() 761 | elif key == Qt.Key_V and self.selectedShape and\ 762 | self.selectedShape.isRotated and not self.rotateOutOfBound(-0.1): 763 | self.selectedShape.rotate(-0.1) 764 | self.shapeMoved.emit() 765 | self.update() 766 | elif key == Qt.Key_R: 767 | self.hideRotated = not self.hideRotated 768 | self.hideRRect.emit(self.hideRotated) 769 | self.update() 770 | elif key == Qt.Key_N: 771 | self.hideNormal = not self.hideNormal 772 | self.hideNRect.emit(self.hideNormal) 773 | self.update() 774 | elif key == Qt.Key_O: 775 | self.canOutOfBounding = not self.canOutOfBounding 776 | elif key == Qt.Key_B: 777 | self.showCenter = not self.showCenter 778 | self.update() 779 | 780 | 781 | def rotateOutOfBound(self, angle): 782 | if self.canOutOfBounding: 783 | return False 784 | for i, p in enumerate(self.selectedShape.points): 785 | if self.outOfPixmap(self.selectedShape.rotatePoint(p,angle)): 786 | return True 787 | return False 788 | 789 | def moveOnePixel(self, direction): 790 | # print(self.selectedShape.points) 791 | if direction == 'Left' and not self.moveOutOfBound(QPointF(-1.0, 0)): 792 | # print("move Left one pixel") 793 | self.selectedShape.points[0] += QPointF(-1.0, 0) 794 | self.selectedShape.points[1] += QPointF(-1.0, 0) 795 | self.selectedShape.points[2] += QPointF(-1.0, 0) 796 | self.selectedShape.points[3] += QPointF(-1.0, 0) 797 | self.selectedShape.center += QPointF(-1.0, 0) 798 | elif direction == 'Right' and not self.moveOutOfBound(QPointF(1.0, 0)): 799 | # print("move Right one pixel") 800 | self.selectedShape.points[0] += QPointF(1.0, 0) 801 | self.selectedShape.points[1] += QPointF(1.0, 0) 802 | self.selectedShape.points[2] += QPointF(1.0, 0) 803 | self.selectedShape.points[3] += QPointF(1.0, 0) 804 | self.selectedShape.center += QPointF(1.0, 0) 805 | elif direction == 'Up' and not self.moveOutOfBound(QPointF(0, -1.0)): 806 | # print("move Up one pixel") 807 | self.selectedShape.points[0] += QPointF(0, -1.0) 808 | self.selectedShape.points[1] += QPointF(0, -1.0) 809 | self.selectedShape.points[2] += QPointF(0, -1.0) 810 | self.selectedShape.points[3] += QPointF(0, -1.0) 811 | self.selectedShape.center += QPointF(0, -1.0) 812 | elif direction == 'Down' and not self.moveOutOfBound(QPointF(0, 1.0)): 813 | # print("move Down one pixel") 814 | self.selectedShape.points[0] += QPointF(0, 1.0) 815 | self.selectedShape.points[1] += QPointF(0, 1.0) 816 | self.selectedShape.points[2] += QPointF(0, 1.0) 817 | self.selectedShape.points[3] += QPointF(0, 1.0) 818 | self.selectedShape.center += QPointF(0, 1.0) 819 | self.shapeMoved.emit() 820 | self.repaint() 821 | 822 | def moveOutOfBound(self, step): 823 | points = [p1+p2 for p1, p2 in zip(self.selectedShape.points, [step]*4)] 824 | return True in map(self.outOfPixmap, points) 825 | 826 | def setLastLabel(self, text): 827 | assert text 828 | self.shapes[-1].label = text 829 | return self.shapes[-1] 830 | 831 | def undoLastLine(self): 832 | assert self.shapes 833 | self.current = self.shapes.pop() 834 | self.current.setOpen() 835 | self.line.points = [self.current[-1], self.current[0]] 836 | self.drawingPolygon.emit(True) 837 | 838 | def resetAllLines(self): 839 | assert self.shapes 840 | self.current = self.shapes.pop() 841 | self.current.setOpen() 842 | self.line.points = [self.current[-1], self.current[0]] 843 | self.drawingPolygon.emit(True) 844 | self.current = None 845 | self.drawingPolygon.emit(False) 846 | self.update() 847 | 848 | def loadPixmap(self, pixmap): 849 | self.pixmap = pixmap 850 | self.shapes = [] 851 | self.repaint() 852 | 853 | def loadShapes(self, shapes): 854 | self.shapes = list(shapes) 855 | self.current = None 856 | self.repaint() 857 | 858 | def setShapeVisible(self, shape, value): 859 | self.visible[shape] = value 860 | self.repaint() 861 | 862 | def overrideCursor(self, cursor): 863 | self.restoreCursor() 864 | self._cursor = cursor 865 | QApplication.setOverrideCursor(cursor) 866 | 867 | def restoreCursor(self): 868 | QApplication.restoreOverrideCursor() 869 | 870 | def resetState(self): 871 | self.restoreCursor() 872 | self.pixmap = None 873 | self.update() 874 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | from pascal_voc_io import XML_EXT 12 | import os.path 13 | import sys 14 | import math 15 | 16 | class LabelFileError(Exception): 17 | pass 18 | 19 | 20 | class LabelFile(object): 21 | # It might be changed as window creates. By default, using XML ext 22 | # suffix = '.lif' 23 | suffix = XML_EXT 24 | 25 | def __init__(self, filename=None): 26 | self.shapes = () 27 | self.imagePath = None 28 | self.imageData = None 29 | self.verified = False 30 | 31 | def savePascalVocFormat(self, filename, shapes, imagePath, imageData, 32 | lineColor=None, fillColor=None, databaseSrc=None): 33 | imgFolderPath = os.path.dirname(imagePath) 34 | imgFolderName = os.path.split(imgFolderPath)[-1] 35 | imgFileName = os.path.basename(imagePath) 36 | imgFileNameWithoutExt = os.path.splitext(imgFileName)[0] 37 | # Read from file path because self.imageData might be empty if saving to 38 | # Pascal format 39 | image = QImage() 40 | image.load(imagePath) 41 | imageShape = [image.height(), image.width(), 42 | 1 if image.isGrayscale() else 3] 43 | writer = PascalVocWriter(imgFolderName, imgFileNameWithoutExt, 44 | imageShape, localImgPath=imagePath) 45 | writer.verified = self.verified 46 | 47 | for shape in shapes: 48 | points = shape['points'] 49 | label = shape['label'] 50 | # Add Chris 51 | difficult = int(shape['difficult']) 52 | direction = shape['direction'] 53 | isRotated = shape['isRotated'] 54 | # if shape is normal box, save as bounding box 55 | # print('direction is %lf' % direction) 56 | if not isRotated: 57 | bndbox = LabelFile.convertPoints2BndBox(points) 58 | writer.addBndBox(bndbox[0], bndbox[1], bndbox[2], 59 | bndbox[3], label, difficult) 60 | else: #if shape is rotated box, save as rotated bounding box 61 | robndbox = LabelFile.convertPoints2RotatedBndBox(shape) 62 | writer.addRotatedBndBox(robndbox[0],robndbox[1], 63 | robndbox[2],robndbox[3],robndbox[4],label,difficult) 64 | 65 | writer.save(targetFile=filename) 66 | return 67 | 68 | def toggleVerify(self): 69 | self.verified = not self.verified 70 | 71 | @staticmethod 72 | def isLabelFile(filename): 73 | fileSuffix = os.path.splitext(filename)[1].lower() 74 | return fileSuffix == LabelFile.suffix 75 | 76 | @staticmethod 77 | def convertPoints2BndBox(points): 78 | xmin = float('inf') 79 | ymin = float('inf') 80 | xmax = float('-inf') 81 | ymax = float('-inf') 82 | for p in points: 83 | x = p[0] 84 | y = p[1] 85 | xmin = min(x, xmin) 86 | ymin = min(y, ymin) 87 | xmax = max(x, xmax) 88 | ymax = max(y, ymax) 89 | 90 | # Martin Kersner, 2015/11/12 91 | # 0-valued coordinates of BB caused an error while 92 | # training faster-rcnn object detector. 93 | if xmin < 1: 94 | xmin = 1 95 | 96 | if ymin < 1: 97 | ymin = 1 98 | 99 | return (int(xmin), int(ymin), int(xmax), int(ymax)) 100 | 101 | # You Hao, 2017/06/121 102 | @staticmethod 103 | def convertPoints2RotatedBndBox(shape): 104 | points = shape['points'] 105 | center = shape['center'] 106 | direction = shape['direction'] 107 | 108 | cx = center.x() 109 | cy = center.y() 110 | 111 | w = math.sqrt((points[0][0]-points[1][0]) ** 2 + 112 | (points[0][1]-points[1][1]) ** 2) 113 | 114 | h = math.sqrt((points[2][0]-points[1][0]) ** 2 + 115 | (points[2][1]-points[1][1]) ** 2) 116 | 117 | angle = direction % math.pi 118 | 119 | return (round(cx,4),round(cy,4),round(w,4),round(h,4),round(angle,6)) 120 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /libs/pascal_voc_io.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf8 -*- 3 | import sys 4 | from xml.etree import ElementTree 5 | from xml.etree.ElementTree import Element, SubElement 6 | 7 | try: 8 | from lxml import etree 9 | print("running with lxml.etree") 10 | except ImportError: 11 | try: 12 | # Python 2.5 13 | import xml.etree.cElementTree as etree 14 | print("running with cElementTree on Python 2.5+") 15 | except ImportError: 16 | try: 17 | # Python 2.5 18 | import xml.etree.ElementTree as etree 19 | print("running with ElementTree on Python 2.5+") 20 | except ImportError: 21 | try: 22 | # normal cElementTree install 23 | import cElementTree as etree 24 | print("running with cElementTree") 25 | except ImportError: 26 | try: 27 | # normal ElementTree install 28 | import elementtree.ElementTree as etree 29 | print("running with ElementTree") 30 | except ImportError: 31 | print("Failed to import ElementTree from any known place") 32 | 33 | import codecs 34 | import math 35 | 36 | XML_EXT = '.xml' 37 | 38 | class PascalVocWriter: 39 | 40 | def __init__(self, foldername, filename, imgSize,databaseSrc='Unknown', localImgPath=None): 41 | self.foldername = foldername 42 | self.filename = filename 43 | self.databaseSrc = databaseSrc 44 | self.imgSize = imgSize 45 | self.boxlist = [] 46 | self.roboxlist = [] 47 | self.localImgPath = localImgPath 48 | self.verified = False 49 | 50 | 51 | def prettify(self, elem): 52 | """ 53 | Return a pretty-printed XML string for the Element. 54 | """ 55 | rough_string = ElementTree.tostring(elem, 'utf8') 56 | root = etree.fromstring(rough_string) 57 | try: 58 | return etree.tostring(root, pretty_print=True) 59 | except TypeError: 60 | return etree.tostring(root) 61 | 62 | def genXML(self): 63 | """ 64 | Return XML root 65 | """ 66 | # Check conditions 67 | if self.filename is None or \ 68 | self.foldername is None or \ 69 | self.imgSize is None: 70 | return None 71 | 72 | top = Element('annotation') 73 | top.set('verified', 'yes' if self.verified else 'no') 74 | 75 | folder = SubElement(top, 'folder') 76 | folder.text = self.foldername 77 | 78 | filename = SubElement(top, 'filename') 79 | filename.text = self.filename 80 | 81 | localImgPath = SubElement(top, 'path') 82 | localImgPath.text = self.localImgPath 83 | 84 | source = SubElement(top, 'source') 85 | database = SubElement(source, 'database') 86 | database.text = self.databaseSrc 87 | 88 | size_part = SubElement(top, 'size') 89 | width = SubElement(size_part, 'width') 90 | height = SubElement(size_part, 'height') 91 | depth = SubElement(size_part, 'depth') 92 | width.text = str(self.imgSize[1]) 93 | height.text = str(self.imgSize[0]) 94 | if len(self.imgSize) == 3: 95 | depth.text = str(self.imgSize[2]) 96 | else: 97 | depth.text = '1' 98 | 99 | segmented = SubElement(top, 'segmented') 100 | segmented.text = '0' 101 | return top 102 | 103 | def addBndBox(self, xmin, ymin, xmax, ymax, name, difficult): 104 | bndbox = {'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax} 105 | bndbox['name'] = name 106 | bndbox['difficult'] = difficult 107 | self.boxlist.append(bndbox) 108 | 109 | # You Hao 2017/06/21 110 | # add to analysis robndbox 111 | def addRotatedBndBox(self, cx, cy, w, h, angle, name, difficult): 112 | robndbox = {'cx': cx, 'cy': cy, 'w': w, 'h': h, 'angle': angle} 113 | robndbox['name'] = name 114 | robndbox['difficult'] = difficult 115 | self.roboxlist.append(robndbox) 116 | 117 | def appendObjects(self, top): 118 | for each_object in self.boxlist: 119 | object_item = SubElement(top, 'object') 120 | typeItem = SubElement(object_item, 'type') 121 | typeItem.text = "bndbox" 122 | name = SubElement(object_item, 'name') 123 | try: 124 | name.text = unicode(each_object['name']) 125 | except NameError: 126 | # Py3: NameError: name 'unicode' is not defined 127 | name.text = each_object['name'] 128 | pose = SubElement(object_item, 'pose') 129 | pose.text = "Unspecified" 130 | truncated = SubElement(object_item, 'truncated') 131 | if int(each_object['ymax']) == int(self.imgSize[0]) or (int(each_object['ymin'])== 1): 132 | truncated.text = "1" # max == height or min 133 | elif (int(each_object['xmax'])==int(self.imgSize[1])) or (int(each_object['xmin'])== 1): 134 | truncated.text = "1" # max == width or min 135 | else: 136 | truncated.text = "0" 137 | difficult = SubElement(object_item, 'difficult') 138 | difficult.text = str( bool(each_object['difficult']) & 1 ) 139 | bndbox = SubElement(object_item, 'bndbox') 140 | xmin = SubElement(bndbox, 'xmin') 141 | xmin.text = str(each_object['xmin']) 142 | ymin = SubElement(bndbox, 'ymin') 143 | ymin.text = str(each_object['ymin']) 144 | xmax = SubElement(bndbox, 'xmax') 145 | xmax.text = str(each_object['xmax']) 146 | ymax = SubElement(bndbox, 'ymax') 147 | ymax.text = str(each_object['ymax']) 148 | 149 | # You Hao 2017/06/21 150 | # add to store robndbox 151 | for each_object in self.roboxlist: 152 | object_item = SubElement(top, 'object') 153 | typeItem = SubElement(object_item, 'type') 154 | typeItem.text = "robndbox" 155 | name = SubElement(object_item, 'name') 156 | try: 157 | name.text = unicode(each_object['name']) 158 | except NameError: 159 | # Py3: NameError: name 'unicode' is not defined 160 | name.text = each_object['name'] 161 | pose = SubElement(object_item, 'pose') 162 | pose.text = "Unspecified" 163 | truncated = SubElement(object_item, 'truncated') 164 | # if int(each_object['ymax']) == int(self.imgSize[0]) or (int(each_object['ymin'])== 1): 165 | # truncated.text = "1" # max == height or min 166 | # elif (int(each_object['xmax'])==int(self.imgSize[1])) or (int(each_object['xmin'])== 1): 167 | # truncated.text = "1" # max == width or min 168 | # else: 169 | truncated.text = "0" 170 | difficult = SubElement(object_item, 'difficult') 171 | difficult.text = str( bool(each_object['difficult']) & 1 ) 172 | robndbox = SubElement(object_item, 'robndbox') 173 | cx = SubElement(robndbox, 'cx') 174 | cx.text = str(each_object['cx']) 175 | cy = SubElement(robndbox, 'cy') 176 | cy.text = str(each_object['cy']) 177 | w = SubElement(robndbox, 'w') 178 | w.text = str(each_object['w']) 179 | h = SubElement(robndbox, 'h') 180 | h.text = str(each_object['h']) 181 | angle = SubElement(robndbox, 'angle') 182 | angle.text = str(each_object['angle']) 183 | 184 | def save(self, targetFile=None): 185 | root = self.genXML() 186 | self.appendObjects(root) 187 | out_file = None 188 | if targetFile is None: 189 | out_file = codecs.open( 190 | self.filename + XML_EXT, 'w', encoding='utf-8') 191 | else: 192 | out_file = codecs.open(targetFile, 'w', encoding='utf-8') 193 | 194 | prettifyResult = self.prettify(root) 195 | out_file.write(prettifyResult.decode('utf8')) 196 | out_file.close() 197 | 198 | 199 | class PascalVocReader: 200 | 201 | def __init__(self, filepath): 202 | # shapes type: 203 | # [labbel, [(x1,y1), (x2,y2), (x3,y3), (x4,y4)], color, color, difficult] 204 | self.shapes = [] 205 | self.filepath = filepath 206 | self.verified = False 207 | self.parseXML() 208 | 209 | def getShapes(self): 210 | return self.shapes 211 | 212 | def addShape(self, label, bndbox, difficult): 213 | xmin = int(bndbox.find('xmin').text) 214 | ymin = int(bndbox.find('ymin').text) 215 | xmax = int(bndbox.find('xmax').text) 216 | ymax = int(bndbox.find('ymax').text) 217 | points = [(xmin, ymin), (xmax, ymin), (xmax, ymax), (xmin, ymax)] 218 | self.shapes.append((label, points, 0, False, None, None, difficult)) 219 | 220 | # You Hao 2017/06/21 221 | # add to analysis robndbox load from xml 222 | def addRotatedShape(self, label, robndbox, difficult): 223 | cx = float(robndbox.find('cx').text) 224 | cy = float(robndbox.find('cy').text) 225 | w = float(robndbox.find('w').text) 226 | h = float(robndbox.find('h').text) 227 | angle = float(robndbox.find('angle').text) 228 | 229 | p0x,p0y = self.rotatePoint(cx,cy, cx - w/2, cy - h/2, -angle) 230 | p1x,p1y = self.rotatePoint(cx,cy, cx + w/2, cy - h/2, -angle) 231 | p2x,p2y = self.rotatePoint(cx,cy, cx + w/2, cy + h/2, -angle) 232 | p3x,p3y = self.rotatePoint(cx,cy, cx - w/2, cy + h/2, -angle) 233 | 234 | points = [(p0x, p0y), (p1x, p1y), (p2x, p2y), (p3x, p3y)] 235 | self.shapes.append((label, points, angle, True, None, None, difficult)) 236 | 237 | def rotatePoint(self, xc,yc, xp,yp, theta): 238 | xoff = xp-xc; 239 | yoff = yp-yc; 240 | 241 | cosTheta = math.cos(theta) 242 | sinTheta = math.sin(theta) 243 | pResx = cosTheta * xoff + sinTheta * yoff 244 | pResy = - sinTheta * xoff + cosTheta * yoff 245 | # pRes = (xc + pResx, yc + pResy) 246 | return xc+pResx,yc+pResy 247 | 248 | def parseXML(self): 249 | assert self.filepath.endswith(XML_EXT), "Unsupport file format" 250 | parser = etree.XMLParser(encoding='utf-8') 251 | xmltree = ElementTree.parse(self.filepath, parser=parser).getroot() 252 | filename = xmltree.find('filename').text 253 | try: 254 | verified = xmltree.attrib['verified'] 255 | if verified == 'yes': 256 | self.verified = True 257 | except KeyError: 258 | self.verified = False 259 | 260 | for object_iter in xmltree.findall('object'): 261 | typeItem = object_iter.find('type') 262 | 263 | # print(typeItem.text) 264 | if typeItem.text == 'bndbox': 265 | bndbox = object_iter.find("bndbox") 266 | label = object_iter.find('name').text 267 | # Add chris 268 | difficult = False 269 | if object_iter.find('difficult') is not None: 270 | difficult = bool(int(object_iter.find('difficult').text)) 271 | self.addShape(label, bndbox, difficult) 272 | 273 | # You Hao 2017/06/21 274 | # add to load robndbox 275 | elif typeItem.text == 'robndbox': 276 | robndbox = object_iter.find('robndbox') 277 | label = object_iter.find('name').text 278 | difficult = False 279 | if object_iter.find('difficult') is not None: 280 | difficult = bool(int(object_iter.find('difficult').text)) 281 | self.addRotatedShape(label, robndbox, difficult) 282 | 283 | else: 284 | pass 285 | 286 | return True 287 | -------------------------------------------------------------------------------- /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 | import math 14 | 15 | DEFAULT_LINE_COLOR = QColor(0, 255, 0, 128) 16 | DEFAULT_FILL_COLOR = QColor(255, 0, 0, 128) 17 | DEFAULT_SELECT_LINE_COLOR = QColor(255, 255, 255) 18 | DEFAULT_SELECT_FILL_COLOR = QColor(0, 128, 255, 155) 19 | DEFAULT_VERTEX_FILL_COLOR = QColor(0, 255, 0, 255) 20 | DEFAULT_HVERTEX_FILL_COLOR = QColor(255, 0, 0) 21 | 22 | 23 | class Shape(object): 24 | P_SQUARE, P_ROUND = range(2) 25 | 26 | MOVE_VERTEX, NEAR_VERTEX = range(2) 27 | 28 | # The following class variables influence the drawing 29 | # of _all_ shape objects. 30 | line_color = DEFAULT_LINE_COLOR 31 | fill_color = DEFAULT_FILL_COLOR 32 | select_line_color = DEFAULT_SELECT_LINE_COLOR 33 | select_fill_color = DEFAULT_SELECT_FILL_COLOR 34 | vertex_fill_color = DEFAULT_VERTEX_FILL_COLOR 35 | hvertex_fill_color = DEFAULT_HVERTEX_FILL_COLOR 36 | point_type = P_ROUND 37 | point_size = 8 38 | scale = 1.0 39 | 40 | def __init__(self, label=None, line_color=None,difficult = False): 41 | self.label = label 42 | self.points = [] 43 | self.fill = False 44 | self.selected = False 45 | self.difficult = difficult 46 | 47 | self.direction = 0 # added by hy 48 | self.center = None # added by hy 49 | self.isRotated = True 50 | 51 | self._highlightIndex = None 52 | self._highlightMode = self.NEAR_VERTEX 53 | self._highlightSettings = { 54 | self.NEAR_VERTEX: (4, self.P_ROUND), 55 | self.MOVE_VERTEX: (1.5, self.P_SQUARE), 56 | } 57 | 58 | self._closed = False 59 | 60 | if line_color is not None: 61 | # Override the class line_color attribute 62 | # with an object attribute. Currently this 63 | # is used for drawing the pending line a different color. 64 | self.line_color = line_color 65 | 66 | 67 | def rotate(self, theta): 68 | for i, p in enumerate(self.points): 69 | self.points[i] = self.rotatePoint(p, theta) 70 | self.direction -= theta 71 | self.direction = self.direction % (2 * math.pi) 72 | 73 | def rotatePoint(self, p, theta): 74 | order = p-self.center; 75 | cosTheta = math.cos(theta) 76 | sinTheta = math.sin(theta) 77 | pResx = cosTheta * order.x() + sinTheta * order.y() 78 | pResy = - sinTheta * order.x() + cosTheta * order.y() 79 | pRes = QPointF(self.center.x() + pResx, self.center.y() + pResy) 80 | return pRes 81 | 82 | def close(self): 83 | self.center = QPointF((self.points[0].x()+self.points[2].x()) / 2, (self.points[0].y()+self.points[2].y()) / 2) 84 | # print("refresh center!") 85 | self._closed = True 86 | 87 | def reachMaxPoints(self): 88 | if len(self.points) >= 4: 89 | return True 90 | return False 91 | 92 | def addPoint(self, point): 93 | if self.points and len(self.points) == 4 and point == self.points[0]: 94 | self.close() 95 | else: 96 | self.points.append(point) 97 | 98 | def popPoint(self): 99 | if self.points: 100 | return self.points.pop() 101 | return None 102 | 103 | def isClosed(self): 104 | return self._closed 105 | 106 | def setOpen(self): 107 | self._closed = False 108 | 109 | def paint(self, painter): 110 | if self.points: 111 | color = self.select_line_color if self.selected else self.line_color 112 | pen = QPen(color) 113 | # Try using integer sizes for smoother drawing(?) 114 | pen.setWidth(max(1, int(round(2.0 / self.scale)))) 115 | painter.setPen(pen) 116 | 117 | line_path = QPainterPath() 118 | vrtx_path = QPainterPath() 119 | 120 | line_path.moveTo(self.points[0]) 121 | # Uncommenting the following line will draw 2 paths 122 | # for the 1st vertex, and make it non-filled, which 123 | # may be desirable. 124 | #self.drawVertex(vrtx_path, 0) 125 | 126 | for i, p in enumerate(self.points): 127 | line_path.lineTo(p) 128 | # print('shape paint points (%d, %d)' % (p.x(), p.y())) 129 | self.drawVertex(vrtx_path, i) 130 | if self.isClosed(): 131 | line_path.lineTo(self.points[0]) 132 | 133 | # dir_path = QPainterPath() 134 | # tempP = self.points[0]+QPointF(10,10) 135 | # print('direction2 is %lf, a os %lf' % (self.direction,math.tan(self.direction))) 136 | # dir_path.moveTo(tempP) 137 | # dir_path.lineTo(tempP + QPointF(10, (10-tempP.x())* math.tan(self.direction)+tempP.y())) 138 | # painter.drawPath(dir_path) 139 | 140 | painter.drawPath(line_path) 141 | painter.drawPath(vrtx_path) 142 | painter.fillPath(vrtx_path, self.vertex_fill_color) 143 | if self.fill: 144 | color = self.select_fill_color if self.selected else self.fill_color 145 | painter.fillPath(line_path, color) 146 | 147 | if self.center is not None: 148 | center_path = QPainterPath() 149 | d = self.point_size / self.scale 150 | center_path.addRect(self.center.x() - d / 2, self.center.y() - d / 2, d, d) 151 | painter.drawPath(center_path) 152 | if self.isRotated: 153 | painter.fillPath(center_path, self.vertex_fill_color) 154 | else: 155 | painter.fillPath(center_path, QColor(0, 0, 0)) 156 | 157 | def paintNormalCenter(self, painter): 158 | if self.center is not None: 159 | center_path = QPainterPath(); 160 | d = self.point_size / self.scale 161 | center_path.addRect(self.center.x() - d / 2, self.center.y() - d / 2, d, d) 162 | painter.drawPath(center_path) 163 | if not self.isRotated: 164 | painter.fillPath(center_path, QColor(0, 0, 0)) 165 | 166 | def drawVertex(self, path, i): 167 | d = self.point_size / self.scale 168 | shape = self.point_type 169 | point = self.points[i] 170 | if i == self._highlightIndex: 171 | size, shape = self._highlightSettings[self._highlightMode] 172 | d *= size 173 | if self._highlightIndex is not None: 174 | self.vertex_fill_color = self.hvertex_fill_color 175 | else: 176 | self.vertex_fill_color = Shape.vertex_fill_color 177 | if shape == self.P_SQUARE: 178 | path.addRect(point.x() - d / 2, point.y() - d / 2, d, d) 179 | elif shape == self.P_ROUND: 180 | path.addEllipse(point, d / 2.0, d / 2.0) 181 | else: 182 | assert False, "unsupported vertex shape" 183 | # def drawVertex(self, path, center): 184 | # pass 185 | 186 | def nearestVertex(self, point, epsilon): 187 | for i, p in enumerate(self.points): 188 | if distance(p - point) <= epsilon: 189 | return i 190 | return None 191 | 192 | def containsPoint(self, point): 193 | return self.makePath().contains(point) 194 | 195 | def makePath(self): 196 | path = QPainterPath(self.points[0]) 197 | for p in self.points[1:]: 198 | path.lineTo(p) 199 | return path 200 | 201 | def boundingRect(self): 202 | return self.makePath().boundingRect() 203 | 204 | def moveBy(self, offset): 205 | self.points = [p + offset for p in self.points] 206 | 207 | def moveVertexBy(self, i, offset): 208 | self.points[i] = self.points[i] + offset 209 | 210 | def highlightVertex(self, i, action): 211 | self._highlightIndex = i 212 | self._highlightMode = action 213 | 214 | def highlightClear(self): 215 | self._highlightIndex = None 216 | 217 | def copy(self): 218 | shape = Shape("%s" % self.label) 219 | shape.points = [p for p in self.points] 220 | 221 | shape.center = self.center 222 | shape.direction = self.direction 223 | shape.isRotated = self.isRotated 224 | 225 | shape.fill = self.fill 226 | shape.selected = self.selected 227 | shape._closed = self._closed 228 | if self.line_color != Shape.line_color: 229 | shape.line_color = self.line_color 230 | if self.fill_color != Shape.fill_color: 231 | shape.fill_color = self.fill_color 232 | shape.difficult = self.difficult 233 | return shape 234 | 235 | def __len__(self): 236 | return len(self.points) 237 | 238 | def __getitem__(self, key): 239 | return self.points[key] 240 | 241 | def __setitem__(self, key, value): 242 | self.points[key] = value 243 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /libs/ustr.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | def ustr(x): 4 | '''py2/py3 unicode helper''' 5 | 6 | if sys.version_info < (3, 0, 0): 7 | from PyQt4.QtCore import QString 8 | if type(x) == str: 9 | return x.decode('utf-8') 10 | if type(x) == QString: 11 | return unicode(x) 12 | return x 13 | else: 14 | return x # py3 15 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/robjects.png 12 | icons/close.png 13 | icons/fit-width.png 14 | icons/fit-window.png 15 | icons/undo.png 16 | icons/eye.png 17 | icons/quit.png 18 | icons/copy.png 19 | icons/edit.png 20 | icons/open.png 21 | icons/save.png 22 | icons/save-as.png 23 | icons/color.png 24 | icons/color_line.png 25 | icons/zoom.png 26 | icons/zoom-in.png 27 | icons/zoom-out.png 28 | icons/cancel.png 29 | icons/next.png 30 | icons/prev.png 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [bumpversion] 2 | current_version = 1.3.3 3 | commit = True 4 | tag = True 5 | 6 | [bumpversion:file:setup.py] 7 | search = version='{current_version}' 8 | replace = version='{new_version}' 9 | 10 | [bdist_wheel] 11 | universal = 1 12 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from setuptools import setup 5 | 6 | with open('README.rst') as readme_file: 7 | readme = readme_file.read() 8 | 9 | with open('HISTORY.rst') as history_file: 10 | history = history_file.read() 11 | 12 | requirements = [ 13 | # TODO: put package requirements here 14 | ] 15 | 16 | test_requirements = [ 17 | 'qt', 18 | 'qt4', 19 | 'libxml2' 20 | ] 21 | 22 | setup( 23 | name='labelImg', 24 | version='1.3.3', 25 | description="LabelImg is a graphical image annotation tool and label object bounding boxes in images", 26 | long_description=readme + '\n\n' + history, 27 | author="TzuTa Lin", 28 | author_email='tzu.ta.lin@gmail.com', 29 | url='https://github.com/tzutalin/labelImg', 30 | packages=[ 31 | 'labelImg', 'labelImg.libs' 32 | ], 33 | package_dir={'labelImg': '.'}, 34 | entry_points={ 35 | 'console_scripts': [ 36 | 'labelImg=labelImg.labelImg:main' 37 | ] 38 | }, 39 | include_package_data=True, 40 | install_requires=requirements, 41 | license="MIT license", 42 | zip_safe=False, 43 | keywords='labelImg', 44 | classifiers=[ 45 | 'Development Status :: 2 - Pre-Alpha', 46 | 'Intended Audience :: Developers', 47 | 'License :: OSI Approved :: MIT License', 48 | 'Natural Language :: English', 49 | "Programming Language :: Python :: 2", 50 | 'Programming Language :: Python :: 2.6', 51 | 'Programming Language :: Python :: 2.7', 52 | 'Programming Language :: Python :: 3', 53 | 'Programming Language :: Python :: 3.3', 54 | 'Programming Language :: Python :: 3.4', 55 | 'Programming Language :: Python :: 3.5', 56 | 'Programming Language :: Python :: 3.6', 57 | ], 58 | test_suite='tests', 59 | tests_require=test_requirements 60 | ) 61 | -------------------------------------------------------------------------------- /tests/.gitignore: -------------------------------------------------------------------------------- 1 | test.xml 2 | -------------------------------------------------------------------------------- /tests/test.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/tests/test.bmp -------------------------------------------------------------------------------- /tests/test_io.py: -------------------------------------------------------------------------------- 1 | 2 | from unittest import TestCase 3 | 4 | import sys 5 | import os 6 | dir_name = os.path.abspath(os.path.dirname(__file__)) 7 | libs_path = os.path.join(dir_name, '..', 'libs') 8 | sys.path.insert(0, libs_path) 9 | from pascal_voc_io import PascalVocWriter 10 | from pascal_voc_io import PascalVocReader 11 | 12 | # Test Write/Read 13 | writer = PascalVocWriter('tests', 'test', (512, 512, 1), localImgPath='tests/test.bmp') 14 | difficult = 1 15 | writer.addBndBox(60, 40, 430, 504, 'person', difficult) 16 | writer.addBndBox(113, 40, 450, 403, 'face', difficult) 17 | writer.save('tests/test.xml') 18 | 19 | reader = PascalVocReader('tests/test.xml') 20 | shapes = reader.getShapes() 21 | -------------------------------------------------------------------------------- /tests/test_qt.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 | -------------------------------------------------------------------------------- /tests/臉書.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cgvict/roLabelImg/316da904960259ce9e68030505797bbbe2fad531/tests/臉書.jpg --------------------------------------------------------------------------------