├── .gitignore ├── .travis.yml ├── LICENSE.md ├── Makefile ├── README.md ├── _init_path.py ├── contributors.txt ├── data ├── predefined_classes.txt ├── predefined_cls_classes.txt └── predefined_sub_classes.txt.example ├── freeze_build.py ├── 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 ├── save-as.png ├── save-as.svg ├── save.png ├── save.svg ├── undo-cross.png ├── undo.png ├── zoom-in.png ├── zoom-out.png └── zoom.png ├── labelImg.py ├── labelImg.spec ├── libs ├── ImageManagement.py ├── __init__.py ├── appSettings.py ├── canvas.py ├── colorDialog.py ├── configs.py ├── constants.py ├── labelDialog.py ├── labelFile.py ├── lib.py ├── pascalVocIO.py ├── remoteDialog.py ├── saveMaskImage.py ├── settingDialog.py ├── shape.py ├── toolBar.py ├── ustr.py └── zoomWidget.py ├── resources.py ├── resources.qrc ├── screenshot ├── bbox_label.jpg ├── brush_task.jpg ├── cls_task.jpg ├── parse_label.jpg ├── remote_settings.JPG └── setting_panel.jpg └── scrips ├── __init__.py └── generate_image.py /.gitignore: -------------------------------------------------------------------------------- 1 | icons/.DS_Store 2 | database/ 3 | *.pyc 4 | *.pkl 5 | .*.swp 6 | 7 | build/ 8 | dist/ 9 | 10 | tags 11 | test.py 12 | .idea/ 13 | .vscode/ 14 | tools/ 15 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | 5 | addons: 6 | apt: 7 | packages: 8 | - cmake 9 | - pyqt4-dev-tools 10 | 11 | # command to install dependencies 12 | #install: "pip install --user -r requirements.txt" 13 | # command to run tests 14 | script: make all 15 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | Copyright (c) <2016-2017> lzx1413 2 | 3 | Copyright (C) 2013 MIT, Computer Science and Artificial Intelligence Laboratory. Bryan Russell, Antonio Torralba, William T. Freeman 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | all: resources.py 3 | 4 | %.py: %.qrc 5 | pyrcc4 -o $@ $< 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LabelImg 2 | 3 | [![Build Status](https://travis-ci.org/lzx1413/labelImgPlus.svg?branch=master)](https://travis-ci.org/lzx1413/labelImgPlus) 4 | 5 | LabelImg is a graphical image annotation tool. 6 | 7 | It is written in Python and uses Qt for its graphical interface. 8 | 9 | The annotation file will be saved as an XML file. The annotation format is PASCAL VOC format, and the format is the same as [ImageNet](http://www.image-net.org/) 10 | 11 | task mode change 12 | 13 | ![](screenshot/setting_panel.jpg) 14 | 15 | DET mode 16 | 17 | ![](screenshot/bbox_label.jpg) 18 | 19 | SEG mode 20 | 21 | ![](screenshot/parse_label.jpg) 22 | 23 | CLS mode 24 | 25 | ![](screenshot/cls_task.jpg) 26 | 27 | Brush SEG mode(in development: brush branch) 28 | 29 | ![](screenshot/brush_task.jpg) 30 | 31 | ## Release software for windows 32 | [baiduyun](https://pan.baidu.com/s/1iREYsJiQCzPPZGf6dFvauw) 33 | [googledriver](https://drive.google.com/open?id=118bUKQGlfwLgRTpptNzgBijJneInvw7T) 34 | 35 | ## Build source and use it 36 | 37 | Requires at least [Python 2.6](http://www.python.org/getit/) and has been tested with [PyQt4.8](http://www.riverbankcomputing.co.uk/software/pyqt/intro). 38 | 39 | In order to build the resource and assets, you need to install pyqt4-dev-tools: 40 | 41 | * Ubuntu 42 | 43 | `sudo apt-get install pyqt4-dev-tools` 44 | * Mac 45 | install pyqt4 with [instructions](https://robonobodojo.wordpress.com/2017/02/08/installing-pyqt4-on-mac-osx/) 46 | 47 | `sudo apt-get install python-opencv` 48 | 49 | `pip install lxml` 50 | 51 | `pip install qdarkstyle` 52 | 53 | `./labelImg.py` 54 | 55 | * Windows 56 | 57 | Need to download and setup [Python 2.6](https://www.python.org/downloads/windows/) or later and [PyQt4](https://www.riverbankcomputing.com/software/pyqt/download),lxml,qdarkstyle. 58 | Open cmd and go to $labelImg, 59 | 60 | `$ pyrcc4 -o resources.py resources.qrc` 61 | 62 | `$ python labelImg.py` 63 | 64 | ## Default file framework 65 | 66 | |---Images 67 | 68 | ​ |---images_1 69 | 70 | ​ |---images_2 71 | 72 | |---Annotation 73 | 74 | ​ |---images_1 75 | 76 | ​ |---images_2 77 | 78 | the file containing annotations will be created by default. 79 | 80 | ## Usage 81 | After cloning the code, you should run `$ make all` to generate the resource file. 82 | 83 | You can then start annotating by running `$ ./labelImg.py`. For usage 84 | instructions you can see [Here](https://youtu.be/p0nR2YsCY_U) 85 | 86 | At the moment annotations are saved as an XML file. The format is PASCAL VOC format, and the format is the same as [ImageNet](http://www.image-net.org/) 87 | 88 | You can also see [ImageNet Utils](https://github.com/tzutalin/ImageNet_Utils) to download image, create a label text for machine learning, etc 89 | 90 | ### Label and parsing 91 | 92 | support rectangle label and parsing labels 93 | 94 | ### Create pre-defined classes 95 | 96 | You can edit the [data/predefined_classes.txt](https://github.com/tzutalin/labelImg/blob/master/data/predefined_classes.txt) to load pre-defined classes 97 | 98 | You also can create labels with two levels in [data/predefined_sub_classes.txt](https://github.com/lzx1413/labelImg/blob/master/data/predefined_sub_classes.txt) 99 | 100 | And the labels will be ranked by the frequency you use it. 101 | 102 | ### General steps from scratch 103 | 104 | * Build and launch: `$ make all; python labelImg.py` 105 | 106 | * Click 'Change default saved annotation folder' in Menu/File 107 | 108 | * Click 'Open Dir' 109 | 110 | * Click 'Create RectBox' 111 | 112 | The annotation will be saved to the folder you specifiy 113 | 114 | ### Hotkeys 115 | 116 | * Ctrl + r : Change the defult target dir which saving annotation files 117 | 118 | * Ctrl + n : Create a bounding box 119 | 120 | * Ctrl + s : Save 121 | 122 | * Right : Next image 123 | 124 | * Left : Previous image 125 | 126 | ### Online image data mode 127 | 128 | the server have to make the images in a folder that clint can get from http/https with **get** function 129 | 130 | * settings 131 | 132 | open File -->RemoteDBSettings(ctrl+m) like that 133 | 134 | ![](screenshot/remote_settings.JPG) 135 | 136 | the remote image list is a file contenting the name of the images (a line is a image) . 137 | 138 | the image will be cached in a folder created in the software file named database/pics/XXXX and this will take a lot of memory if there are a lot of images,and this will be modified in the future. 139 | 140 | open File -->ChangedDefaultSavedAnnotationDir(ctrl+r) to set the folder to save the results 141 | 142 | 2. if your settings are right,you will find the **Get Images** button becomes enabled and click it ,then you can annotate the images as before 143 | 144 | ### Change list 145 | * 18-08-19 py3 pyqt5 supported in the branch py3 146 | 147 | * 17-08-14 add class label function 148 | 149 | ### Todo list 150 | * support pyqt5 and python 3 151 | * add more functions while adding parsing labels 152 | * refine the setting functions 153 | 154 | ### How to contribute 155 | Send a pull request 156 | 157 | ### License 158 | [License](LICENSE.md) 159 | -------------------------------------------------------------------------------- /_init_path.py: -------------------------------------------------------------------------------- 1 | """Set up paths""" 2 | import sys 3 | 4 | def add_path(path): 5 | if path not in sys.path: 6 | sys.path.insert(0, path) 7 | 8 | add_path('libs') 9 | -------------------------------------------------------------------------------- /contributors.txt: -------------------------------------------------------------------------------- 1 | TzuTa Lin 2 | lzx1413 3 | [LabelMe](http://labelme2.csail.mit.edu/Release3.0/index.php) 4 | 5 | -------------------------------------------------------------------------------- /data/predefined_classes.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 4 5 | 5 6 | 6 7 | 7 8 | 8 9 | 9 10 | 0 11 | A 12 | B 13 | C 14 | D 15 | H 16 | X 17 | - 18 | = -------------------------------------------------------------------------------- /data/predefined_cls_classes.txt: -------------------------------------------------------------------------------- 1 | dog 2 | cat 3 | people 4 | -------------------------------------------------------------------------------- /data/predefined_sub_classes.txt.example: -------------------------------------------------------------------------------- 1 | body: hair face neck arm leg hand skin foot 2 | upper cloth: Apparel blazer blouse cardigan coat jacket hoodie jumper shirt sweater sweatshirt top t-shirt vest suit intimate 3 | trouser: jeans pants leggings panties romper shorts tights 4 | shoe: boots clogs flats heels loafers pumps sandals shoes sneakers wedges 5 | accessory: accessory bracelet decor earrings necklace Pearl ring sunglasses glasses watch 6 | overcoat: bodysuit swimwear 7 | cloth: bag belt bra cape gloves hat purse satchel scarf socks tie stockings denim 8 | dress: dress skirt 9 | others: case cream others wallet souvenir sister black -------------------------------------------------------------------------------- /freeze_build.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | from subprocess import call 3 | call(["pyinstaller", "--onefile", "--windowed", "labelImg.py"]) 4 | 5 | # Now it is a workaround. It should use hook file 6 | def readlines(filename): 7 | result = [] 8 | with open(filename, "r") as ins: 9 | for line in ins: 10 | result.append(line) 11 | return result 12 | 13 | lines = readlines('labelImg.spec') 14 | for ind, line in enumerate(lines): 15 | if 'hiddenimports' in line: 16 | lines[ind] = "\t\t\t hiddenimports = ['cv2', 'json', 'lxml.etree', 'lxml', 'etree', 'xml.etree.ElementTree'],\n" 17 | print lines[ind] 18 | 19 | FILE = open('labelImg.spec', "w") 20 | FILE.writelines(lines) 21 | FILE.close() 22 | 23 | call(["pyinstaller", "labelImg.spec"]) 24 | -------------------------------------------------------------------------------- /icons/cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/cancel.png -------------------------------------------------------------------------------- /icons/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/close.png -------------------------------------------------------------------------------- /icons/color.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/color.png -------------------------------------------------------------------------------- /icons/color_line.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/color_line.png -------------------------------------------------------------------------------- /icons/copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/copy.png -------------------------------------------------------------------------------- /icons/delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/delete.png -------------------------------------------------------------------------------- /icons/done.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/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/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/edit.png -------------------------------------------------------------------------------- /icons/expert1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/expert1.png -------------------------------------------------------------------------------- /icons/expert2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/expert2.png -------------------------------------------------------------------------------- /icons/eye.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/eye.png -------------------------------------------------------------------------------- /icons/feBlend-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/feBlend-icon.png -------------------------------------------------------------------------------- /icons/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/file.png -------------------------------------------------------------------------------- /icons/fit-width.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/fit-width.png -------------------------------------------------------------------------------- /icons/fit-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/fit-window.png -------------------------------------------------------------------------------- /icons/fit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/fit.png -------------------------------------------------------------------------------- /icons/help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/help.png -------------------------------------------------------------------------------- /icons/labels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/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/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/new.png -------------------------------------------------------------------------------- /icons/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/next.png -------------------------------------------------------------------------------- /icons/objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/objects.png -------------------------------------------------------------------------------- /icons/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/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/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/prev.png -------------------------------------------------------------------------------- /icons/quit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/quit.png -------------------------------------------------------------------------------- /icons/save-as.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/save-as.png -------------------------------------------------------------------------------- /icons/save.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/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 | 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 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/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/undo-cross.png -------------------------------------------------------------------------------- /icons/undo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/undo.png -------------------------------------------------------------------------------- /icons/zoom-in.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/zoom-in.png -------------------------------------------------------------------------------- /icons/zoom-out.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/zoom-out.png -------------------------------------------------------------------------------- /icons/zoom.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/icons/zoom.png -------------------------------------------------------------------------------- /labelImg.spec: -------------------------------------------------------------------------------- 1 | # -*- mode: python -*- 2 | 3 | block_cipher = None 4 | 5 | 6 | a = Analysis(['labelImg.py'], 7 | pathex=['D:\\workplace\\labelImg'], 8 | binaries=[], 9 | datas=[], 10 | hiddenimports=[], 11 | hookspath=[], 12 | runtime_hooks=[], 13 | excludes=[], 14 | win_no_prefer_redirects=False, 15 | win_private_assemblies=False, 16 | cipher=block_cipher) 17 | pyz = PYZ(a.pure, a.zipped_data, 18 | cipher=block_cipher) 19 | exe = EXE(pyz, 20 | a.scripts, 21 | a.binaries, 22 | a.zipfiles, 23 | a.datas, 24 | name='labelImg', 25 | debug=False, 26 | strip=False, 27 | upx=True, 28 | console=True ) 29 | -------------------------------------------------------------------------------- /libs/ImageManagement.py: -------------------------------------------------------------------------------- 1 | import urllib 2 | import threading 3 | import os 4 | 5 | 6 | class loadImageThread(threading.Thread): 7 | 8 | def __init__(self, website, image_list, dowloaded_list, FilePath): 9 | threading.Thread.__init__(self) 10 | self.website = str(website) 11 | self.filepath = FilePath 12 | self.image_list = image_list 13 | self.mDowloaded_list = dowloaded_list 14 | 15 | def run(self): 16 | for image_url in self.image_list: 17 | print self.website + image_url 18 | urllib.urlretrieve( 19 | self.website + image_url, 20 | self.filepath + image_url) 21 | self.mDowloaded_list.append( 22 | os.path.abspath( 23 | self.filepath + image_url)) 24 | 25 | 26 | def loadOnlineImgMul( 27 | website, 28 | image_list, 29 | thread_num, 30 | dowloaded_image_list, 31 | FilePath): 32 | if len(image_list) / thread_num == 0: 33 | thread_num = 1 34 | if thread_num == 1: 35 | num_per_thread = len(image_list) 36 | else: 37 | num_per_thread = len(image_list) / thread_num 38 | for i in xrange(thread_num + 1): 39 | if (i + 1) * num_per_thread > len(image_list): 40 | t = loadImageThread( 41 | website, 42 | image_list[ 43 | i * num_per_thread:-1], 44 | dowloaded_image_list, 45 | FilePath) 46 | t.start() 47 | else: 48 | t = loadImageThread( 49 | website, 50 | image_list[ 51 | i * 52 | num_per_thread:( 53 | i + 54 | 1) * 55 | num_per_thread], 56 | dowloaded_image_list, 57 | FilePath) 58 | t.start() 59 | -------------------------------------------------------------------------------- /libs/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/libs/__init__.py -------------------------------------------------------------------------------- /libs/appSettings.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import os 3 | 4 | class APPSettings(object): 5 | def __init__(self): 6 | self.data = {} 7 | self.path = '.settings.pkl' 8 | 9 | def __setitem__(self, key, value): 10 | self.data[key] = value 11 | 12 | def __getitem__(self, key): 13 | return self.data[key] 14 | 15 | def get(self, key, default=None): 16 | if key in self.data: 17 | return self.data[key] 18 | return default 19 | 20 | def save(self): 21 | with open(self.path, 'wb') as f: 22 | pickle.dump(self.data, f, pickle.HIGHEST_PROTOCOL) 23 | return True 24 | return False 25 | 26 | def load(self): 27 | if os.path.exists(self.path): 28 | with open(self.path, 'rb') as f: 29 | self.data = pickle.load(f) 30 | return True 31 | return False 32 | 33 | -------------------------------------------------------------------------------- /libs/canvas.py: -------------------------------------------------------------------------------- 1 | from PyQt4.QtGui import * 2 | from PyQt4.QtCore import * 3 | #from PyQt4.QtOpenGL import * 4 | 5 | from shape import Shape 6 | from lib import distance 7 | 8 | CURSOR_DEFAULT = Qt.ArrowCursor 9 | CURSOR_POINT = Qt.PointingHandCursor 10 | CURSOR_DRAW = Qt.CrossCursor 11 | CURSOR_MOVE = Qt.ClosedHandCursor 12 | CURSOR_GRAB = Qt.OpenHandCursor 13 | 14 | # class Canvas(QGLWidget): 15 | 16 | 17 | class Canvas(QWidget): 18 | zoomRequest = pyqtSignal(int) 19 | scrollRequest = pyqtSignal(int, int) 20 | newShape = pyqtSignal() 21 | selectionChanged = pyqtSignal(bool) 22 | shapeMoved = pyqtSignal() 23 | drawingPolygon = pyqtSignal(bool) 24 | 25 | CREATE, EDIT = range(2) 26 | RECT_SHAPE, POLYGON_SHAPE = range(2) 27 | 28 | epsilon = 11.0 29 | 30 | def __init__(self, *args, **kwargs): 31 | super(Canvas, self).__init__(*args, **kwargs) 32 | # Initialise local state. 33 | self.shape_type = self.POLYGON_SHAPE 34 | self.brush_point = None 35 | self.task_mode = 3 36 | self.erase_mode = False 37 | self.current_brush_path = None 38 | self.mask_Image = None 39 | self.brush_color =QColor(255,0,0,255) 40 | self.brush_size = 10 41 | self.brush = QPainter(); 42 | self.mode = self.EDIT 43 | self.shapes = [] 44 | self.current = None 45 | self.selectedShape = None # save the selected shape here 46 | self.selectedShapeCopy = None 47 | self.lineColor = QColor(0, 0, 255) 48 | self.line = Shape(line_color=self.lineColor) 49 | self.prevPoint = QPointF() 50 | self.offsets = QPointF(), QPointF() 51 | self.scale = 1.0 52 | self.bg_image = QImage() 53 | self.visible = {} 54 | self._hideBackround = False 55 | self.hideBackround = False 56 | self.hShape = None 57 | self.hVertex = None 58 | self._painter = QPainter(self) 59 | self.font_size = 50 60 | self._cursor = CURSOR_DEFAULT 61 | # Menus: 62 | self.menus = (QMenu(), QMenu()) 63 | # Set widget options. 64 | self.setMouseTracking(True) 65 | self.setFocusPolicy(Qt.WheelFocus) 66 | 67 | def set_shape_type(self, type): 68 | if type == 0: 69 | self.shape_type = self.RECT_SHAPE 70 | self.line.set_shape_type(type) 71 | return True 72 | elif type == 1: 73 | self.shape_type = self.POLYGON_SHAPE 74 | self.line.set_shape_type(type) 75 | return True 76 | else: 77 | print "not support the shape type: " + str(type) 78 | return False 79 | 80 | def enterEvent(self, ev): 81 | self.overrideCursor(self._cursor) 82 | def get_mask_image(self): 83 | return self.mask_pixmap 84 | 85 | def leaveEvent(self, ev): 86 | self.restoreCursor() 87 | 88 | def focusOutEvent(self, ev): 89 | self.restoreCursor() 90 | 91 | def isVisible(self, shape): 92 | return self.visible.get(shape, True) 93 | 94 | def drawing(self): 95 | return self.mode == self.CREATE 96 | 97 | def editing(self): 98 | return self.mode == self.EDIT 99 | 100 | def setEditing(self, value=True): 101 | self.mode = self.EDIT if value else self.CREATE 102 | if not value: # Create 103 | self.unHighlight() 104 | self.deSelectShape() 105 | 106 | def unHighlight(self): 107 | if self.hShape: 108 | self.hShape.highlightClear() 109 | self.hVertex = self.hShape = None 110 | 111 | def selectedVertex(self): 112 | return self.hVertex is not None 113 | 114 | def mouseMoveEvent(self, ev): 115 | """Update line with last point and current coordinates.""" 116 | pos = self.transformPos(ev.posF()) 117 | self.restoreCursor() 118 | if self.task_mode == 3: 119 | self.brush_point = pos 120 | 121 | if Qt.LeftButton & ev.buttons(): 122 | if self.outOfPixmap(pos): 123 | return 124 | if not self.current_brush_path: 125 | self.current_brush_path = QPainterPath() 126 | self.current_brush_path.moveTo(pos) 127 | else: 128 | self.current_brush_path.lineTo(pos) 129 | self.repaint() 130 | return 131 | 132 | 133 | # Polygon drawing. 134 | if self.drawing(): 135 | self.overrideCursor(CURSOR_DRAW) 136 | if self.current: 137 | color = self.lineColor 138 | if self.outOfPixmap(pos): 139 | # Don't allow the user to draw outside the pixmap. 140 | # Project the point to the pixmap's edges. 141 | pos = self.intersectionPoint(self.current[-1], pos) 142 | elif len(self.current) > 1 and self.closeEnough(pos, self.current[0]): 143 | # Attract line to starting point and colorise to alert the 144 | # user: 145 | pos = self.current[0] 146 | color = self.current.line_color 147 | self.overrideCursor(CURSOR_POINT) 148 | self.current.highlightVertex(0, Shape.NEAR_VERTEX) 149 | self.line[1] = pos 150 | self.line.line_color = color 151 | self.repaint() 152 | self.current.highlightClear() 153 | return 154 | 155 | # Polygon copy moving. 156 | if Qt.RightButton & ev.buttons(): 157 | if self.selectedShapeCopy and self.prevPoint: 158 | self.overrideCursor(CURSOR_MOVE) 159 | self.boundedMoveShape(self.selectedShapeCopy, pos) 160 | self.repaint() 161 | elif self.selectedShape: 162 | self.selectedShapeCopy = self.selectedShape.copy() 163 | self.repaint() 164 | return 165 | 166 | # Polygon/Vertex moving. 167 | if Qt.LeftButton & ev.buttons(): 168 | if self.selectedVertex(): 169 | self.boundedMoveVertex(pos) 170 | self.shapeMoved.emit() 171 | self.repaint() 172 | elif self.selectedShape and self.prevPoint: 173 | self.overrideCursor(CURSOR_MOVE) 174 | self.boundedMoveShape(self.selectedShape, pos) 175 | self.shapeMoved.emit() 176 | self.repaint() 177 | return 178 | 179 | # Just hovering over the canvas, 2 posibilities: 180 | # - Highlight shapes 181 | # - Highlight vertex 182 | # Update shape/vertex fill and tooltip value accordingly. 183 | self.setToolTip("Image") 184 | for shape in reversed([s for s in self.shapes if self.isVisible(s)]): 185 | # Look for a nearby vertex to highlight. If that fails, 186 | # check if we happen to be inside a shape. 187 | index = shape.nearestVertex(pos, self.epsilon) 188 | if index is not None: 189 | if self.selectedVertex(): 190 | self.hShape.highlightClear() 191 | self.hVertex, self.hShape = index, shape 192 | shape.highlightVertex(index, shape.MOVE_VERTEX) 193 | self.overrideCursor(CURSOR_POINT) 194 | self.setToolTip("Click & drag to move point") 195 | self.setStatusTip(self.toolTip()) 196 | self.update() 197 | break 198 | elif shape.containsPoint(pos): 199 | if self.selectedVertex(): 200 | self.hShape.highlightClear() 201 | self.hVertex, self.hShape = None, shape 202 | self.setToolTip( 203 | "Click & drag to move shape '%s'" % 204 | shape.label) 205 | self.setStatusTip(self.toolTip()) 206 | self.overrideCursor(CURSOR_GRAB) 207 | self.update() 208 | break 209 | else: # Nothing found, clear highlights, reset state. 210 | if self.hShape: 211 | self.hShape.highlightClear() 212 | self.update() 213 | self.hVertex, self.hShape = None, None 214 | 215 | def mousePressEvent(self, ev): 216 | pos = self.transformPos(ev.posF()) 217 | if ev.button() == Qt.LeftButton: 218 | if self.drawing(): 219 | if self.shape_type == self.POLYGON_SHAPE and self.current: 220 | self.current.addPoint(self.line[1]) 221 | self.line[0] = self.current[-1] 222 | if self.current.isClosed(): 223 | self.finalise() 224 | elif self.shape_type == self.RECT_SHAPE and self.current and self.current.reachMaxPoints() is False: 225 | initPos = self.current[0] 226 | minX = initPos.x() 227 | minY = initPos.y() 228 | targetPos = self.line[1] 229 | maxX = targetPos.x() 230 | maxY = targetPos.y() 231 | self.current.addPoint(QPointF(minX, maxY)) 232 | self.current.addPoint(targetPos) 233 | self.current.addPoint(QPointF(maxX, minY)) 234 | self.current.addPoint(initPos) 235 | self.line[0] = self.current[-1] 236 | if self.current.isClosed(): 237 | self.finalise() 238 | elif not self.outOfPixmap(pos): 239 | self.current = Shape(shape_type=self.shape_type) 240 | self.current.addPoint(pos) 241 | self.line.points = [pos, pos] 242 | self.setHiding() 243 | self.drawingPolygon.emit(True) 244 | self.update() 245 | else: 246 | self.selectShapePoint(pos) 247 | self.prevPoint = pos 248 | self.repaint() 249 | elif ev.button() == Qt.RightButton and self.editing(): 250 | self.selectShapePoint(pos) 251 | self.prevPoint = pos 252 | self.repaint() 253 | 254 | def mouseReleaseEvent(self, ev): 255 | if ev.button() == Qt.RightButton: 256 | menu = self.menus[bool(self.selectedShapeCopy)] 257 | self.restoreCursor() 258 | if not menu.exec_(self.mapToGlobal(ev.pos()))\ 259 | and self.selectedShapeCopy: 260 | # Cancel the move by deleting the shadow copy. 261 | self.selectedShapeCopy = None 262 | self.repaint() 263 | elif ev.button() == Qt.LeftButton and self.selectedShape: 264 | self.overrideCursor(CURSOR_GRAB) 265 | elif ev.button() == Qt.LeftButton and self.task_mode == 3 and self.current_brush_path: 266 | self.current_brush_path = None 267 | 268 | def endMove(self, copy=False): 269 | assert self.selectedShape and self.selectedShapeCopy 270 | shape = self.selectedShapeCopy 271 | #del shape.fill_color 272 | #del shape.line_color 273 | if copy: 274 | self.shapes.append(shape) 275 | self.selectedShape.selected = False 276 | self.selectedShape = shape 277 | self.repaint() 278 | else: 279 | shape.label = self.selectedShape.label 280 | self.deleteSelected() 281 | self.shapes.append(shape) 282 | self.selectedShapeCopy = None 283 | 284 | def hideBackroundShapes(self, value): 285 | self.hideBackround = value 286 | if self.selectedShape: 287 | # Only hide other shapes if there is a current selection. 288 | # Otherwise the user will not be able to select a shape. 289 | self.setHiding(True) 290 | self.repaint() 291 | 292 | def setHiding(self, enable=True): 293 | self._hideBackround = self.hideBackround if enable else False 294 | 295 | def canCloseShape(self): 296 | return self.drawing() and self.current and len(self.current) > 2 297 | 298 | def mouseDoubleClickEvent(self, ev): 299 | # We need at least 4 points here, since the mousePress handler 300 | # adds an extra one before this handler is called. 301 | if self.canCloseShape() and len(self.current) > 3: 302 | self.current.popPoint() 303 | self.finalise() 304 | 305 | def selectShape(self, shape): 306 | self.deSelectShape() 307 | shape.selected = True 308 | self.selectedShape = shape 309 | self.setHiding() 310 | self.selectionChanged.emit(True) 311 | self.update() 312 | 313 | def selectShapePoint(self, point): 314 | """Select the first shape created which contains this point.""" 315 | self.deSelectShape() 316 | if self.selectedVertex(): # A vertex is marked for selection. 317 | index, shape = self.hVertex, self.hShape 318 | shape.highlightVertex(index, shape.MOVE_VERTEX) 319 | return 320 | for shape in reversed(self.shapes): 321 | if self.isVisible(shape) and shape.containsPoint(point): 322 | shape.selected = True 323 | self.selectedShape = shape 324 | self.calculateOffsets(shape, point) 325 | self.setHiding() 326 | self.selectionChanged.emit(True) 327 | return 328 | 329 | def calculateOffsets(self, shape, point): 330 | rect = shape.boundingRect() 331 | x1 = rect.x() - point.x() 332 | y1 = rect.y() - point.y() 333 | x2 = (rect.x() + rect.width()) - point.x() 334 | y2 = (rect.y() + rect.height()) - point.y() 335 | self.offsets = QPointF(x1, y1), QPointF(x2, y2) 336 | 337 | def boundedMoveVertex(self, pos): 338 | index, shape = self.hVertex, self.hShape 339 | point = shape[index] 340 | if self.outOfPixmap(pos): 341 | pos = self.intersectionPoint(point, pos) 342 | 343 | shiftPos = pos - point 344 | shape.moveVertexBy(index, shiftPos) 345 | 346 | if self.shape_type == self.RECT_SHAPE: 347 | lindex = (index + 1) % 4 348 | rindex = (index + 3) % 4 349 | lshift = None 350 | rshift = None 351 | if index % 2 == 0: 352 | lshift = QPointF(shiftPos.x(), 0) 353 | rshift = QPointF(0, shiftPos.y()) 354 | else: 355 | rshift = QPointF(shiftPos.x(), 0) 356 | lshift = QPointF(0, shiftPos.y()) 357 | shape.moveVertexBy(rindex, rshift) 358 | shape.moveVertexBy(lindex, lshift) 359 | 360 | def boundedMoveShape(self, shape, pos): 361 | if self.outOfPixmap(pos): 362 | return False # No need to move 363 | o1 = pos + self.offsets[0] 364 | if self.outOfPixmap(o1): 365 | pos -= QPointF(min(0, o1.x()), min(0, o1.y())) 366 | o2 = pos + self.offsets[1] 367 | if self.outOfPixmap(o2): 368 | pos += QPointF(min(0, self.bg_image.width() - o2.x()), 369 | min(0, self.bg_image.height() - o2.y())) 370 | # The next line tracks the new position of the cursor 371 | # relative to the shape, but also results in making it 372 | # a bit "shaky" when nearing the border and allows it to 373 | # go outside of the shape's area for some reason. XXX 374 | #self.calculateOffsets(self.selectedShape, pos) 375 | dp = pos - self.prevPoint 376 | if dp: 377 | shape.moveBy(dp) 378 | self.prevPoint = pos 379 | return True 380 | return False 381 | 382 | def deSelectShape(self): 383 | if self.selectedShape: 384 | self.selectedShape.selected = False 385 | self.selectedShape = None 386 | self.setHiding(False) 387 | self.selectionChanged.emit(False) 388 | self.update() 389 | 390 | def deleteSelected(self): 391 | if self.selectedShape: 392 | shape = self.selectedShape 393 | self.shapes.remove(self.selectedShape) 394 | self.selectedShape = None 395 | self.update() 396 | return shape 397 | 398 | def copySelectedShape(self): 399 | if self.selectedShape: 400 | shape = self.selectedShape.copy() 401 | self.deSelectShape() 402 | self.shapes.append(shape) 403 | shape.selected = True 404 | self.selectedShape = shape 405 | self.boundedShiftShape(shape) 406 | return shape 407 | 408 | def boundedShiftShape(self, shape): 409 | # Try to move in one direction, and if it fails in another. 410 | # Give up if both fail. 411 | point = shape[0] 412 | offset = QPointF(2.0, 2.0) 413 | self.calculateOffsets(shape, point) 414 | self.prevPoint = point 415 | if not self.boundedMoveShape(shape, point - offset): 416 | self.boundedMoveShape(shape, point + offset) 417 | 418 | def paintEvent(self, event): 419 | if not self.bg_image: 420 | return super(Canvas, self).paintEvent(event) 421 | 422 | p = self._painter 423 | p.begin(self) 424 | p.setFont(QFont('Times', self.font_size, QFont.Bold)) 425 | p.setRenderHint(QPainter.Antialiasing) 426 | p.setRenderHint(QPainter.HighQualityAntialiasing) 427 | p.setRenderHint(QPainter.SmoothPixmapTransform) 428 | 429 | p.scale(self.scale, self.scale) 430 | p.translate(self.offsetToCenter()) 431 | 432 | p.drawImage(0, 0, self.bg_image) 433 | #print self.brush_point.x(),self.brush_point.y() 434 | if self.task_mode == 3: 435 | p.setOpacity(0.3) 436 | p.drawImage(0,0,self.mask_pixmap) 437 | if self.brush_point: 438 | p.drawEllipse(self.brush_point,self.brush_size/2,self.brush_size/2) 439 | if self.current_brush_path: 440 | if self.mask_pixmap.isNull(): 441 | self.mask_pixmap = QImage(self.bg_image.size(), QImage.Format_ARGB32) 442 | self.mask_pixmap.fill(QColor(255,255,255,0)) 443 | self.brush.begin(self.mask_pixmap) 444 | brush_pen = QPen() 445 | self.brush.setCompositionMode(QPainter.CompositionMode_Source) 446 | brush_pen.setColor(self.brush_color) 447 | brush_pen.setWidth(self.brush_size) 448 | brush_pen.setCapStyle(Qt.RoundCap) 449 | brush_pen.setJoinStyle(Qt.RoundJoin) 450 | self.brush.setPen(brush_pen) 451 | self.brush.drawPath(self.current_brush_path) 452 | self.brush.end() 453 | Shape.scale = self.scale 454 | for shape in self.shapes: 455 | if shape.fill_color: 456 | shape.fill = True 457 | shape.paint(p) 458 | elif (shape.selected or not self._hideBackround) and self.isVisible(shape): 459 | shape.fill = shape.selected or shape == self.hShape 460 | shape.paint(p) 461 | if self.current: 462 | self.current.paint(p) 463 | self.line.paint(p) 464 | if self.selectedShapeCopy: 465 | self.selectedShapeCopy.paint(p) 466 | # Paint rect 467 | if self.current is not None and len(self.line) == 2: 468 | leftTop = self.line[0] 469 | rightBottom = self.line[1] 470 | rectWidth = rightBottom.x() - leftTop.x() 471 | rectHeight = rightBottom.y() - leftTop.y() 472 | color = QColor(0, 220, 0) 473 | p.setPen(color) 474 | brush = QBrush(Qt.BDiagPattern) 475 | p.setBrush(brush) 476 | if self.shape_type == self.RECT_SHAPE: 477 | p.drawRect(leftTop.x(), leftTop.y(), rectWidth, rectHeight) 478 | 479 | p.end() 480 | 481 | def transformPos(self, point): 482 | """Convert from widget-logical coordinates to painter-logical coordinates.""" 483 | return point / self.scale - self.offsetToCenter() 484 | 485 | def offsetToCenter(self): 486 | s = self.scale 487 | area = super(Canvas, self).size() 488 | if self.bg_image: 489 | w, h = self.bg_image.width() * s, self.bg_image.height() * s 490 | else: 491 | w,h = 100,100 492 | aw, ah = area.width(), area.height() 493 | x = (aw - w) / (2 * s) if aw > w else 0 494 | y = (ah - h) / (2 * s) if ah > h else 0 495 | return QPointF(x, y) 496 | 497 | def outOfPixmap(self, p): 498 | w, h = self.bg_image.width(), self.bg_image.height() 499 | return not (0 <= p.x() <= w and 0 <= p.y() <= h) 500 | 501 | def finalise(self): 502 | assert self.current 503 | self.current.close() 504 | self.shapes.append(self.current) 505 | self.current = None 506 | self.setHiding(False) 507 | self.newShape.emit() 508 | self.update() 509 | 510 | def closeEnough(self, p1, p2): 511 | #d = distance(p1 - p2) 512 | #m = (p1-p2).manhattanLength() 513 | # print "d %.2f, m %d, %.2f" % (d, m, d - m) 514 | return distance(p1 - p2) < self.epsilon 515 | 516 | def intersectionPoint(self, p1, p2): 517 | # Cycle through each image edge in clockwise fashion, 518 | # and find the one intersecting the current line segment. 519 | # http://paulbourke.net/geometry/lineline2d/ 520 | size = self.bg_image.size() 521 | points = [(0, 0), 522 | (size.width(), 0), 523 | (size.width(), size.height()), 524 | (0, size.height())] 525 | x1, y1 = p1.x(), p1.y() 526 | x2, y2 = p2.x(), p2.y() 527 | d, i, (x, y) = min(self.intersectingEdges((x1, y1), (x2, y2), points)) 528 | x3, y3 = points[i] 529 | x4, y4 = points[(i + 1) % 4] 530 | if (x, y) == (x1, y1): 531 | # Handle cases where previous point is on one of the edges. 532 | if x3 == x4: 533 | return QPointF(x3, min(max(0, y2), max(y3, y4))) 534 | else: # y3 == y4 535 | return QPointF(min(max(0, x2), max(x3, x4)), y3) 536 | return QPointF(x, y) 537 | 538 | def intersectingEdges(self, xxx_todo_changeme, xxx_todo_changeme1, points): 539 | """For each edge formed by `points', yield the intersection 540 | with the line segment `(x1,y1) - (x2,y2)`, if it exists. 541 | Also return the distance of `(x2,y2)' to the middle of the 542 | edge along with its index, so that the one closest can be chosen.""" 543 | (x1, y1) = xxx_todo_changeme 544 | (x2, y2) = xxx_todo_changeme1 545 | for i in xrange(4): 546 | x3, y3 = points[i] 547 | x4, y4 = points[(i + 1) % 4] 548 | denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1) 549 | nua = (x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3) 550 | nub = (x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3) 551 | if denom == 0: 552 | # This covers two cases: 553 | # nua == nub == 0: Coincident 554 | # otherwise: Parallel 555 | continue 556 | ua, ub = nua / denom, nub / denom 557 | if 0 <= ua <= 1 and 0 <= ub <= 1: 558 | x = x1 + ua * (x2 - x1) 559 | y = y1 + ua * (y2 - y1) 560 | m = QPointF((x3 + x4) / 2, (y3 + y4) / 2) 561 | d = distance(m - QPointF(x2, y2)) 562 | yield d, i, (x, y) 563 | 564 | # These two, along with a call to adjustSize are required for the 565 | # scroll area. 566 | def sizeHint(self): 567 | return self.minimumSizeHint() 568 | 569 | def minimumSizeHint(self): 570 | if self.bg_image: 571 | return self.scale * self.bg_image.size() 572 | return super(Canvas, self).minimumSizeHint() 573 | 574 | def wheelEvent(self, ev): 575 | if ev.orientation() == Qt.Vertical: 576 | mods = ev.modifiers() 577 | if Qt.ControlModifier == int(mods): 578 | self.zoomRequest.emit(ev.delta()) 579 | else: 580 | self.scrollRequest.emit( 581 | ev.delta(), Qt.Horizontal if ( 582 | Qt.ShiftModifier == int(mods)) else Qt.Vertical) 583 | else: 584 | self.scrollRequest.emit(ev.delta(), Qt.Horizontal) 585 | ev.accept() 586 | 587 | def keyPressEvent(self, ev): 588 | key = ev.key() 589 | if key == Qt.Key_Escape and self.current: 590 | print 'ESC press' 591 | self.current = None 592 | self.drawingPolygon.emit(False) 593 | self.update() 594 | elif key == Qt.Key_Return and self.canCloseShape(): 595 | self.finalise() 596 | 597 | def setLastLabel(self, text): 598 | assert text 599 | self.shapes[-1].label = text 600 | return self.shapes[-1] 601 | 602 | def undoLastLine(self): 603 | assert self.shapes 604 | self.current = self.shapes.pop() 605 | self.current.setOpen() 606 | self.line.points = [self.current[-1], self.current[0]] 607 | self.drawingPolygon.emit(True) 608 | 609 | def resetAllLines(self): 610 | assert self.shapes 611 | self.current = self.shapes.pop() 612 | self.current.setOpen() 613 | self.line.points = [self.current[-1], self.current[0]] 614 | self.drawingPolygon.emit(True) 615 | self.current = None 616 | self.drawingPolygon.emit(False) 617 | self.update() 618 | 619 | def loadMaskmap(self,mask): 620 | self.mask_pixmap = mask 621 | self.repaint() 622 | def loadPixmap(self, pixmap): 623 | self.bg_image = pixmap 624 | self.shapes = [] 625 | self.mask_pixmap = QImage(self.bg_image.size(), QImage.Format_ARGB32) 626 | self.mask_pixmap.fill(QColor(255,255,255,0)) 627 | self.repaint() 628 | 629 | def loadShapes(self, shapes): 630 | self.shapes = list(shapes) 631 | self.shape_type = shapes[0].get_shape_type() 632 | print self.shape_type 633 | self.current = None 634 | self.repaint() 635 | 636 | def setShapeVisible(self, shape, value): 637 | self.visible[shape] = value 638 | self.repaint() 639 | 640 | def overrideCursor(self, cursor): 641 | self.restoreCursor() 642 | self._cursor = cursor 643 | QApplication.setOverrideCursor(cursor) 644 | 645 | def restoreCursor(self): 646 | QApplication.restoreOverrideCursor() 647 | 648 | def resetState(self): 649 | self.restoreCursor() 650 | self.bg_image = None 651 | self.update() 652 | -------------------------------------------------------------------------------- /libs/colorDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt4.QtGui import * 2 | from PyQt4.QtCore import * 3 | 4 | BB = QDialogButtonBox 5 | 6 | 7 | class ColorDialog(QColorDialog): 8 | 9 | def __init__(self, parent=None): 10 | super(ColorDialog, self).__init__(parent) 11 | self.setOption(QColorDialog.ShowAlphaChannel) 12 | # The Mac native dialog does not support our restore button. 13 | self.setOption(QColorDialog.DontUseNativeDialog) 14 | # Add a restore defaults button. 15 | # The default is set at invocation time, so that it 16 | # works across dialogs for different elements. 17 | self.default = None 18 | self.bb = self.layout().itemAt(1).widget() 19 | self.bb.addButton(BB.RestoreDefaults) 20 | self.bb.clicked.connect(self.checkRestore) 21 | 22 | def getColor(self, value=None, title=None, default=None): 23 | self.default = default 24 | if title: 25 | self.setWindowTitle(title) 26 | if value: 27 | self.setCurrentColor(value) 28 | return self.currentColor() if self.exec_() else None 29 | 30 | def checkRestore(self, button): 31 | if self.bb.buttonRole(button) & BB.ResetRole and self.default: 32 | self.setCurrentColor(self.default) 33 | -------------------------------------------------------------------------------- /libs/configs.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/libs/configs.py -------------------------------------------------------------------------------- /libs/constants.py: -------------------------------------------------------------------------------- 1 | SETTING_FILENAME = 'filename' 2 | SETTING_RECENT_FILES = 'recentFiles' 3 | SETTING_WIN_SIZE = 'window/size' 4 | SETTING_WIN_POSE = 'window/position' 5 | SETTING_WIN_GEOMETRY = 'window/geometry' 6 | SETTING_LINE_COLOR = 'line/color' 7 | SETTING_FILL_COLOR = 'fill/color' 8 | SETTING_ADVANCE_MODE = 'advanced' 9 | SETTING_WIN_STATE = 'window/state' 10 | SETTING_SAVE_DIR = 'savedir' 11 | SETTING_LAST_OPEN_DIR = 'lastOpenDir' 12 | SETTING_TASK_MODE = 'task_mode' 13 | SETTING_LABEL_FONT_SIZE = 'det/label_font_size' 14 | COLORMAP = {0: [0, 0, 0], 1: [120, 120, 120], 2: [180, 120, 120], 3: [6, 230, 230], 4: [80, 50, 50], 5: [4, 200, 3], 15 | 6: [120, 120, 80], 7: [140, 140, 140], 8: [204, 5, 255], 9: [230, 230, 230], 10: [4, 250, 7], 16 | 11: [224, 5, 255], 12: [235, 255, 7], 13: [150, 5, 61], 14: [120, 120, 70], 15: [8, 255, 51], 17 | 16: [255, 6, 82], 17: [143, 255, 140], 18: [204, 255, 4], 19: [255, 51, 7], 20: [204, 70, 3], 18 | 21: [0, 102, 200], 22: [61, 230, 250], 23: [255, 6, 51], 24: [11, 102, 255], 25: [255, 7, 71], 19 | 26: [255, 9, 224], 27: [9, 7, 230], 28: [220, 220, 220], 29: [255, 9, 92], 30: [112, 9, 255], 20 | 31: [8, 255, 214], 32: [7, 255, 224], 33: [255, 184, 6], 34: [10, 255, 71], 35: [255, 41, 10], 21 | 36: [7, 255, 255], 37: [224, 255, 8], 38: [102, 8, 255], 39: [255, 61, 6], 40: [255, 194, 7], 22 | 41: [255, 122, 8], 42: [0, 255, 20], 43: [255, 8, 41], 44: [255, 5, 153], 45: [6, 51, 255], 23 | 46: [235, 12, 255], 47: [160, 150, 20], 48: [0, 163, 255], 49: [140, 140, 140], 50: [250, 10, 15], 24 | 51: [20, 255, 0], 52: [31, 255, 0], 53: [255, 31, 0], 54: [255, 224, 0], 55: [153, 255, 0], 25 | 56: [0, 0, 255], 57: [255, 71, 0], 58: [0, 235, 255], 59: [0, 173, 255], 60: [31, 0, 255], 26 | 61: [11, 200, 200], 62: [255, 82, 0], 63: [0, 255, 245], 64: [0, 61, 255], 65: [0, 255, 112], 27 | 66: [0, 255, 133], 67: [255, 0, 0], 68: [255, 163, 0], 69: [255, 102, 0], 70: [194, 255, 0], 28 | 71: [0, 143, 255], 72: [51, 255, 0], 73: [0, 82, 255], 74: [0, 255, 41], 75: [0, 255, 173], 29 | 76: [10, 0, 255], 77: [173, 255, 0], 78: [0, 255, 153], 79: [255, 92, 0], 80: [255, 0, 255], 30 | 81: [255, 0, 245], 82: [255, 0, 102], 83: [255, 173, 0], 84: [255, 0, 20], 85: [255, 184, 184], 31 | 86: [0, 31, 255], 87: [0, 255, 61], 88: [0, 71, 255], 89: [255, 0, 204], 90: [0, 255, 194], 32 | 91: [0, 255, 82], 92: [0, 10, 255], 93: [0, 112, 255], 94: [51, 0, 255], 95: [0, 194, 255], 33 | 96: [0, 122, 255], 97: [0, 255, 163], 98: [255, 153, 0], 99: [0, 255, 10], 100: [255, 112, 0], 34 | 101: [143, 255, 0], 102: [82, 0, 255], 103: [163, 255, 0], 104: [255, 235, 0], 105: [8, 184, 170], 35 | 106: [133, 0, 255], 107: [0, 255, 92], 108: [184, 0, 255], 109: [255, 0, 31], 110: [0, 184, 255], 36 | 111: [0, 214, 255], 112: [255, 0, 112], 113: [92, 255, 0], 114: [0, 224, 255], 115: [112, 224, 255], 37 | 116: [70, 184, 160], 117: [163, 0, 255], 118: [153, 0, 255], 119: [71, 255, 0], 120: [255, 0, 163], 38 | 121: [255, 204, 0], 122: [255, 0, 143], 123: [0, 255, 235], 124: [133, 255, 0], 125: [255, 0, 235], 39 | 126: [245, 0, 255], 127: [255, 0, 122], 128: [255, 245, 0], 129: [10, 190, 212], 130: [214, 255, 0], 40 | 131: [0, 204, 255], 132: [20, 0, 255], 133: [255, 255, 0], 134: [0, 153, 255], 135: [0, 41, 255], 41 | 136: [0, 255, 204], 137: [41, 0, 255], 138: [41, 255, 0], 139: [173, 0, 255], 140: [0, 245, 255], 42 | 141: [71, 0, 255], 142: [122, 0, 255], 143: [0, 255, 184], 144: [0, 92, 255], 145: [184, 255, 0], 43 | 146: [0, 133, 255], 147: [255, 214, 0], 148: [25, 194, 194], 149: [102, 255, 0], 150: [92, 0, 255], 44 | 255: [255, 255, 255]} -------------------------------------------------------------------------------- /libs/labelDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt4.QtGui import * 2 | from PyQt4.QtCore import * 3 | 4 | from lib import newIcon, labelValidator 5 | 6 | BB = QDialogButtonBox 7 | 8 | 9 | class SubListWidget(QDialog): 10 | 11 | def __init__(self, parent=None, listItem=None): 12 | self.setWindowTitle('select a label') 13 | self.select_text = None 14 | super(SubListWidget, self).__init__(parent) 15 | self.listwidget = QListWidget(self) 16 | layout = QVBoxLayout() 17 | if listItem is not None and len(listItem) > 0: 18 | for item in listItem: 19 | self.listwidget.addItem(item) 20 | layout.addWidget(self.listwidget) 21 | self.setLayout(layout) 22 | self.listwidget.itemDoubleClicked.connect(self.listItemDoubleClicked) 23 | self.move(QCursor.pos()) 24 | 25 | def get_select_item(self): 26 | return self.select_text if self.exec_() else None 27 | 28 | def listItemDoubleClicked(self, tQListWidgetItem): 29 | text = tQListWidgetItem.text().trimmed() 30 | self.select_text = text 31 | print text 32 | if text is not None: 33 | self.accept() 34 | 35 | 36 | class LabelDialog(QDialog): 37 | 38 | def __init__( 39 | self, 40 | text="Enter object label", 41 | parent=None, 42 | listItem=None, 43 | sub_label_items=None, 44 | label_fre_dic=None): 45 | super(LabelDialog, self).__init__(parent) 46 | self.edit = QLineEdit() 47 | self.edit.setText(text) 48 | self.edit.setValidator(labelValidator()) 49 | self.edit.editingFinished.connect(self.postProcess) 50 | layout = QVBoxLayout() 51 | self.label_fre_dic = label_fre_dic 52 | layout.addWidget(self.edit) 53 | self.buttonBox = bb = BB(BB.Ok | BB.Cancel, Qt.Horizontal, self) 54 | bb.button(BB.Ok).setIcon(newIcon('done')) 55 | bb.button(BB.Cancel).setIcon(newIcon('undo')) 56 | bb.accepted.connect(self.validate) 57 | bb.rejected.connect(self.reject) 58 | layout.addWidget(bb) 59 | if sub_label_items: 60 | self.sub_labels_dic = sub_label_items 61 | self.sublistwidget = SubListWidget() 62 | if self.sub_labels_dic.keys() is not None and len(self.sub_labels_dic.keys()) > 0: 63 | self.listWidget = QListWidget(self) 64 | keys = sorted(self.sub_labels_dic.keys()) 65 | for item in keys: 66 | self.listWidget.addItem(item) 67 | self.listWidget.itemClicked.connect(self.listItemClicked) 68 | layout.addWidget(self.listWidget) 69 | elif listItem: 70 | sorted_labels = [] 71 | if self.label_fre_dic: 72 | print label_fre_dic 73 | sorted_labels = sorted( 74 | self.label_fre_dic, 75 | key=self.label_fre_dic.get, 76 | reverse=True) 77 | if listItem is not None and len(listItem) > 0: 78 | self.listWidget = QListWidget(self) 79 | for item in sorted_labels: 80 | self.listWidget.addItem(item) 81 | self.listWidget.itemDoubleClicked.connect( 82 | self.listItemDoubleClicked) 83 | layout.addWidget(self.listWidget) 84 | self.setLayout(layout) 85 | 86 | def validate(self): 87 | if self.edit.text().trimmed(): 88 | self.accept() 89 | 90 | def postProcess(self): 91 | self.edit.setText(self.edit.text().trimmed()) 92 | 93 | def popUp(self, text='', move=True): 94 | self.edit.setText(text) 95 | self.edit.setSelection(0, len(text)) 96 | self.edit.setFocus(Qt.PopupFocusReason) 97 | if move: 98 | self.move(QCursor.pos()) 99 | return self.edit.text() if self.exec_() else None 100 | 101 | def sublistwidgetclicked(self, tQListWidgetItem): 102 | print tQListWidgetItem.text().trimmed() 103 | print 'doubleclicked' 104 | 105 | def listItemDoubleClicked(self, tQListWidgetItem): 106 | text = tQListWidgetItem.text().trimmed() 107 | self.edit.setText(text) 108 | self.validate() 109 | 110 | def listItemClicked(self, tQListWidgetItem): 111 | self.sublistwidget.close() 112 | labels = self.sub_labels_dic[str(tQListWidgetItem.text().trimmed())] 113 | label_dic = {} 114 | for label in labels: 115 | if label in self.label_fre_dic: 116 | label_dic[label] = self.label_fre_dic[label] 117 | else: 118 | label_dic[label] = 0 119 | sorted_labels = sorted(label_dic, key=label_dic.get, reverse=True) 120 | self.sublistwidget = SubListWidget(listItem=sorted_labels, parent=self) 121 | self.sublistwidget.show() 122 | self.edit.setText(self.sublistwidget.get_select_item()) 123 | self.validate() 124 | -------------------------------------------------------------------------------- /libs/labelFile.py: -------------------------------------------------------------------------------- 1 | import os.path 2 | import sys 3 | from pascalVocIO import PascalVocWriter 4 | from base64 import b64encode, b64decode 5 | 6 | 7 | class LabelFileError(Exception): 8 | pass 9 | 10 | 11 | class LabelFile(object): 12 | # It might be changed as window creates 13 | suffix = '.lif' 14 | 15 | def __init__(self, filename=None): 16 | self.shapes = () 17 | self.imagePath = None 18 | self.imageData = None 19 | if filename is not None: 20 | self.load(filename) 21 | 22 | def savePascalVocFormat( 23 | self, 24 | savefilename, 25 | image_size, 26 | shapes, 27 | imagePath=None, 28 | databaseSrc=None, 29 | shape_type_='RECT'): 30 | imgFolderPath = os.path.dirname(imagePath) 31 | imgFolderName = os.path.split(imgFolderPath)[-1] 32 | imgFileName = os.path.basename(imagePath) 33 | imgFileNameWithoutExt = os.path.splitext(imgFileName)[0] 34 | 35 | #img = cv2.imread(imagePath) 36 | writer = PascalVocWriter( 37 | imgFolderName, 38 | imgFileNameWithoutExt, 39 | image_size, 40 | localImgPath=imagePath, 41 | shape_type=shape_type_) 42 | bSave = False 43 | for shape in shapes: 44 | points = shape['points'] 45 | label = shape['label'] 46 | if shape['shape_type'] == 0: 47 | print 'add rects' 48 | bndbox = LabelFile.convertPoints2BndBox(points) 49 | writer.addBndBox( 50 | bndbox[0], 51 | bndbox[1], 52 | bndbox[2], 53 | bndbox[3], 54 | label) 55 | if shape['shape_type'] == 1: 56 | print 'add polygons' 57 | writer.addPolygon(points, label,instance_id=shape['instance_id']) 58 | 59 | bSave = True 60 | 61 | if bSave: 62 | writer.save(targetFile=savefilename) 63 | return 64 | 65 | @staticmethod 66 | def isLabelFile(filename): 67 | fileSuffix = os.path.splitext(filename)[1].lower() 68 | return fileSuffix == LabelFile.suffix 69 | 70 | @staticmethod 71 | def convertPoints2BndBox(points): 72 | xmin = sys.maxsize 73 | ymin = sys.maxsize 74 | xmax = -sys.maxsize 75 | ymax = -sys.maxsize 76 | for p in points: 77 | x = p[0] 78 | y = p[1] 79 | xmin = min(x, xmin) 80 | ymin = min(y, ymin) 81 | xmax = max(x, xmax) 82 | ymax = max(y, ymax) 83 | 84 | # Martin Kersner, 2015/11/12 85 | # 0-valued coordinates of BB caused an error while 86 | # training faster-rcnn object detector. 87 | if (xmin < 1): 88 | xmin = 1 89 | 90 | if (ymin < 1): 91 | ymin = 1 92 | 93 | return (int(xmin), int(ymin), int(xmax), int(ymax)) 94 | -------------------------------------------------------------------------------- /libs/lib.py: -------------------------------------------------------------------------------- 1 | from math import sqrt 2 | 3 | from PyQt4.QtGui import * 4 | from PyQt4.QtCore import * 5 | 6 | 7 | def newIcon(icon): 8 | return QIcon(':/' + icon) 9 | 10 | 11 | def newButton(text, icon=None, slot=None): 12 | b = QPushButton(text) 13 | if icon is not None: 14 | b.setIcon(newIcon(icon)) 15 | if slot is not None: 16 | b.clicked.connect(slot) 17 | return b 18 | 19 | 20 | def newAction(parent, text, slot=None, shortcut=None, icon=None, 21 | tip=None, checkable=False, enabled=True): 22 | """Create a new action and assign callbacks, shortcuts, etc.""" 23 | a = QAction(text, parent) 24 | if icon is not None: 25 | a.setIcon(newIcon(icon)) 26 | if shortcut is not None: 27 | if isinstance(shortcut, (list, tuple)): 28 | a.setShortcuts(shortcut) 29 | else: 30 | a.setShortcut(shortcut) 31 | if tip is not None: 32 | a.setToolTip(tip) 33 | a.setStatusTip(tip) 34 | if slot is not None: 35 | a.triggered.connect(slot) 36 | if checkable: 37 | a.setCheckable(True) 38 | a.setEnabled(enabled) 39 | return a 40 | 41 | 42 | def addActions(widget, actions): 43 | for action in actions: 44 | if action is None: 45 | widget.addSeparator() 46 | elif isinstance(action, QMenu): 47 | widget.addMenu(action) 48 | else: 49 | widget.addAction(action) 50 | 51 | 52 | def labelValidator(): 53 | return QRegExpValidator(QRegExp(r'^[^ \t].+'), None) 54 | 55 | 56 | class struct(object): 57 | 58 | def __init__(self, **kwargs): 59 | self.__dict__.update(kwargs) 60 | 61 | 62 | def distance(p): 63 | return sqrt(p.x() * p.x() + p.y() * p.y()) 64 | 65 | 66 | def fmtShortcut(text): 67 | mod, key = text.split('+', 1) 68 | return '%s+%s' % (mod, key) 69 | -------------------------------------------------------------------------------- /libs/pascalVocIO.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from xml.etree import ElementTree 3 | from xml.etree.ElementTree import Element, SubElement 4 | from xml.dom import minidom 5 | from lxml import etree 6 | 7 | 8 | class PascalVocWriter: 9 | 10 | def __init__( 11 | self, 12 | foldername, 13 | filename, 14 | imgSize, 15 | databaseSrc='Unknown', 16 | localImgPath=None, 17 | shape_type=None): 18 | self.foldername = foldername 19 | self.filename = filename 20 | self.databaseSrc = databaseSrc 21 | self.imgSize = imgSize 22 | self.boxlist = [] 23 | self.localImgPath = localImgPath 24 | self.shape_type = shape_type 25 | 26 | def prettify(self, elem): 27 | """ 28 | Return a pretty-printed XML string for the Element. 29 | """ 30 | rough_string = ElementTree.tostring(elem, 'utf8') 31 | root = etree.fromstring(rough_string) 32 | return etree.tostring(root,pretty_print=True) 33 | 34 | def genXML(self): 35 | """ 36 | Return XML root 37 | """ 38 | # Check conditions 39 | ''' 40 | if self.filename is None or \ 41 | self.foldername is None or \ 42 | self.imgSize is None or \ 43 | len(self.boxlist) <= 0: 44 | ''' 45 | if self.filename is None or \ 46 | len(self.boxlist) <= 0: 47 | return None 48 | 49 | top = Element('annotation') 50 | folder = SubElement(top, 'folder') 51 | folder.text = self.foldername 52 | 53 | filename = SubElement(top, 'filename') 54 | filename.text = self.filename 55 | 56 | localImgPath = SubElement(top, 'path') 57 | self.localImgPath = self.localImgPath.split('/')[-1] 58 | localImgPath.text = self.localImgPath 59 | 60 | source = SubElement(top, 'source') 61 | database = SubElement(source, 'database') 62 | database.text = self.databaseSrc 63 | 64 | if self.imgSize: 65 | size_part = SubElement(top, 'size') 66 | width = SubElement(size_part, 'width') 67 | height = SubElement(size_part, 'height') 68 | depth = SubElement(size_part, 'depth') 69 | width.text = str(self.imgSize[1]) 70 | height.text = str(self.imgSize[0]) 71 | if len(self.imgSize) == 3: 72 | depth.text = str(self.imgSize[2]) 73 | else: 74 | depth.text = '1' 75 | 76 | segmented = SubElement(top, 'segmented') 77 | segmented.text = '0' 78 | shape_type = SubElement(top, 'shape_type') 79 | shape_type.text = self.shape_type 80 | return top 81 | 82 | def addBndBox(self, xmin, ymin, xmax, ymax, name): 83 | bndbox = {'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax} 84 | bndbox['name'] = name 85 | self.boxlist.append(bndbox) 86 | 87 | def addPolygon(self, shape, name,instance_id): 88 | polygon = {} 89 | i = 0 90 | for point in shape: 91 | polygon[i] = point 92 | i = i + 1 93 | polygon['name'] = name 94 | polygon['point_num'] = str(len(shape)) 95 | polygon['instance_id'] = instance_id 96 | self.boxlist.append(polygon) 97 | 98 | def appendObjects(self, top): 99 | for each_object in self.boxlist: 100 | print(each_object) 101 | object_item = SubElement(top, 'object') 102 | if each_object['name']: 103 | name = SubElement(object_item, 'name') 104 | name.text = unicode(each_object['name']) 105 | pose = SubElement(object_item, 'pose') 106 | pose.text = "Unspecified" 107 | if 'instance_id' in each_object.keys(): 108 | instance_id = SubElement(object_item,'instance_id') 109 | instance_id.text = str(each_object['instance_id']) 110 | truncated = SubElement(object_item, 'truncated') 111 | truncated.text = "0" 112 | difficult = SubElement(object_item, 'difficult') 113 | difficult.text = "0" 114 | if self.shape_type == 'RECT': 115 | bndbox = SubElement(object_item, 'bndbox') 116 | xmin = SubElement(bndbox, 'xmin') 117 | xmin.text = str(each_object['xmin']) 118 | ymin = SubElement(bndbox, 'ymin') 119 | ymin.text = str(each_object['ymin']) 120 | xmax = SubElement(bndbox, 'xmax') 121 | xmax.text = str(each_object['xmax']) 122 | ymax = SubElement(bndbox, 'ymax') 123 | ymax.text = str(each_object['ymax']) 124 | elif self.shape_type == 'POLYGON': 125 | polygon = SubElement(object_item, 'polygon') 126 | for i in xrange(int(each_object['point_num'])): 127 | point = SubElement(polygon, 'point' + str(i)) 128 | point.text = str( 129 | int(each_object[i][0])) + ',' + str(int(each_object[i][1])) 130 | print i, point.text 131 | 132 | def save(self, targetFile=None): 133 | root = self.genXML() 134 | self.appendObjects(root) 135 | out_file = None 136 | if targetFile is None: 137 | out_file = open(self.filename + '.xml', 'w') 138 | else: 139 | out_file = open(targetFile, 'w') 140 | out_file.write(self.prettify(root)) 141 | # out_file.write(root) 142 | out_file.close() 143 | 144 | 145 | class PascalVocReader: 146 | 147 | def __init__(self, filepath): 148 | # shapes type: 149 | ## [labbel, [(x1,y1), (x2,y2), (x3,y3), (x4,y4)], color, color] 150 | self.shapes = [] 151 | self.filepath = filepath 152 | self.shape_type = None 153 | self.image_size = [] 154 | self.parseXML() 155 | 156 | def getShapes(self): 157 | return self.shapes 158 | 159 | def getShapeType(self): 160 | return self.shape_type 161 | 162 | def addPolygonShape(self,label,points,instance_id = 0): 163 | points = [(point[0],point[1]) for point in points] 164 | self.shapes.append((label,points,None,None,1,instance_id)) 165 | def get_img_size(self): 166 | if self.image_size: 167 | return self.image_size 168 | def addShape(self, label, rect,instance_id = 0): 169 | xmin = rect[0] 170 | ymin = rect[1] 171 | xmax = rect[2] 172 | ymax = rect[3] 173 | points = [(xmin, ymin), (xmin, ymax), (xmax, ymax), (xmax, ymin)] 174 | self.shapes.append((label, points, None, None, 0,instance_id)) 175 | 176 | def parseXML(self): 177 | assert self.filepath.endswith('.xml'), "Unsupport file format" 178 | parser = etree.XMLParser(encoding='utf-8') 179 | xmltree = ElementTree.parse(self.filepath,parser=parser).getroot() 180 | filename = xmltree.find('filename').text 181 | if xmltree.find('shape_type') is not None: 182 | self.shape_type = xmltree.find('shape_type').text 183 | else: 184 | self.shape_type = 'RECT' 185 | self.image_size.append(int(xmltree.find('size').find('width').text)) 186 | self.image_size.append(int(xmltree.find('size').find('height').text)) 187 | if self.shape_type == 'RECT': 188 | for object_iter in xmltree.findall('object'): 189 | rects = [] 190 | bndbox = object_iter.find("bndbox") 191 | rects.append([int(it.text) for it in bndbox]) 192 | label = object_iter.find('name').text 193 | for rect in rects: 194 | self.addShape(label, rect) 195 | return True 196 | elif self.shape_type == 'POLYGON': 197 | for object_iter in xmltree.findall('object'): 198 | points = [] 199 | polygons = object_iter.find("polygon") 200 | label = object_iter.find('name').text 201 | for point in polygons: 202 | point = point.text.split(',') 203 | point = [int(dot) for dot in point] 204 | points.append(point) 205 | if object_iter.find('instance_id') is not None: 206 | instance_id = int(object_iter.find('instance_id').text) 207 | self.addPolygonShape(label, points,instance_id) 208 | else: 209 | print 'unsupportable shape type' 210 | 211 | 212 | # tempParseReader = PascalVocReader('test.xml') 213 | # print tempParseReader.getShapes() 214 | """ 215 | # Test 216 | tmp = PascalVocWriter('temp','test', (10,20,3)) 217 | tmp.addBndBox(10,10,20,30,'chair') 218 | tmp.addBndBox(1,1,600,600,'car') 219 | tmp.save() 220 | """ 221 | -------------------------------------------------------------------------------- /libs/remoteDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui, QtCore 2 | import socket 3 | import re 4 | 5 | 6 | class SetRemoteDialog(QtGui.QDialog): 7 | remote_mode = True 8 | remote_url = "" 9 | dowload_thead_num = 4 10 | server_image_list = None 11 | 12 | def __init__(self, parent=None): 13 | QtGui.QDialog.__init__(self, parent) 14 | self.resize(320, 100) 15 | self.setWindowTitle('set remote db') 16 | self.remote_cb = QtGui.QCheckBox("use remote database") 17 | if self.__class__.remote_mode: 18 | self.remote_cb.toggle() 19 | self.remote_cb.stateChanged.connect(self.set_remote_mode) 20 | grid = QtGui.QGridLayout() 21 | grid.addWidget(self.remote_cb, 0, 0, 1, 1) 22 | grid.addWidget( 23 | QtGui.QLabel( 24 | u'dowload image thread num', 25 | parent=self), 26 | 1, 27 | 0, 28 | 1, 29 | 1) 30 | self.thread_num = QtGui.QSpinBox() 31 | self.thread_num.setRange(1, 10) 32 | self.thread_num.setValue(self.__class__.dowload_thead_num) 33 | self.thread_num.valueChanged.connect(self.set_thread_num) 34 | grid.addWidget(self.thread_num, 1, 1, 1, 1) 35 | grid.addWidget( 36 | QtGui.QLabel( 37 | u'remote db url[123.57.438.245/]', 38 | parent=self), 39 | 2, 40 | 0, 41 | 1, 42 | 1) 43 | self.remote_url_line = QtGui.QLineEdit(parent=self) 44 | if self.__class__.remote_url: 45 | self.remote_url_line.setText(self.__class__.remote_url) 46 | grid.addWidget(self.remote_url_line, 2, 1, 1, 1) 47 | grid.addWidget( 48 | QtGui.QLabel( 49 | u'remote image list', 50 | parent=self), 51 | 3, 52 | 0, 53 | 1, 54 | 1) 55 | self.server_image_list = QtGui.QLineEdit(parent=self) 56 | if self.__class__.server_image_list: 57 | self.server_image_list.setText(self.__class__.server_image_list) 58 | grid.addWidget(self.server_image_list, 3, 1, 1, 1) 59 | buttonBox = QtGui.QDialogButtonBox(parent=self) 60 | buttonBox.setOrientation(QtCore.Qt.Horizontal) 61 | buttonBox.setStandardButtons( 62 | QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) 63 | buttonBox.accepted.connect(self.accept) 64 | buttonBox.rejected.connect(self.reject) 65 | layout = QtGui.QVBoxLayout() 66 | layout.addLayout(grid) 67 | spacerItem = QtGui.QSpacerItem( 68 | 20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) 69 | layout.addItem(spacerItem) 70 | layout.addWidget(buttonBox) 71 | self.setLayout(layout) 72 | 73 | def test_remote_url(self, url, port): 74 | try: 75 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 76 | sock.settimeout(5) 77 | sock.connect((url, port)) 78 | return True 79 | except socket.error as e: 80 | return False 81 | finally: 82 | sock.close() 83 | 84 | def set_remote_mode(self, state): 85 | if state == QtCore.Qt.Checked: 86 | self.__class__.remote_mode = True 87 | else: 88 | self.__class__.remote_mode = False 89 | 90 | def set_thread_num(self, num): 91 | self.__class__.dowload_thead_num = num 92 | 93 | def get_thread_num(self): 94 | return self.__class__.dowload_thead_num 95 | 96 | def is_in_remote_mode(self): 97 | return self.__class__.remote_mode 98 | 99 | def get_server_image_list(self): 100 | if self.server_image_list is not None: 101 | return self.server_image_list.text() 102 | else: 103 | QtGui.QMessageBox.about( 104 | self, 105 | "server image list!", 106 | "the server image list is None!") 107 | 108 | def get_remote_url(self): 109 | origin_url = self.remote_url_line.text() 110 | if re.match(r'\w.+$', origin_url): 111 | if self.test_remote_url(origin_url.split('/')[0], 80): 112 | self.__class__.remote_url = origin_url 113 | return self.__class__.remote_url 114 | else: 115 | QtGui.QMessageBox.about( 116 | self, "server connect error!", "can not connect the server") 117 | 118 | else: 119 | QtGui.QMessageBox.about( 120 | self, 121 | "url format error!", 122 | "the url is not in the correct format \n such as 1.1.1.1/sf/") 123 | -------------------------------------------------------------------------------- /libs/saveMaskImage.py: -------------------------------------------------------------------------------- 1 | ''' 2 | this file define a class to save the result of the mask of parse 3 | the mask will be save as a gray image using different color to represent different 4 | object 5 | ''' 6 | import numpy as np 7 | import logging 8 | from PIL import Image,ImageDraw 9 | 10 | 11 | class label_mask_writer: 12 | 13 | def __init__( 14 | self, 15 | label_num_dict, 16 | save_file_path, 17 | image_height, 18 | image_width): 19 | self.label_num_dict = label_num_dict 20 | 21 | self.save_file_path = save_file_path 22 | self.image_height = image_height 23 | self.image_width = image_width 24 | self.labels = [] 25 | self.shapes = [] 26 | 27 | def save_mask_image(self, shapes): 28 | for shape in shapes: 29 | self.add_mask_label(shape['label']) 30 | self.add_shape_points(shape['points']) 31 | image = self.get_mask_image() 32 | image.save(self.save_file_path, 'PNG') 33 | 34 | def add_mask_label(self, label): 35 | self.labels.append(label) 36 | 37 | def add_shape_points(self, shape_points): 38 | self.shapes.append(shape_points) 39 | 40 | def get_mask_image(self): 41 | ''' 42 | convert label and shapes to gray image mask 43 | :return: gray image mask 44 | ''' 45 | assert len(self.labels) == len(self.shapes) 46 | mask_bg = Image.new('L',(self.image_width,self.image_height)) 47 | mask_draw = ImageDraw.Draw(mask_bg) 48 | if self.labels: 49 | index = 0 50 | for label in self.labels: 51 | color = self.label_num_dict[label] 52 | vertex = self.shapes[index] 53 | mask_draw.polygon(vertex,color) 54 | index += 1 55 | else: 56 | logging.error('there are no shapes to save !') 57 | return mask_bg 58 | -------------------------------------------------------------------------------- /libs/settingDialog.py: -------------------------------------------------------------------------------- 1 | from PyQt4 import QtGui, QtCore 2 | import socket 3 | import re 4 | 5 | 6 | class SettingDialog(QtGui.QDialog): 7 | enable_color_map = True 8 | label_font_size = 10 9 | task_mode = 0 #0=det, 1=seg, 2=cls 10 | instance_seg_flag = False 11 | 12 | 13 | def __init__(self, parent,config): 14 | QtGui.QDialog.__init__(self, parent) 15 | self.resize(320, 240) 16 | self.__class__.task_mode = config['task_mode'] 17 | self.__class__.label_font_size = config['label_font_size'] 18 | self.init_UI() 19 | def createModeGroup(self): 20 | ''' 21 | set the trask mode setting group 22 | :return: mode group 23 | ''' 24 | self.modegroupBox = QtGui.QGroupBox("& Task Mode") 25 | self.modegroupBox.setCheckable(True) 26 | self.modegroupBox.setChecked(True) 27 | self.CLS_mode_rb = QtGui.QRadioButton("CLS Mode") 28 | self.CLS_mode_rb.clicked.connect(self.CLS_model_selected) 29 | self.DET_mode_rb = QtGui.QRadioButton("DET Mode") 30 | self.DET_mode_rb.clicked.connect(self.DET_model_selected) 31 | self.SEG_mode_rb = QtGui.QRadioButton("SEG Mode") 32 | self.SEG_mode_rb.clicked.connect(self.SEG_model_selected) 33 | self.BRU_mode_rb = QtGui.QRadioButton("BRU Mode") 34 | self.BRU_mode_rb.clicked.connect(self.BRU_model_selected) 35 | 36 | vbox = QtGui.QVBoxLayout() 37 | vbox.addWidget(self.CLS_mode_rb) 38 | vbox.addWidget(self.DET_mode_rb) 39 | vbox.addWidget(self.SEG_mode_rb) 40 | vbox.addWidget(self.BRU_mode_rb) 41 | vbox.addStretch(True) 42 | self.modegroupBox.setLayout(vbox) 43 | return self.modegroupBox 44 | 45 | def createDEToptGroup(self): 46 | self.detgroupBox = QtGui.QGroupBox("& DET options") 47 | self.enable_show_label_cb = QtGui.QCheckBox('enable show label name') 48 | 49 | 50 | self.label_font_size_sl = QtGui.QSlider(QtCore.Qt.Horizontal) 51 | self.label_font_size_sl.setRange(5,50) 52 | self.label_font_size_sp = QtGui.QSpinBox() 53 | self.label_font_size_sp.setRange(5,50) 54 | QtCore.QObject.connect(self.label_font_size_sl, QtCore.SIGNAL("valueChanged(int)"), 55 | 56 | self.label_font_size_sp, QtCore.SLOT("setValue(int)")) 57 | self.label_font_size_sl.valueChanged.connect(self.change_label_font_size) 58 | self.label_font_size_sl.setValue(self.__class__.label_font_size) 59 | vbox = QtGui.QVBoxLayout() 60 | vbox.addWidget(self.enable_show_label_cb) 61 | vbox.addWidget(QtGui.QLabel('label font size')) 62 | vbox.addWidget(self.label_font_size_sl) 63 | vbox.addWidget(self.label_font_size_sp) 64 | vbox.addStretch() 65 | self.detgroupBox.setLayout(vbox) 66 | return self.detgroupBox 67 | 68 | def createCLSoptGroup(self): 69 | self.clsgroupBox = QtGui.QGroupBox("& CLS options") 70 | #self.single_label_rb = QtGui.QRadioButton("single label") 71 | #self.multi_label_rb = QtGui.QRadioButton("multi label") 72 | vbox = QtGui.QVBoxLayout() 73 | #vbox.addWidget(self.single_label_rb) 74 | #vbox.addWidget(self.multi_label_rb) 75 | vbox.addStretch(True) 76 | self.clsgroupBox.setLayout(vbox) 77 | return self.clsgroupBox 78 | def createBRUoptGroup(self): 79 | self.brugroupBox = QtGui.QGroupBox("& Brush options") 80 | #self.single_label_rb = QtGui.QRadioButton("single label") 81 | #self.multi_label_rb = QtGui.QRadioButton("multi label") 82 | vbox = QtGui.QVBoxLayout() 83 | #vbox.addWidget(self.single_label_rb) 84 | #vbox.addWidget(self.multi_label_rb) 85 | vbox.addStretch(True) 86 | self.brugroupBox.setLayout(vbox) 87 | return self.brugroupBox 88 | 89 | def createSEGoptGroup(self): 90 | self.seggroupBox = QtGui.QGroupBox("& SEG options") 91 | self.enable_color_map_cb = QtGui.QCheckBox('enable color map') 92 | self.instance_seg_label_cb = QtGui.QCheckBox('set instance seg') 93 | self.instance_seg_label_cb.setChecked(self.__class__.instance_seg_flag) 94 | self.instance_seg_label_cb.stateChanged.connect(self.change_instance_seg_label) 95 | if self.__class__.enable_color_map: 96 | self.enable_color_map_cb.toggle() 97 | self.enable_color_map_cb.stateChanged.connect( 98 | self.change_color_enable_state) 99 | if self.__class__.enable_color_map: 100 | self.enable_color_map_cb.setChecked(True) 101 | vbox = QtGui.QVBoxLayout() 102 | vbox.addWidget(self.enable_color_map_cb) 103 | vbox.addWidget(self.instance_seg_label_cb) 104 | vbox.addStretch(True) 105 | self.seggroupBox.setLayout(vbox) 106 | return self.seggroupBox 107 | 108 | 109 | def init_UI(self): 110 | main_v_layout = QtGui.QVBoxLayout() 111 | 112 | grid = QtGui.QGridLayout() 113 | grid.addWidget(self.createModeGroup(),0,0) 114 | grid.addWidget(self.createDEToptGroup(),1,0) 115 | grid.addWidget(self.createCLSoptGroup(),2,0) 116 | grid.addWidget(self.createSEGoptGroup(),3,0) 117 | grid.addWidget(self.createBRUoptGroup(),4,0) 118 | if self.__class__.task_mode == 0: 119 | self.DET_mode_rb.setChecked(True) 120 | self.DET_model_selected() 121 | elif self.__class__.task_mode == 1: 122 | self.SEG_mode_rb.setChecked(True) 123 | self.SEG_model_selected() 124 | elif self.__class__.task_mode == 2: 125 | self.CLS_mode_rb.setChecked(True) 126 | self.CLS_model_selected() 127 | elif self.__class__.task_mode == 3: 128 | self.BRU_mode_rb.setChecked(True) 129 | self.BRU_model_selected() 130 | buttonBox = QtGui.QDialogButtonBox(parent=self) 131 | buttonBox.setOrientation(QtCore.Qt.Horizontal) 132 | buttonBox.setStandardButtons( 133 | QtGui.QDialogButtonBox.Cancel | QtGui.QDialogButtonBox.Ok) 134 | buttonBox.accepted.connect(self.accept) 135 | buttonBox.rejected.connect(self.reject) 136 | main_v_layout.addLayout(grid) 137 | spacerItem = QtGui.QSpacerItem( 138 | 20, 48, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) 139 | main_v_layout.addItem(spacerItem) 140 | main_v_layout.addWidget(buttonBox) 141 | self.setLayout(main_v_layout) 142 | 143 | def CLS_model_selected(self): 144 | self.__class__.task_mode = 2 145 | self.clsgroupBox.setDisabled(False) 146 | self.detgroupBox.setDisabled(True) 147 | self.seggroupBox.setDisabled(True) 148 | self.brugroupBox.setDisabled(True) 149 | 150 | def DET_model_selected(self): 151 | self.__class__.task_mode = 0 152 | self.detgroupBox.setDisabled(False) 153 | self.clsgroupBox.setDisabled(True) 154 | self.seggroupBox.setDisabled(True) 155 | self.brugroupBox.setDisabled(True) 156 | 157 | def SEG_model_selected(self): 158 | self.__class__.task_mode = 1 159 | self.seggroupBox.setDisabled(False) 160 | self.detgroupBox.setDisabled(True) 161 | self.clsgroupBox.setDisabled(True) 162 | self.brugroupBox.setDisabled(True) 163 | 164 | def BRU_model_selected(self): 165 | self.__class__.task_mode = 3 166 | self.brugroupBox.setDisabled(False) 167 | self.seggroupBox.setDisabled(True) 168 | self.detgroupBox.setDisabled(True) 169 | self.clsgroupBox.setDisabled(True) 170 | 171 | def change_color_enable_state(self, state): 172 | if state == QtCore.Qt.Checked: 173 | self.__class__.enable_color_map = True 174 | else: 175 | self.__class__.enable_color_map = False 176 | def change_instance_seg_label(self,state): 177 | if state == QtCore.Qt.Checked: 178 | self.__class__.instance_seg_flag = True 179 | else: 180 | self.__class__.instance_seg_flag = False 181 | def change_label_font_size(self,value): 182 | self.__class__.label_font_size = value 183 | 184 | def get_color_map_state(self): 185 | return self.__class__.enable_color_map 186 | 187 | def get_setting_state(self): 188 | if self.__class__.task_mode == 0: 189 | return {'mode': 0,'enable_color_map':self.__class__.enable_color_map,'label_font_size': self.__class__.label_font_size} 190 | 191 | elif self.__class__.task_mode == 1: 192 | return {'mode': 1,'enable_color_map':self.__class__.enable_color_map,'instance_seg_flag':self.instance_seg_flag} 193 | 194 | elif self.__class__.task_mode == 2: 195 | return {'mode': 2} 196 | elif self.__class__.task_mode == 3: 197 | return {'mode': 3} 198 | 199 | -------------------------------------------------------------------------------- /libs/shape.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding: utf-8 -*- 3 | 4 | from PyQt4.QtGui import * 5 | from PyQt4.QtCore import * 6 | 7 | from lib import distance 8 | 9 | DEFAULT_LINE_COLOR = QColor(0, 255, 0, 128) 10 | DEFAULT_FILL_COLOR = QColor(255, 0, 0, 10) 11 | DEFAULT_SELECT_LINE_COLOR = QColor(255, 255, 255) 12 | DEFAULT_SELECT_FILL_COLOR = QColor(0, 128, 255, 155) 13 | DEFAULT_VERTEX_FILL_COLOR = QColor(0, 255, 0, 255) 14 | DEFAULT_HVERTEX_FILL_COLOR = QColor(255, 0, 0) 15 | 16 | 17 | class Shape(object): 18 | P_SQUARE, P_ROUND = range(2) 19 | RECT_SHAPE, POLYGON_SHAPE = range(2) 20 | MOVE_VERTEX, NEAR_VERTEX = range(2) 21 | 22 | # The following class variables influence the drawing 23 | # of _all_ shape objects. 24 | line_color = DEFAULT_LINE_COLOR 25 | fill_color = DEFAULT_FILL_COLOR 26 | select_line_color = DEFAULT_SELECT_LINE_COLOR 27 | select_fill_color = DEFAULT_SELECT_FILL_COLOR 28 | vertex_fill_color = DEFAULT_VERTEX_FILL_COLOR 29 | hvertex_fill_color = DEFAULT_HVERTEX_FILL_COLOR 30 | point_type = P_ROUND 31 | point_size = 8 32 | scale = 1.0 33 | label_font_size = 10 34 | 35 | def __init__(self, label=None, shape_type=0, line_color=None,instance_id = 0): 36 | self.label = label 37 | self.instance_id = instance_id 38 | self.points = [] 39 | self.fill = False 40 | self.selected = False 41 | self.shape_type = self.RECT_SHAPE 42 | self.max_piont_num = 4 43 | if shape_type == 1: 44 | self.shape_type = self.POLYGON_SHAPE 45 | self.max_piont_num = 100 46 | self._highlightIndex = None 47 | self._highlightMode = self.NEAR_VERTEX 48 | self._highlightSettings = { 49 | self.NEAR_VERTEX: (4, self.P_ROUND), 50 | self.MOVE_VERTEX: (1.5, self.P_SQUARE), 51 | } 52 | 53 | self._closed = False 54 | 55 | if line_color is not None: 56 | # Override the class line_color attribute 57 | # with an object attribute. Currently this 58 | # is used for drawing the pending line a different color. 59 | self.line_color = line_color 60 | 61 | def set_shape_type(self, type): 62 | self.shape_type = type 63 | 64 | def set_instance_id(self,id): 65 | self.instance_id = id 66 | def get_shape_type(self): 67 | return self.shape_type 68 | 69 | def close(self): 70 | assert len(self.points) > 2 71 | self._closed = True 72 | print len(self.points) 73 | 74 | def isRect(self): 75 | return self.shape_type == self.RECT_SHAPE 76 | 77 | def isPolygon(self): 78 | return self.shape_type == self.POLYGON_SHAPE 79 | 80 | def reachMaxPoints(self): 81 | if len(self.points) >= self.max_piont_num: 82 | return True 83 | return False 84 | 85 | def addPoint(self, point): 86 | if self.points and point == self.points[0]: 87 | self.close() 88 | else: 89 | self.points.append(point) 90 | 91 | def popPoint(self): 92 | if self.points: 93 | return self.points.pop() 94 | return None 95 | 96 | def isClosed(self): 97 | return self._closed 98 | 99 | def setOpen(self): 100 | self._closed = False 101 | 102 | def paint(self, painter): 103 | color = self.select_line_color if self.selected else self.line_color 104 | pen = QPen(color) 105 | # Try using integer sizes for smoother drawing(?) 106 | pen.setWidth(max(1, int(round(2.0 / self.scale)))) 107 | painter.setPen(pen) 108 | painter.setFont(QFont(painter.font().family(), self.__class__.label_font_size, QFont.Bold)) 109 | 110 | line_path = QPainterPath() 111 | vrtx_path = QPainterPath() 112 | 113 | line_path.moveTo(self.points[0]) 114 | # Uncommenting the following line will draw 2 paths 115 | # for the 1st vertex, and make it non-filled, which 116 | # may be desirable. 117 | self.drawVertex(vrtx_path, 0) 118 | 119 | for i, p in enumerate(self.points): 120 | line_path.lineTo(p) 121 | self.drawVertex(vrtx_path, i) 122 | if self.isClosed(): 123 | line_path.lineTo(self.points[0]) 124 | painter.drawPath(line_path) 125 | painter.drawPath(vrtx_path) 126 | painter.fillPath(vrtx_path, self.vertex_fill_color) 127 | if self.fill: 128 | color = self.select_fill_color if self.selected else self.fill_color 129 | if isinstance(color,list): 130 | color=QColor(*color) 131 | painter.fillPath(line_path, color) 132 | if self.label is not None and self.shape_type == self.RECT_SHAPE: 133 | #painter.setBrush(QColor(255,255,255)) 134 | top_left_point = QPointF(self.points[0].x(),self.points[0].y()-int(self.__class__.label_font_size*1.15)) 135 | label_bg = QRectF(top_left_point,self.points[3]) 136 | painter.drawRect(label_bg) 137 | painter.drawText(self.points[0], self.label) 138 | 139 | def drawVertex(self, path, i): 140 | d = self.point_size / self.scale 141 | shape = self.point_type 142 | point = self.points[i] 143 | if i == self._highlightIndex: 144 | size, shape = self._highlightSettings[self._highlightMode] 145 | d *= size 146 | if self._highlightIndex is not None: 147 | self.vertex_fill_color = self.hvertex_fill_color 148 | else: 149 | self.vertex_fill_color = Shape.vertex_fill_color 150 | if shape == self.P_SQUARE: 151 | path.addRect(point.x() - d / 2, point.y() - d / 2, d, d) 152 | elif shape == self.P_ROUND: 153 | path.addEllipse(point, d / 2.0, d / 2.0) 154 | else: 155 | assert False, "unsupported vertex shape" 156 | 157 | def nearestVertex(self, point, epsilon): 158 | for i, p in enumerate(self.points): 159 | if distance(p - point) <= epsilon: 160 | return i 161 | return None 162 | 163 | def containsPoint(self, point): 164 | return self.makePath().contains(point) 165 | 166 | def makePath(self): 167 | path = QPainterPath(self.points[0]) 168 | for p in self.points[1:]: 169 | path.lineTo(p) 170 | return path 171 | 172 | def boundingRect(self): 173 | return self.makePath().boundingRect() 174 | 175 | def moveBy(self, offset): 176 | self.points = [p + offset for p in self.points] 177 | 178 | def moveVertexBy(self, i, offset): 179 | self.points[i] = self.points[i] + offset 180 | 181 | def highlightVertex(self, i, action): 182 | self._highlightIndex = i 183 | self._highlightMode = action 184 | 185 | def highlightClear(self): 186 | self._highlightIndex = None 187 | 188 | def copy(self): 189 | shape = Shape(self.label, self.shape_type, self.line_color) 190 | shape.points = [p for p in self.points] 191 | shape.fill = self.fill 192 | shape.selected = self.selected 193 | shape._closed = self._closed 194 | if self.line_color != shape.line_color: 195 | shape.line_color = self.line_color 196 | if self.fill_color != shape.fill_color: 197 | shape.fill_color = self.fill_color 198 | return shape 199 | 200 | def __len__(self): 201 | return len(self.points) 202 | 203 | def __getitem__(self, key): 204 | return self.points[key] 205 | 206 | def __setitem__(self, key, value): 207 | self.points[key] = value 208 | -------------------------------------------------------------------------------- /libs/toolBar.py: -------------------------------------------------------------------------------- 1 | from PyQt4.QtGui import * 2 | from PyQt4.QtCore import * 3 | 4 | 5 | class ToolBar(QToolBar): 6 | 7 | def __init__(self, title): 8 | super(ToolBar, self).__init__(title) 9 | layout = self.layout() 10 | m = (0, 0, 0, 0) 11 | layout.setSpacing(0) 12 | layout.setContentsMargins(*m) 13 | self.setContentsMargins(*m) 14 | self.setWindowFlags(self.windowFlags() | Qt.FramelessWindowHint) 15 | 16 | def addAction(self, action): 17 | if isinstance(action, QWidgetAction): 18 | return super(ToolBar, self).addAction(action) 19 | btn = ToolButton() 20 | btn.setDefaultAction(action) 21 | btn.setToolButtonStyle(self.toolButtonStyle()) 22 | self.addWidget(btn) 23 | 24 | 25 | class ToolButton(QToolButton): 26 | """ToolBar companion class which ensures all buttons have the same size.""" 27 | minSize = (60, 60) 28 | 29 | def minimumSizeHint(self): 30 | ms = super(ToolButton, self).minimumSizeHint() 31 | w1, h1 = ms.width(), ms.height() 32 | w2, h2 = self.minSize 33 | ToolButton.minSize = max(w1, w2), max(h1, h2) 34 | return QSize(*ToolButton.minSize) 35 | -------------------------------------------------------------------------------- /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 -------------------------------------------------------------------------------- /libs/zoomWidget.py: -------------------------------------------------------------------------------- 1 | from PyQt4.QtGui import * 2 | from PyQt4.QtCore import * 3 | 4 | class ZoomWidget(QSpinBox): 5 | 6 | def __init__(self, value=100): 7 | super(ZoomWidget, self).__init__() 8 | self.setButtonSymbols(QAbstractSpinBox.NoButtons) 9 | self.setRange(1, 500) 10 | self.setSuffix(' %') 11 | self.setValue(value) 12 | self.setToolTip(u'Zoom Level') 13 | self.setStatusTip(self.toolTip()) 14 | self.setAlignment(Qt.AlignCenter) 15 | 16 | def minimumSizeHint(self): 17 | height = super(ZoomWidget, self).minimumSizeHint().height() 18 | fm = QFontMetrics(self.font()) 19 | width = fm.width(str(self.maximum())) 20 | return QSize(width, height) 21 | -------------------------------------------------------------------------------- /resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | icons/help.png 5 | icons/expert2.png 6 | icons/expert2.png 7 | icons/done.png 8 | icons/file.png 9 | icons/labels.png 10 | icons/objects.png 11 | icons/close.png 12 | icons/fit-width.png 13 | icons/fit-window.png 14 | icons/undo.png 15 | icons/eye.png 16 | icons/quit.png 17 | icons/copy.png 18 | icons/edit.png 19 | icons/open.png 20 | icons/save.png 21 | icons/save-as.png 22 | icons/color.png 23 | icons/color_line.png 24 | icons/zoom.png 25 | icons/zoom-in.png 26 | icons/zoom-out.png 27 | icons/cancel.png 28 | icons/next.png 29 | icons/prev.png 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /screenshot/bbox_label.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/screenshot/bbox_label.jpg -------------------------------------------------------------------------------- /screenshot/brush_task.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/screenshot/brush_task.jpg -------------------------------------------------------------------------------- /screenshot/cls_task.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/screenshot/cls_task.jpg -------------------------------------------------------------------------------- /screenshot/parse_label.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/screenshot/parse_label.jpg -------------------------------------------------------------------------------- /screenshot/remote_settings.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/screenshot/remote_settings.JPG -------------------------------------------------------------------------------- /screenshot/setting_panel.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/screenshot/setting_panel.jpg -------------------------------------------------------------------------------- /scrips/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lzx1413/LabelImgTool/264aa97dd74a29b45513d311c21975fa26db39dd/scrips/__init__.py -------------------------------------------------------------------------------- /scrips/generate_image.py: -------------------------------------------------------------------------------- 1 | #coding:utf-8 2 | import json 3 | import os 4 | 5 | from libs import saveMaskImage 6 | from libs.pascalVocIO import PascalVocReader 7 | 8 | 9 | def get_name_dic(file_path): 10 | with open(file_path) as infile: 11 | label_num_dic = json.load(infile) 12 | return label_num_dic 13 | ''' 14 | items = infile.readlines() 15 | index = 0 16 | for item in items: 17 | item = item.strip().split(' ') 18 | for it in item: 19 | label_num_dic[it] = index 20 | index +=1 21 | ''' 22 | 23 | def get_image(filename,label_num_dic = None): 24 | tVocParseReader = PascalVocReader(filename) 25 | raw_shapes = tVocParseReader.getShapes() 26 | print raw_shapes 27 | def format_shape(s): 28 | label,points,aa,bb,c = s 29 | return dict(label=unicode(label), 30 | points=[(int(p[0]), int(p[1])) for p in points]) 31 | 32 | shapes = [format_shape(shape) for shape in raw_shapes] 33 | image_size = tVocParseReader.get_img_size() 34 | result_path = '/mask'+filename.split('/')[1].split('.')[0]+'.png' 35 | mask_writer = saveMaskImage.label_mask_writer(label_num_dic, result_path, image_size[0], 36 | image_size[1]) 37 | mask_writer.save_mask_image(shapes) 38 | 39 | if __name__ == '__main__': 40 | file_list = os.listdir('img_addition') 41 | print file_list 42 | label_num_dic = get_name_dic('label_num_dic.json') 43 | for file_name in file_list: 44 | get_image('img_addition/'+file_name,label_num_dic) 45 | --------------------------------------------------------------------------------