Comparar commits

...

169 Commits

Autor SHA1 Mensagem Data
Phil 550e8c106e Update release (#535) 2019-02-06 16:36:42 -08:00
Konstantin Kichinsky 5e77526ee4 Added app version to the title. Fix to copy-pasting regions with new RegionData class (#420)
* Added app version to the title. Fix to copy-pasting regions with new RegionData

* Update to testing app title

* Added new visual fitlers: contrast, brightness, saturation & blurdiff

* Revert "Added new visual fitlers: contrast, brightness, saturation & blurdiff"

This reverts commit 4ea546a44f3025b835d11109d8cce1731b2bbd26.

* Revert "Revert "Added new visual fitlers: contrast, brightness, saturation & blurdiff""

This reverts commit 247eaf893d82907b7ac90181934882f0e5570b36.

* Revert "Revert "Revert "Added new visual fitlers: contrast, brightness, saturation & blurdiff"""

This reverts commit c0eaa2ee89639550505035b03affa20590592831.
2019-01-16 08:02:54 -08:00
Igor Kasianenko 62bbdb3a8c Fix endpoint flow (#332)
* Fix bug: Stop processing frames when the last frame is reached.

* Fix bug: Continue processing frames if no objects where detected.
2019-01-16 08:02:21 -08:00
Greg Oliver 8037ef009a add installation instructions (#449) 2019-01-16 08:01:50 -08:00
Tanner Barlow 81e0f4a702 Update issue templates (#422)
- Add template for bug report and feature request
2018-12-30 11:49:13 -08:00
Darío Hereñú ec62771473 Fix typos on string #244 and subtitle on #198 (#425) 2018-12-27 07:55:44 -07:00
Konstantin Kichinsky 8e2a69efae Merge pull request #374 from kichinsky/master
Moved CanvasTools to the npm package
2018-12-26 15:42:32 +03:00
Konstantin Kichinsky a6843741b4 Fixed re-importing of old json files to generate 4 points for a rect instead of 2. 2018-12-26 15:35:36 +03:00
Konstantin Kichinsky d3d36518df Added polygon toolbar icon, updates rect icon 2018-12-26 15:35:09 +03:00
Konstantin Kichinsky 3fd153bf4f Updated CanvasTools version 2018-12-26 15:34:51 +03:00
Konstantin Kichinsky 2f2ed76422 Updated CT to fix bug when toggling background. 2018-12-21 01:30:27 +03:00
Konstantin Kichinsky 02a0a4c5be removed console.log 2018-12-21 01:10:08 +03:00
Konstantin Kichinsky c170b37f70 Fix to coordinates 2018-12-21 01:06:25 +03:00
Phil a24ba70f16 Merge pull request #406 from PIC123/feature/continue-review
Fix visited frame issue and app title
2018-12-20 16:28:37 -05:00
Konstantin Kichinsky 7a926d5584 CT Update: RegionData, Points, Polylines
Updated CanvasTools to v2.1.0.
-- added PointRegion and PolylineRegion as new visual types
-- updated code to work with RegionData from CT
-- removed threshold for rects
-- updated json format to work with new region types (and autovatically update from previous versions).
-- styling update, bux fixes
2018-12-21 00:17:18 +03:00
Konstantin Kichinsky cac350a0ce Merge remote-tracking branch 'upstream/master' 2018-12-20 22:38:10 +03:00
= 9209735a69 Fix visited frame issue and app title 2018-12-19 18:50:16 -05:00
Phil 89587a7ff3 Merge pull request #369 from Legor/master
Display a scrollbar for the tags list when not all tags are visible
2018-12-13 15:35:50 -08:00
Konstantin Kichinsky f3711b5b91 Updated reference to CT 2018-12-13 23:16:43 +03:00
Konstantin Kichinsky 2ea28612b3 Update vott-ct version to reflect new folder structure 2018-12-13 22:15:12 +03:00
Konstantin Kichinsky 8eaf547a4e Moved CanvasTools to npm package 2018-12-13 21:59:38 +03:00
Konstantin Kichinsky af64c90bba Merge pull request #7 from Microsoft/master
Merge with origin/master
2018-12-13 21:51:07 +03:00
Legor ecacfbe9cf Display a scrollbar for the tags list when not all tags are visible 2018-12-13 10:32:03 +01:00
Phil 8a3b2dcb3f Update to work with new canvastools (#364) 2018-12-12 13:57:02 -08:00
Phil 73f33c73b1 Merge pull request #351 from Legor/master
Add the version number to the help menu
2018-12-12 12:28:42 -08:00
Phil facba46f50 Merge pull request #360 from kichinsky/master
Updated Help to reflect shortcuts changes
2018-12-11 16:33:08 -08:00
Konstantin Kichinsky 5e2476a3f3 Updated CT library to accept images and videos as source in Editor.addContentSource 2018-12-12 02:02:55 +03:00
Konstantin Kichinsky 27f482ac7f Updated Help to reflect shortcuts changes 2018-12-11 23:18:15 +03:00
Phil 351146ba25 Merge pull request #341 from kichinsky/master
Switching to the new CanvasTools.Editor
2018-12-11 10:17:23 -08:00
Konstantin Kichinsky e3cf827df0 Fix in CT to avoid listening for delete/backspace and other keys while focus is on any input element 2018-12-11 21:11:41 +03:00
Konstantin Kichinsky b384a37528 Updated CT library (wrapped SVG and Canvas into Editor class).
Code clean up, including automated resizing calculation.
Removed unnecessary overlay element.
Moved filters into editor.
2018-12-11 17:26:05 +03:00
Konstantin Kichinsky c7af947adc Moved styles into a CSS file.
So, removed unnecessary video-tagging class from all the elements (impacted CanvasToools).
2018-12-11 17:24:14 +03:00
Legor ee5dc8ce57 Get the app version using pkginfo module 2018-12-10 17:22:53 +01:00
Legor 12e98e6ef4 Merge remote-tracking branch 'upstream/master' 2018-12-10 16:15:05 +01:00
Legor 029df76081 Show version number in help menu 2018-12-10 16:06:41 +01:00
Konstantin Kichinsky 273094c481 Added alt+num support to toggle auto state for tags 2018-12-06 23:40:14 +03:00
Konstantin Kichinsky ec818e1c23 Added auto-applying of tags option 2018-12-06 23:32:19 +03:00
= ec6057c4c9 Bumped to version 1.7.1 2018-12-06 11:18:50 -08:00
= 40e9798e56 Bumped to version 1.7.0 2018-12-06 11:11:31 -08:00
Phil e206244fb6 Merge pull request #283 from PIC123/feature/import-export-tfrecord
Refactor Export TFRecord Logic
2018-12-05 17:00:19 -08:00
= 569a7cfd95 Address pr comments 2018-12-05 16:56:18 -08:00
= 85a45e9be2 Fix tfrecord import/export scaling 2018-12-05 14:17:07 -08:00
Konstantin Kichinsky 140c217dbf Reduce threshold for new regions from 20 to 5 px 2018-12-04 23:14:56 +03:00
Konstantin Kichinsky 08d59f9471 Update code to use new CanvasTools.Editor instead of manually building it from components. 2018-12-04 22:52:32 +03:00
= b590101a3e Merge with master 2018-12-04 09:52:18 -08:00
Konstantin Kichinsky cf4bc28e69 Merge pull request #6 from Microsoft/master
Sync with origin
2018-12-03 22:24:30 +03:00
Konstantin Kichinsky feef3c01eb Merge pull request #5 from kichinsky/dev
Merge dev to master
2018-12-03 22:21:32 +03:00
Elizabeth Halper a4e187e18d Merge pull request #336 from elizabethhalper/bug/stop-propogation
Bug/stop propogation
2018-12-03 10:27:15 -08:00
Elizabeth Halper 7debab9db5 disable backspace with focused textbox 2018-12-03 10:13:44 -08:00
= 0bedec3571 Merge with master 2018-12-03 09:39:51 -08:00
Elizabeth Halper 3cd6755121 html input conditional 2018-12-03 09:10:08 -08:00
Elizabeth Halper 4747a9885c merge current master 2018-12-03 09:05:30 -08:00
= e4b10b5349 Fix merge with master 2018-12-03 00:08:34 -08:00
Phil 1b78605934 Merge pull request #333 from PIC123/patch/reorder-tags-fix
Fix reorder and copy bugs
2018-12-03 00:01:35 -08:00
= cbce7a4b03 Merge with master and fix copy bug 2018-12-02 21:31:19 -08:00
= 8604d63442 Merge branch 'master' of https://github.com/Microsoft/VoTT into patch/reorder-tags-fix 2018-12-02 21:29:15 -08:00
Phil 9e68d58812 Merge pull request #338 from kichinsky/dev
CT Update: "none" and "template" selection modes update and bug fixes
2018-12-02 21:27:11 -08:00
= 5caad8d5f0 Address pr comments 2018-12-02 21:04:49 -08:00
Konstantin Kichinsky 22e0045746 Added mouse wheel suppor to template selection mode 2018-12-03 01:13:55 +03:00
Konstantin Kichinsky 27542799b0 Fix to locking/unlocking regions. 2018-12-03 00:11:36 +03:00
Konstantin Kichinsky 528fa85685 Updated toolbar creation to reflect changes in CT, added none-selection mode. 2018-12-02 23:42:47 +03:00
Konstantin Kichinsky 29502b3f52 New icon for toolbar 2018-12-02 23:42:13 +03:00
Konstantin Kichinsky 2ca905ed82 CT Update:
* new none-selection mode (for regions manipulation only)
* separtors in toolbar, splitted actions into selectors and switches
* updated styles
* fixes for manipulation/selection overlapping
2018-12-02 23:41:48 +03:00
Konstantin Kichinsky 8475d0a1d1 Merge branch 'master' into bug/stop-propogation 2018-12-01 11:52:40 +03:00
Konstantin Kichinsky fc71d7005e Merge pull request #335 from kichinsky/dev
Prevent hotkeys overlapping with text inputs (VoTT + CT)
2018-12-01 11:17:39 +03:00
Elizabeth Halper 6d683bc9a8 fixed e2e tests 2018-11-30 17:28:41 -08:00
Elizabeth Halper 3f7b7962da button adds tag 2018-11-30 13:30:05 -08:00
Elizabeth Halper 116697a3a4 no hotkeys enabled when focused 2018-11-30 13:17:06 -08:00
Elizabeth Halper fb3a122436 isenabled conditional 2018-11-30 13:12:22 -08:00
Konstantin Kichinsky 3adce4fabd Prevent hotkeys overlapping with text inputs 2018-11-30 22:39:29 +03:00
Konstantin Kichinsky 44a838135a CT Update: refactored code, polyline + point selection modes, prevent hotkeys overlapping with text inputs 2018-11-30 22:38:31 +03:00
Konstantin Kichinsky ebc2aed768 Updated toolbar icons 2018-11-30 22:37:23 +03:00
Elizabeth Halper a45e2112ce region and toolbar hotkeys suppressed 2018-11-30 11:19:32 -08:00
Elizabeth Halper 76a6df0faa Merge branch 'master' into bug/stop-propogation
adding to include updated libraries
2018-11-30 10:12:00 -08:00
Elizabeth Halper 6ab9079810 test disableHotkeys 2018-11-30 08:50:59 -08:00
= a0f3e24349 Fix reorder bug 2018-11-29 17:21:22 -08:00
Elizabeth Halper a7b69c1b59 Merge pull request #330 from kichinsky/dev
Keyboard events management + bug fixes
2018-11-29 22:19:20 +01:00
Elizabeth Halper 0f6f8e113c remove comments 2018-11-28 13:59:38 -08:00
Konstantin Kichinsky 072790d870 Added option to disable hotkeys on toolbar
toolbar.disableHotkeys()
toolbar.enableHotkeys()
2018-11-29 00:58:37 +03:00
Konstantin Kichinsky ed06a74f1a Fix to the mask issue on resizing 2018-11-29 00:04:44 +03:00
Elizabeth Halper e1a7ded76b merge master 2018-11-28 12:47:56 -08:00
Konstantin Kichinsky 88ef64f4a4 Updated regions freezing to cancel all events; Moved selection locking to toolbar. Bug fixes 2018-11-28 23:41:58 +03:00
Konstantin Kichinsky 07ffb9f85c Merge pull request #4 from Microsoft/master
Sync kichinsky/dev with origin master
2018-11-28 22:54:00 +03:00
Elizabeth Halper 30d3236705 improve ui for add-tag textbox 2018-11-28 10:52:39 -08:00
Phil c2bb03eb20 Merge pull request #329 from PIC123/feature/reorder-tags
Add ability to reorder tags
2018-11-28 09:57:19 -08:00
= f913bd26e7 Fix ui bug 2018-11-27 17:00:40 -08:00
= c3f61eaa00 Fix merge and selection bug 2018-11-27 16:27:52 -08:00
Phil 7443691a29 Merge pull request #327 from PIC123/feature/more-nav-controls
Add shortcut to move frames
2018-11-27 16:07:27 -08:00
Phil 84b0cfdb34 Merge pull request #325 from PIC123/feature/tag-controls
Remove tags on the fly
2018-11-27 16:07:08 -08:00
= 8dae870773 Fix tag add bug 2018-11-27 16:04:50 -08:00
= 7cd9da0b90 Merge branch 'master' of https://github.com/Microsoft/VoTT into feature/tag-controls 2018-11-27 15:23:41 -08:00
= 0e999d665c Add help on deleting tags 2018-11-27 15:23:22 -08:00
= fdc90dd25c Address pr comments 2018-11-27 15:14:08 -08:00
= a0b1a01d4c Add ability to reorder tags 2018-11-27 15:01:32 -08:00
Elizabeth Halper 162362086c Merge pull request #328 from elizabethhalper/feature/click-region
Add border to selected tag
2018-11-27 23:45:16 +01:00
Elizabeth Halper 231b9bfc5e Merge pull request #324 from elizabethhalper/feature/add-tag-ad-hoc
added tag ad-hoc
2018-11-27 23:37:51 +01:00
Elizabeth Halper 898fa97d1a addressed comments 2018-11-27 14:32:09 -08:00
Elizabeth Halper 126f6c8bae add border to selected tag 2018-11-27 14:10:18 -08:00
= 9ea4f7502e Add shortcut to move frames using e/q 2018-11-27 13:08:01 -08:00
Phil 9080e8ad4a Merge pull request #323 from kichinsky/dev
Update to CanvasTools
2018-11-27 11:46:58 -08:00
= ac5f93da22 Change shortcut to refresh app in help 2018-11-27 10:36:15 -08:00
= ecff49ece6 Add info on deleting tags to help 2018-11-27 10:26:24 -08:00
= 66bdf236a8 Add remove functionality, fix color bug 2018-11-27 10:17:31 -08:00
Konstantin Kichinsky 81de28502d Update to CanvasTools
- Refactored AreaSelector
- Tooltips for regions
- Bug fixes
- Merged template and copy based selection modes
2018-11-27 11:06:29 +03:00
= 8030a33a50 Add feature to remove tags 2018-11-26 17:29:22 -08:00
Elizabeth Halper aa5f88500d added tag ad-hoc 2018-11-26 17:24:26 -08:00
= ce452a6fd6 Remove comments 2018-11-26 14:24:00 -08:00
= 67a0cf4520 Merge with master 2018-11-20 11:00:40 -08:00
Phil 66af16b718 Merge pull request #314 from Microsoft/revert-linting
Revert linting commit
2018-11-20 10:07:31 -08:00
Tanner Barlow 36ba839185 Revert previous commit 2018-11-20 09:55:51 -08:00
Tanner Barlow d57a02c7c9 Replace ESLint JSON config with Yaml, Add Autofix script (#313)
* replace json with yaml

* add lintfix script

* Run autofix and remove passing rules from ignore
2018-11-19 08:47:44 -08:00
= 75c33022ae Merge with master 2018-11-16 20:04:46 -08:00
Konstantin Kichinsky 654a51ea84 Merge pull request #295 from kichinsky/dev
Toolbar & template/copy-based selection modes. Update to exclusive selection mode. Various bug fixes and visual updates.
2018-11-17 02:19:19 +03:00
Konstantin Kichinsky 9aca260949 Fix to keys processing 2018-11-17 02:12:36 +03:00
Konstantin Kichinsky 52bde64875 Merge pull request #3 from kichinsky/master
Sync dev with new master
2018-11-15 22:31:54 +03:00
Konstantin Kichinsky 0ea755df16 Merge pull request #2 from kichinsky/dev
Merge dev into master
2018-11-15 22:23:46 +03:00
Konstantin Kichinsky 70d717ce82 Merge pull request #1 from Microsoft/master
Update from head fork
2018-11-15 22:20:48 +03:00
Tanner Barlow 56367012dd Build and deploy executables/installers via GitHub releases (#301)
* Build and deploy executables via GitHub releases

* bump version
2018-11-15 07:58:01 -08:00
Darío Hereñú cc7ec33b88 Duplicated word - Typo (#158)
on #145, plus minor fixes. Typo on #186
2018-11-15 07:57:08 -08:00
Konstantin Kichinsky e4f304b308 Adgjustment to background toggling + bug fixes
Update to new CanvasTools.js version
* Adjusted how toggling background works
* Fix to selection in locked mode
* Fix to menu positioning when region is near the bottom edge
2018-11-15 13:24:54 +03:00
= 2408e15b96 Fix issue caused by merge 2018-11-14 17:42:05 -08:00
= 0f6883deb3 Fix merge 2018-11-14 16:54:26 -08:00
= b85b03678c Fix export until and add shorter video json 2018-11-14 16:48:46 -08:00
= 28db72f787 Fix tfrecord output 2018-11-14 10:31:09 -08:00
Konstantin Kichinsky f61e455478 Update to tooltips on toolbar 2018-11-14 19:42:46 +03:00
Konstantin Kichinsky e7e8bd8c15 Fix to setting title mixing with other "title" elements. 2018-11-14 19:41:57 +03:00
= 5c04633666 Add export tests 2018-11-13 16:17:00 -08:00
Konstantin Kichinsky aa2d6ae244 Merge branch 'dev' of https://github.com/microsoft/VoTT into dev 2018-11-14 01:38:54 +03:00
Konstantin Kichinsky 169252ee51 Update to shortcuts help 2018-11-14 01:32:18 +03:00
Konstantin Kichinsky 7aca60d11b Updated library with bux fixes and locking mode for selector and freezin for regionsmanager 2018-11-14 01:21:23 +03:00
Phil 7240bf14ef Merge pull request #291 from Microsoft/dev
Merge dev commits into master
2018-11-13 14:18:03 -08:00
Tanner Barlow 53b5827950 Add v1.6.0 to package.json (#294) 2018-11-13 14:14:01 -08:00
Konstantin Kichinsky f9cd6afad9 Added icons to toolbar 2018-11-13 16:49:56 +03:00
Konstantin Kichinsky eaa7ad31a5 Remove old canvastools library 2018-11-13 16:36:27 +03:00
Konstantin Kichinsky 91cd38966c Update to regenerated library version 2018-11-13 16:36:12 +03:00
Konstantin Kichinsky d771d33b76 Updated styles to add toolbar, moved CanvasTools setup into a separate function 2018-11-13 16:28:13 +03:00
Konstantin Kichinsky a3de9252a5 Added new icons for toolbar 2018-11-13 16:26:57 +03:00
Konstantin Kichinsky 9d19f15381 Update to a new version of CanvasTools
-- Use one file ct.min.js with packaged CanvasTools, SnapSVG + css files.
-- New selection modes
-- Toolbar support
-- Removed dependency from System.js (use webpack loader).
2018-11-13 16:09:16 +03:00
Konstantin Kichinsky f7f6ba115e Merge branch 'dev' of https://github.com/microsoft/VoTT into dev 2018-11-13 15:56:05 +03:00
Phil Cherner 16a9a6b1c4 Merge branch 'feature/import-export-tfrecord' of https://github.com/PIC123/VoTT into feature/import-export-tfrecord 2018-11-12 12:05:41 -08:00
Phil Cherner 2e471182f8 Fix linting error 2018-11-12 12:04:44 -08:00
Wallace Breza 3bcbbf919b Merge branch 'dev' into feature/import-export-tfrecord 2018-11-12 11:55:18 -08:00
Konstantin Kichinsky 5afb357557 Style fix for image/video container 2018-11-09 14:11:19 +03:00
Konstantin Kichinsky ad20a57d35 Update to visual styles
* Make image/video look like an artboard
* Shadow for playing toolbar to separate visually from background
2018-11-09 13:56:44 +03:00
Phil Cherner f2ed98f66f Fix exporting for image and video to tfrecords 2018-11-08 17:25:16 -08:00
Phil Cherner f7857b9bd4 Merge branch 'dev' of https://github.com/Microsoft/VoTT into feature/import-export-tfrecord 2018-11-08 10:11:32 -08:00
Phil Cherner faf2928101 Fix export until bug, still not fully working 2018-11-08 08:38:47 -08:00
Phil Cherner c3436b7f9e Refactor tfrecord export logic to follow VOTT pattern 2018-11-05 17:06:45 -08:00
Phil Cherner 4e85a00511 Fix conflict from merge with dev 2018-11-02 14:57:27 -07:00
Phil Cherner 206ed51730 Fix stepfwd in video 2018-10-30 13:42:23 -07:00
Phil Cherner 03f9797c2a Address pr comments 2018-10-30 12:03:53 -07:00
Phil Cherner cb2ed6eb92 Add queue support for parallel export, add loader for opening tfrecords 2018-10-23 17:40:34 -07:00
Phil Cherner a47dc1f6e9 Add support for laoding tags from tf record, (still need default tag) 2018-10-22 13:42:29 -07:00
Phil Cherner 73f1e3515d Add loading animation for loading/exporting 2018-10-19 08:48:22 -07:00
Phil Cherner 82b356174d Clean up code 2018-10-17 11:18:57 -07:00
Phil Cherner c1ae2a8547 Add loading animation for export and add tf export option to export window 2018-10-17 10:17:36 -07:00
Phil Cherner 0483242569 Add tfrecord export for images 2018-10-16 16:22:19 -07:00
Phil Cherner bb7e8d793b Add export to tf for image/video 2018-10-11 16:31:38 -07:00
Phil Cherner 8d2d27fac1 Fix region scale issue 2018-10-11 16:31:38 -07:00
Phil Cherner 7ec0a034ee Add tfrecord read/write. Still need to fix region scaling 2018-10-11 16:31:37 -07:00
Phil Cherner 0a3b7d25e3 Fix region scale issue 2018-10-08 13:52:45 -07:00
Phil Cherner 4a88258301 Add tfrecord read/write. Still need to fix region scaling 2018-10-08 10:00:47 -07:00
Tyler Gibson 6264388f91 Merge pull request #170 from barisdemiray/patch-1
Remove copy/paste line from video tagging flow
2018-09-12 11:57:32 -07:00
PythicCoder 2520b03315 Merge pull request #219 from Anthbs/patch-2
Fix KITTI export missing training images
2018-08-17 00:58:13 +03:00
Anthony 28779d4f7c Fix KITTI export missing training images
Fix for no training images due to posFrameIndex only being incremented after label generation but all images are saved before the labels are generated.
2018-08-13 12:45:10 +12:00
Ari Bornstein de0e686e9b update the pascal exporter
to support file extensions of more then 4 chars .jpeg
this needs to be refractored to properly extract filenames
2018-07-23 13:15:17 +03:00
PythicCoder 558dab59d4 Merge pull request #207 from PsychoLogicAu/GS-22
Add export for KITTI format
2018-07-13 21:21:42 +03:00
Owen Knight 353d5d90c0 Add export for KITTI format 2018-06-12 16:15:48 +10:00
Baris Demiray 5934172b37 Remove copy/paste line from video tagging flow 2018-04-25 16:07:49 +02:00
51 arquivos alterados com 4488 adições e 13638 exclusões
+31
Ver Arquivo
@@ -0,0 +1,31 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
**Expected behavior**
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Platform**
[ ] Electron
[ ] Browser
**OS**
[ ] Windows
[ ] Mac
[ ] Linux
**Additional context**
Add any other context about the problem here.
+20
Ver Arquivo
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+35 -12
Ver Arquivo
@@ -1,14 +1,30 @@
os: linux
language: node_js
node_js:
- "node"
matrix:
include:
- os: osx
osx_image: xcode9.4
language: node_js
node_js: "10"
env:
- ELECTRON_CACHE=$HOME/.cache/electron
- ELECTRON_BUILDER_CACHE=$HOME/.cache/electron-builder
- os: linux
services: docker
language: generic
cache:
directories:
- node_modules
- $HOME/.cache/electron
- $HOME/.cache/electron-builder
before_script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then export DISPLAY=:99.0; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sh -e /etc/init.d/xvfb start; fi
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then sleep 3; fi
- npm install
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then npm install; fi
script:
- npm test
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then npm test; fi
before_deploy:
- |
if [ "$TRAVIS_OS_NAME" == "linux" ]; then
@@ -18,15 +34,22 @@ before_deploy:
-v ~/.cache/electron:/root/.cache/electron \
-v ~/.cache/electron-builder:/root/.cache/electron-builder \
electronuserland/builder:wine \
/bin/bash -c "npm run pack"
/bin/bash -c "npm run dist:winlinux"
else
npm run dist:mac
fi
before_cache:
- rm -rf $HOME/.cache/electron-builder/wine
deploy:
- provider: releases
api_key: $GITHUB_TOKEN
skip_cleanup: true
file:
- "dist/osx/VoTT-darwin-x64.zip"
- "dist/win/VoTT-win32-x64.zip"
- "dist/linux/VoTT-linux-x64.zip"
- "dist/vott-win.exe"
- "dist/vott-linux.snap"
- "dist/vott-mac.dmg"
skip_cleanup: true
on:
tags: true
tags: true
+26 -17
Ver Arquivo
@@ -9,9 +9,14 @@ The tool supports the following **features**:
- The ability to tag and annotate Image Directories or Stand alone videos.
- Computer-assisted tagging and tracking of objects in videos using the [Camshift tracking algorithm](http://opencv.jp/opencv-1.0.0_org/docs/papers/camshift.pdf).
- Exporting tags and assets to Custom Vision Service CNTK , Tensorflow (PascalVOC) or YOLO format for training an object detection model.
- Exporting tags and assets to CNTK, Tensorflow (PascalVOC) or YOLO format for training an object detection model.
- Running and validating a trained CNTK object detection model on new videos to generate stronger models.
- Exporting tags and assets to Custom Vision Service CNTK, Tensorflow (PascalVOC) or YOLO format for training an object detection model.
- Use Active Learning with trained object detection models (locally or remotely) on new videos to generate stronger models.
## Table of Contents
- [Installation](#installation)
@@ -28,7 +33,7 @@ The tool supports the following **features**:
1. Download and extract the app [release package](https://github.com/CatalystCode/CNTK-Object-Detection-Video-Tagging-Tool/releases)
2. Run the app by launching the "VOTT" executable which will be located inside the unzipped folder.
2. Run the app by launching the `VOTT` executable which will be located inside the unzipped folder.
### Installing the Visual Object Tagging Tool npm
@@ -42,6 +47,12 @@ The tool supports the following **features**:
npm start
```
#### NOTE: Installation on Windows from repo
Python 2.7 and VCBuild.exe are requirements for building VoTT using 'npm install'.
If Python is in the path, the install won't attempt to install it again.
To install VCBuild.exe without Visual Studio, use 'npm install -g windows-build-tools' at an Administrator cmd prompt.
### Installing CNTK with the FRCNN Prerequisites for Reviewing Model
*Please note that installation of **CNTK and FASTER-RCNN dependencies** are **optional for tagging** and are **only required for CNTK model review and training**.*
@@ -50,7 +61,7 @@ The tool supports the following **features**:
2. Follow the setup instructions of the [CNTK Faster-RCNN tutorial](https://docs.microsoft.com/en-us/cognitive-toolkit/object-detection-using-faster-r-cnn#setup) (*Note: Faster-RCNN currently only supports Linux python version 3.4 and not 3.5*).
3. Configure `CNTK-Config.json` (which resides in the '\resources\app' directory of the tagging tool) with the following properties to enable the model review feature:
3. Configure `CNTK-Config.json` (which resides in the `\resources\app` directory of the tagging tool) with the following properties to enable the model review feature:
```json
{
@@ -73,15 +84,15 @@ The tool supports the following **features**:
**Frame Extraction Rate**: number of frames to tag per second of video<br>
**Tagging Region Type**: type of bounding box for tagging regions<br>
**Tagging Region Type**: type of bounding box for tagging regions<br>
- *Rectangle*: tag bounding boxes of any dimension
- *Square*: tag bounding boxes of auto-fixed dimensions
**Suggested Region Method**: how to suggest regions for next frame<br>
- *Tracking*: Use camshift to track tagged regions in next frame
- *Copy Last Frame*: Copy all regions to the next frame.
- *Copy Last Frame*: Copy all regions to the next frame
**Enable Scene Change Detection**: Detect scene changes to prevent false positives when tracking. (Note this option is slightly slower)
**Enable Scene Change Detection**: Detect scene changes to prevent false positives when tracking. (Note: this option is slightly slower)
**Labels**: labels of the tagged regions (e.g. `Cat`, `Dog`, `Horse`, `Person`)<br>
@@ -98,7 +109,7 @@ The tool supports the following **features**:
- Since the [camshift algorithm](http://opencv.jp/opencv-1.0.0_org/docs/papers/camshift.pdf) has some known limitations, you can disable tracking for certain sets of frames. To toggle tracking *on* and *off* use the file menu setting, or the keyboard shortcut Ctrl/Cmd + T.
5. Export video Tags using the Object Detection Menu or Ctrl/Cmd + E
5. Export video Tags using the `Object Detection` Menu or Ctrl/Cmd + E
![]( media/5_Export.jpg)
@@ -131,8 +142,6 @@ The tool supports the following **features**:
![](media/3_image_Job_Configuration.jpg)
**Frame Extraction Rate**: number of frames to tag per second of video<br>
**Tagging Region Type**: type of bounding box for tagging regions<br>
- *Rectangle*: tag bounding boxes of any dimension
- *Square*: tag bounding boxes of auto-fixed dimensions
@@ -151,7 +160,7 @@ The tool supports the following **features**:
**Navigation**: users can navigate between video frames by using the ![prev-nxt](media/prev-next.png) buttons, the left/right arrow keys, or the video skip bar
- Tags are auto-saved each time a frame is changed
5. Export Image directory tags Tags using the Object Detection Menu or Ctrl/Cmd + E
5. Export Image directory Tags using the Object Detection Menu or Ctrl/Cmd + E
![]( media/5_image_Export.jpg)
@@ -175,13 +184,13 @@ There are two options to run a model for active learning within VoTT one is to u
### Remote Active learning using Docker
1. Set up your own remote model endpoint locally or on Azure with docker [CNTK Example](https://github.com/User1m/vott-reviewer-ext)
1. Set up your own remote model endpoint locally or on Azure with Docker [CNTK Example](https://github.com/User1m/vott-reviewer-ext)
2. Paste the review service endpoint to review your model.
### Local Active Learning CNTK Example
1. Train model with [Object Detection using FasterRCNN](https://docs.microsoft.com/en-us/cognitive-toolkit/object-detection-using-faster-r-cnn#run-faster-r-cnn-on-your-own-data)<br>
2. Since CNTK does not embed the names of the classes in the model, on default, the module returns non descriptive names for the classes, e.g. "class_1", "class_2". To resolve this copy the class_map.txt file generated by the FASTER-RCNN tutorial to the same directory that contains your model.
2. Since CNTK does not embed the names of the classes in the model, on default, the module returns non descriptive names for the classes, e.g. "class_1", "class_2". To resolve this copy the `class_map.txt` file generated by the FASTER-RCNN tutorial to the same directory that contains your model.  
3. Load a new asset that the model has not been trained on
4. Configure a new or load a previous tagging job
@@ -192,7 +201,7 @@ There are two options to run a model for active learning within VoTT one is to u
7. Repeat step 1 on new assets until the model performance is satisfactory
## Supporting additonal object detection Export and Review formats.
## Supporting additional object detection Export and Review formats.
In the latest release we provide support for [Export and Review formats](https://github.com/CatalystCode/VOTT/tree/master/src/lib/detection_algorithms). To add a new object detection format, copy the interface folder and use the Yolo and CNTK implementations as reference.
@@ -237,10 +246,10 @@ Use the number keys to quickly tag a selected region *(Only works for single dig
## Upcoming Features
- Tagging project management
- Altenative Tracking algorithms such as optical flow.
- Classification Labeling Support
- Segmentation Annoatation support.
- Tagging project management.
- Alternative Tracking algorithms such as optical flow.
- Classification Labeling Support.
- Segmentation Annotation support.
- Zoom in and out
---

Antes

Largura:  |  Altura:  |  Tamanho: 50 KiB

Depois

Largura:  |  Altura:  |  Tamanho: 50 KiB

BIN
Ver Arquivo
Arquivo binário não exibido.

Depois

Largura:  |  Altura:  |  Tamanho: 48 KiB

+83 -51
Ver Arquivo
@@ -6,6 +6,8 @@ const BrowserWindow = electron.BrowserWindow;
const windowStateKeeper = require('electron-window-state');
const path = require('path');
const url = require('url');
// To access the version defined in package.json
require('pkginfo')(module, 'version');
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
@@ -40,6 +42,16 @@ function createWindow () {
slashes: true
}));
ipcMain.on('setAppTitle', function(event, arg) {
let text = arg;
if (text !== undefined && text !== null) {
text = `${text} - VoTT - ${module.exports.version}`;
} else {
text = `VoTT - ${module.exports.version}`;
}
mainWindow.webContents.send('setAppTitleWithVersion', text);
});
ipcMain.on('setFilePath', function (event, arg) {
mainWindow.setRepresentedFilename(arg);
@@ -55,58 +67,78 @@ function createWindow () {
// do this independently for each object
ipcMain.on('show-popup', function(event, arg) {
let popup = new BrowserWindow({
parent: mainWindow,
modal: true,
show: false,
frame: false,
autoHideMenuBar : true
});
switch (arg.type) {
case "export":
popup.setSize(359, 300);
if (arg.type === "help") {
let helpPopup = new BrowserWindow({
parent: mainWindow,
modal: false,
show: false,
frame: true,
autoHideMenuBar: true
});
helpPopup.setSize(500, 500);
helpPopup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/help-configuration.html'),
protocol: 'file:',
slashes: true
}));
helpPopup.once('ready-to-show', () => {
helpPopup.send('configs', arg);
helpPopup.show();
});
} else {
let popup = new BrowserWindow({
parent: mainWindow,
modal: true,
show: false,
frame: false,
autoHideMenuBar : true
});
switch (arg.type) {
case "export":
popup.setSize(359, 300);
popup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/export-configuration.html'),
protocol: 'file:',
slashes: true
}));
break;
case "review":
popup.setSize(359, 310);
popup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/export-configuration.html'),
pathname: path.join(__dirname, 'src/public/html/review-configuration.html'),
protocol: 'file:',
slashes: true
}));
break;
case "review":
popup.setSize(359, 310);
popup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/review-configuration.html'),
protocol: 'file:',
slashes: true
}));
break;
case "review-endpoint":
popup.setSize(359, 150);
popup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/review-endpoint-configuration.html'),
protocol: 'file:',
slashes: true
}));
break;
case "help":
popup.setSize(500, 500);
popup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/help-configuration.html'),
protocol: 'file:',
slashes: true
}));
break;
default: return;
break;
case "review-endpoint":
popup.setSize(359, 150);
popup.loadURL(url.format({
pathname: path.join(__dirname, 'src/public/html/review-endpoint-configuration.html'),
protocol: 'file:',
slashes: true
}));
break;
default: return;
}
popup.once('ready-to-show', () => {
popup.send('configs', arg);
popup.show();
});
}
popup.once('ready-to-show', () => {
popup.send('configs', arg);
popup.show();
});
});
@@ -114,9 +146,6 @@ function createWindow () {
mainWindow.send('export-tags', arg);
});
ipcMain.on('export-records', (event, arg) => {
mainWindow.send('export-records', arg);
});
ipcMain.on('review-model', (event, arg) => {
mainWindow.send('review-model', arg);
@@ -156,7 +185,7 @@ function createWindow () {
},
{
label: 'Open tfRecord Directory...',
accelerator: 'CmdOrCtrl+R',
accelerator: 'CmdOrCtrl+Space',
click () { mainWindow.webContents.send('openRecordDirectory'); }
},
{
@@ -251,7 +280,7 @@ function createWindow () {
},
{
label: 'Refresh App',
accelerator: 'CmdOrCtrl+Space',
accelerator: 'CmdOrCtrl+R',
click () { mainWindow.reload(); }
}
]
@@ -263,6 +292,9 @@ function createWindow () {
label: 'Keyboard Shortcuts',
accelerator: 'CmdOrCtrl+H',
click () { mainWindow.webContents.send('help');}
},
{
label: 'Version: ' + module.exports.version
}
]
}
+2506 -1774
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+23 -16
Ver Arquivo
@@ -1,22 +1,16 @@
{
"name": "vott",
"version": "1.0.0",
"version": "1.7.2",
"description": "An electron app for building end to end Object Detection Models with CNTK from Sample Videos.",
"main": "main.js",
"scripts": {
"start": "electron .",
"pretest": "eslint src/*.js src/lib",
"test": "jasmine",
"clean": "rm -rf dist",
"clean:osx": "rm -rf dist/osx",
"clean:win": "rm -rf dist/win",
"clean:linux": "rm -rf dist/linux",
"pack": "npm run clean && npm run pack:osx && npm run pack:win && npm run pack:linux",
"pack:osx": "./node_modules/.bin/electron-zip-packager . VoTT --overwrite --platform=darwin --arch=x64 --icon=src/public/images/icon.icns --prune=true --out=dist/osx --ignore=dist --ignore=node_modules",
"pack:win": "./node_modules/.bin/electron-zip-packager . VoTT --overwrite --asar=true --platform=win32 --arch=x64 --icon=src/public/images/icon.ico --prune=true --out=dist/win --version-string.CompanyName=Microsoft --version-string.ProductName=\"Visual Object Tagging Tool\" --ignore=dist --ignore=node_modules",
"pack:linux": "./node_modules/.bin/electron-zip-packager . VoTT --overwrite --asar=true --platform=linux --arch=x64 --icon=src/public/images/icon.icns --prune=true --out=dist/linux --ignore=dist --ignore=node_modules"
"dist:winlinux": "build -wl --x64",
"dist:mac": "build -m --x64"
},
"repository": "https://github.com/CatalystCode/VOTT",
"repository": "https://github.com/Microsoft/VoTT",
"keywords": [
"Video-Tagging",
"CNTK",
@@ -24,30 +18,43 @@
"Object-Detection"
],
"readme": "README.md",
"author": "aribornstein",
"author": "Microsoft",
"license": "MIT",
"devDependencies": {
"babel-eslint": "^10.0.1",
"bower": "^1.7.2",
"electron": "^3.0.8",
"electron": "^3.0.10",
"electron-builder": "^20.38.2",
"electron-packager": "^12.2.0",
"electron-zip-packager": "^4.0.2",
"eslint": "^5.8.0",
"eslint": "^5.9.0",
"eslint-config-strongloop": "^2.1.0",
"jasmine": "^3.3.0",
"jasmine-spec-reporter": "^4.2.1",
"spectron": "^5.0.0",
"spectron-fake-dialog": "0.0.1"
},
"build": {
"appId": "com.microsoft.vott",
"artifactName": "${productName}-${os}.${ext}",
"dmg": {
"icon": "build/icon.icns"
},
"win": {
"icon": "build/icon.ico"
}
},
"dependencies": {
"async": "^2.1.5",
"async": "^2.6.1",
"cntk-fastercnn": "^0.1.2",
"crypto-js": "^3.1.9-1",
"electron-window-state": "^4.0.2",
"reload": "^2.3.1",
"remote": "^0.2.6",
"replace": "^0.3.0",
"rimraf": "^2.6.2",
"send": "^0.16.2",
"tfrecord": "^0.2.0"
"tfrecord": "^0.2.0",
"vott-ct": "^2.1.4"
},
"eslintIgnore": [
"src/lib/detection_algorithms/detectionUtils.js"
+57 -2
Ver Arquivo
@@ -1,11 +1,14 @@
const path = require('path');
const fakeDialog = require('spectron-fake-dialog');
const helper = require('./helpers/spectron_helper');
const rimraf = require('rimraf');
const fs = require('fs');
describe('E2E - Images', () => {
let app = null;
beforeAll((done) => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
app = helper.initializeSpectron()
fakeDialog.apply(app); // Sets up fake dialog mock
app.start().then(done);
@@ -101,11 +104,63 @@ describe('E2E - Images', () => {
.then(done);
});
it('clicking "Cancel" button hides the export window', (done) => {
it('clicking "Export" button starts the export into Pascal VOC format', (done) => {
app.client.windowHandles()
.then(result => result.value[result.value.length - 1])
.then(handle => app.client.window(handle))
.then(() => app.client.click('#cancelButton'))
.then(() => app.client.pause(2000))
.then(() => app.client.selectByValue('#format','Tensorflow Pascal VOC'))
.then(() => app.client.getValue('#format'))
.then(result => expect(result).toEqual('Tensorflow Pascal VOC'))
.then(() => app.client.click('#exportButton'))
.then(() => app.client.pause(2000))
.then(() => {
const expectedPaths = ['Annotations','ImageSets','JPEGImages'].map((dir)=>path.join(__dirname, 'sample_data','sample_images_output',dir));
for(let expectedPath of expectedPaths){
fs.readdir(expectedPath, (err, files) => {
expect(files).toBeTruthy();
if (err) {
throw new Error(err);
}
});
}
rimraf(path.join(__dirname, 'sample_data','sample_images_output'), done);
})
.then(() => app.browserWindow.isVisible())
.then(result => expect(result).toBeFalsy())
.then(done);
});
it('clicking "Object Detection > Export" displays export window again', (done) => {
helper.send(app, 'export')
.then(() => app.client.windowHandles())
.then(result => {
expect(result.value.length).toEqual(2)
return result.value[result.value.length - 1];
})
.then(handle => app.client.window(handle))
.then(() => app.browserWindow.getURL())
.then(url => expect(url).toContain('src/public/html/export-configuration.html'))
.then(() => app.client.getTitle())
.then(title => expect(title).toEqual('Export Configuration'))
.then(done);
});
it('clicking "Export" button starts the export into TFRecord format', (done) => {
app.client.windowHandles()
.then(result => result.value[result.value.length - 1])
.then(handle => app.client.window(handle))
.then(() => app.client.pause(2000))
.then(() => app.client.selectByValue('#format','TFRecords'))
.then(() => app.client.getValue('#format'))
.then(result => expect(result).toEqual('TFRecords'))
.then(() => app.client.click('#exportButton'))
.then(() => app.client.pause(5000))
.then(async () => {
let testRecord = await helper.readRecord(path.join(__dirname, 'sample_data','sample_images_output'),'nike1.png.tfrecord');
expect(String.fromCharCode.apply(null, testRecord.features.feature['image/object/class/text'].bytesList.value[0])).toEqual('nike');
rimraf(path.join(__dirname, 'sample_data','sample_images_output'), done);
})
.then(() => app.browserWindow.isVisible())
.then(result => expect(result).toBeFalsy())
.then(done);
+4 -2
Ver Arquivo
@@ -95,9 +95,11 @@ describe('E2E - New Project', () => {
const projectConfig = JSON.parse(data);
// Validate tags have been creates
// Validate tags have been created
expect(projectConfig.inputTags).toContain(expectedTags.join(','));
expect(projectConfig.frames['grizzly-bear.jpg'][0].tags[0]).toEqual('grizzly-bear');
if (projectConfig.frames['grizzly-bear.jpg'][0].tags[0]){
expect(projectConfig.frames['grizzly-bear.jpg'][0].tags[0]).toEqual('grizzly-bear');
}
fs.unlink(expectedPath, done);
});
+1 -1
Ver Arquivo
@@ -26,7 +26,7 @@ describe('E2E - Startup', () => {
app.browserWindow.getURL()
.then(url => expect(url).toContain('src/index.html'))
.then(() => app.client.getTitle())
.then(title => expect(title).toEqual('Visual Object Tagging Tool'))
.then(title => expect(title).toContain('Visual Object Tagging Tool'))
.then(done);
});
+86
Ver Arquivo
@@ -1,9 +1,12 @@
const path = require('path');
const fakeDialog = require('spectron-fake-dialog');
const helper = require('./helpers/spectron_helper');
const rimraf = require('rimraf');
const fs = require('fs');
describe('E2E - Video', () => {
beforeAll((done) => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
app = helper.initializeSpectron()
fakeDialog.apply(app); // Sets up fake dialog mock
app.start().then(done);
@@ -55,5 +58,88 @@ describe('E2E - Video', () => {
})
.then(done);
});
it('Object Dection -> Export menu item is enabled', (done) => {
helper.isApplicationMenuItemEnabled(app, 'Object Detection', 'Export')
.then(result => expect(result).toBeTruthy())
.then(done);
});
it('clicking "Object Detection > Export" displays export window', (done) => {
helper.send(app, 'export')
.then(() => app.client.windowHandles())
.then(result => {
expect(result.value.length).toEqual(2)
return result.value[result.value.length - 1];
})
.then(handle => app.client.window(handle))
.then(() => app.browserWindow.getURL())
.then(url => expect(url).toContain('src/public/html/export-configuration.html'))
.then(() => app.client.getTitle())
.then(title => expect(title).toEqual('Export Configuration'))
.then(done);
});
it('clicking "Export" button starts the export into Pascal VOC format', (done) => {
app.client.windowHandles()
.then(result => result.value[result.value.length - 1])
.then(handle => app.client.window(handle))
.then(() => app.client.pause(2000))
.then(() => app.client.selectByValue('#format','Tensorflow Pascal VOC'))
.then(() => app.client.getValue('#format'))
.then(result => expect(result).toEqual('Tensorflow Pascal VOC'))
.then(() => {app.client.click('#exportButton')})
.then(() => app.client.pause(2000))
.then(() => {
const expectedPaths = ['Annotations','ImageSets','JPEGImages'].map((dir)=>path.join(__dirname, 'sample_data','sample_video_output',dir));
for(let expectedPath of expectedPaths){
fs.readdir(expectedPath, (err, files) => {
expect(files).toBeTruthy();
if (err) {
throw new Error(err);
}
});
}
rimraf(path.join(__dirname, 'sample_data','sample_video_output'), done);
})
.then(() => app.browserWindow.isVisible())
.then(result => expect(result).toBeFalsy())
.then(done);
});
it('clicking "Object Detection > Export" displays export window again', (done) => {
helper.send(app, 'export')
.then(() => app.client.windowHandles())
.then(result => {
expect(result.value.length).toEqual(2)
return result.value[result.value.length - 1];
})
.then(handle => app.client.window(handle))
.then(() => app.browserWindow.getURL())
.then(url => expect(url).toContain('src/public/html/export-configuration.html'))
.then(() => app.client.getTitle())
.then(title => expect(title).toEqual('Export Configuration'))
.then(done);
});
it('clicking "Export" button starts the export into TFRecord format', (done) => {
app.client.windowHandles()
.then(result => result.value[result.value.length - 1])
.then(handle => app.client.window(handle))
.then(() => app.client.pause(2000))
.then(() => app.client.selectByValue('#format','TFRecords'))
.then(() => app.client.getValue('#format'))
.then(result => expect(result).toEqual('TFRecords'))
.then(() => app.client.click('#exportButton'))
.then(() => app.client.pause(5000))
.then(async () => {
let testRecord = await helper.readRecord(path.join(__dirname, 'sample_data','sample_video_output'),'sample_video_frame_1.jpg.tfrecord');
expect(String.fromCharCode.apply(null, testRecord.features.feature['image/object/class/text'].bytesList.value[0])).toEqual('ball');
rimraf(path.join(__dirname, 'sample_data','sample_video_output'), done);
})
.then(() => app.browserWindow.isVisible())
.then(result => expect(result).toBeFalsy())
.then(done);
});
});
});
+12
Ver Arquivo
@@ -1,3 +1,6 @@
const tfrecord = require('tfrecord');
const path = require('path');
class SpectronHelper {
initializeSpectron() {
var Application = require('spectron').Application
@@ -52,6 +55,15 @@ class SpectronHelper {
}, eventName);
}
async readRecord(pathname, recordName) {
const reader = await tfrecord.createReader(pathname + path.sep + recordName);
let example;
while (example = await reader.readExample()) {
return example;
}
// The reader auto-closes after it reaches the end of the file.
}
isApplicationMenuItemEnabled(app, ...menuPath) {
return app.client.execute((menuPath) => {
let currentMenu = require('electron').remote.Menu.getApplicationMenu();
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+84 -193
Ver Arquivo
@@ -14,14 +14,24 @@ var async = require("async");
var trackingEnabled = true;
var saveState,
visitedFrames, //keep track of the visited frames
visitedFramesNumber,
videotagging,
detection,
trackingExtension,
assetFolder;
assetFolder,
sourceDir;
function setAppTitle(text) {
ipcRenderer.send('setAppTitle', text);
}
ipcRenderer.on('setAppTitleWithVersion', (event, message) => {
document.querySelector("head title").innerHTML = message;
});
$(document).ready(() => {//init confirm keys figure out why this doesn't work
$('#inputtags').tagsinput({confirmKeys: [13, 32, 44, 45, 46, 59, 188]});
setAppTitle("Visual Object Tagging Tool");
});
//ipc rendering
@@ -45,45 +55,6 @@ const mkdirSync = function (dirPath) {
}
}
ipcRenderer.on('export-records', async (event, exportConfig) => {
addLoader();
if(videotagging.imagelist && visitedFrames.size > 0){
mkdirSync(exportConfig.exportPath);
var isLastFrame;
var exportUntil;
switch (exportConfig.exportUntil) {
case "tagged":
exportUntil = videotagging.imagelist.map((file) => file.split(path.sep).pop()).indexOf(Object.keys(self.videotagging.frames)[Object.keys(self.videotagging.frames).length - 1])
break;
case "last":
exportUntil = videotagging.imagelist.length;
break;
case "visited":
exportUntil = visitedFrames.size;
break;
default:
break;
}
let recordPromises = [];
let carg = async.queue(async function(tasks, callback){
if(videotagging.recordlist) await writeRecord(videotagging.imagelist[tasks.i],videotagging.recordlist[tasks.i],exportConfig.exportPath);
else await writeRecord(videotagging.imagelist[tasks.i],null,exportConfig.exportPath);
callback();
},50)
carg.drain = function(){
$(".loader").remove();
}
for (let i = 0; i < exportUntil; i++) {
carg.push({i:i},function(err){
console.log(`record #: ${i}/${exportUntil} saved`)
});
}
}
});
ipcRenderer.on('saveVideo', (event, message) => {
save();
let notification = new Notification('Offline Video Tagger', {
@@ -118,7 +89,11 @@ ipcRenderer.on('export', (event, message) => {
ipcRenderer.on('export-tags', (event, exportConfig) => {
addLoader();
detection.export(videotagging.imagelist, exportConfig.exportFormat, exportConfig.exportUntil, exportConfig.exportPath, testSetSize, () => {
let imagePaths;
if(videotagging.imagelist){
imagePaths = videotagging.imagelist.map((filepath) => path.join(videotagging.sourceDir,filepath))
}
detection.export(imagePaths, exportConfig.exportFormat, exportConfig.exportUntil, exportConfig.exportPath, testSetSize, () => {
if(!videotagging.imagelist){
videotagging.video.oncanplay = updateVisitedFrames;
}
@@ -234,9 +209,17 @@ document.addEventListener('mousewheel', (e) => {
});
window.addEventListener('keydown', (e) => {
if(e.shiftKey && videotagging){
videotagging.multiselection = true;
if(videotagging){
if(e.keyCode >= 49 && e.keyCode <= 57 && e.shiftKey){
let index = e.keyCode - 49
if(videotagging.newTagIndex != index){
videotagging.newTagIndex = index;
}
} else if(e.shiftKey){
videotagging.multiselection = true;
}
}
});
window.addEventListener('keyup', (e) => {
@@ -244,13 +227,13 @@ window.addEventListener('keyup', (e) => {
if(!e.shiftKey){
videotagging.multiselection = false;
}
if(e.keyCode >= 48 && e.keyCode <= 57){
videotagging.newTagIndex = null;
}
var selectedRegions = videotagging.getSelectedRegions();
if(e.ctrlKey && (e.code == 'KeyC' || e.code == 'KeyX' || e.code == 'KeyA')){
var widthRatio = videotagging.overlay.width / videotagging.sourceWidth;
var heightRatio = videotagging.overlay.height / videotagging.sourceHeight;
var content = [];
if(e.code == 'KeyA'){ //select all
@@ -261,11 +244,13 @@ window.addEventListener('keyup', (e) => {
for(let currentRegion of selectedRegions){
content.push(
{
x1: currentRegion.x1 * widthRatio,
y1: currentRegion.y1 * heightRatio,
x2: currentRegion.x2 * widthRatio,
y2: currentRegion.y2 * heightRatio,
tags: currentRegion.tags
x1: currentRegion.box.x1,
y1: currentRegion.box.y1,
x2: currentRegion.box.x2,
y2: currentRegion.box.y2,
tags: currentRegion.tags,
points: currentRegion.points,
type: currentRegion.type
}
)
@@ -287,8 +272,17 @@ window.addEventListener('keyup', (e) => {
var content = JSON.parse(clipboard.readText());
for(let currentRegion of content){
videotagging.createRegion(currentRegion.x1, currentRegion.y1, currentRegion.x2, currentRegion.y2);
let x = Math.min(currentRegion.x1, currentRegion.x2);
let y = Math.min(currentRegion.y1, currentRegion.y2);
let w = Math.abs(currentRegion.x1 - currentRegion.x2);
let h = Math.abs(currentRegion.y1 - currentRegion.y2);
let ps = currentRegion.points.map((point) => new videotagging.CT.Core.Point2D(point.x, point.y));
let type = currentRegion.type;
let rd = new videotagging.CT.Core.RegionData(x, y, w, h, ps, type);
rd = videotagging.editor.scaleRegionToFrameSize(rd);
videotagging.createRegion(rd);
videotagging.addTagsToRegion(currentRegion.tags);
videotagging.showAllRegions();
}
}catch(error) {
console.log('ERROR: No bounding box in clipboard')
@@ -307,7 +301,6 @@ function addLoader(appendTo = "#videoWrapper") {
function updateVisitedFrames(){
if(videotagging.imagelist){
visitedFrames.add(videotagging.imagelist[videotagging.imageIndex].split(path.sep).pop());
visitedFramesNumber.add(videotagging.imageIndex);
} else {
visitedFrames.add(videotagging.getCurrentFrameId());
}
@@ -372,7 +365,7 @@ function openPath(pathName, isDir, isRecords = false) {
$('#framerateGroup').show();
//set title indicator
$('title').text(`Tagging Job Configuration: ${path.basename(pathName, path.extname(pathName))}`);
setAppTitle(`Tagging Job Configuration: ${path.basename(pathName, path.extname(pathName))}`);
$('#inputtags').tagsinput('removeAll');//remove all previous tag labels
if (isDir) {
@@ -384,6 +377,7 @@ function openPath(pathName, isDir, isRecords = false) {
}
assetFolder = path.join(path.dirname(pathName), `${path.basename(pathName, path.extname(pathName))}_output`);
sourceDir = pathName;
try {
var config = require(`${pathName}.json`);
@@ -431,13 +425,19 @@ function openPath(pathName, isDir, isRecords = false) {
videotagging.video.currentTime = 0;
videotagging.framerate = $('#framerate').val();
videotagging.src = ''; // ensures reload if user opens same video
videotagging.sourceDir = sourceDir;
if (config) {
if (config.tag_colors){
videotagging.optionalTags.colors = config.tag_colors;
}
videotagging.inputframes = config.frames;
visitedFrames = new Set(config.visitedFrames);
if(config.visitedFrames){
visitedFrames = new Set(config.visitedFrames);
}
else {
visitedFrames = new Set([Object.keys(config.frames).sort()[0]])
}
visitedFramesNumber = new Set(Array.from(Array(visitedFrames).keys()));
} else {
videotagging.inputframes = {};
@@ -461,17 +461,17 @@ function openPath(pathName, isDir, isRecords = false) {
});
}
visitedFrames = new Set([videotagging.imagelist[0]]);
visitedFramesNumber = new Set([0])
} else {
visitedFrames = new Set();
visitedFramesNumber = new Set();
}
// visitedFrames = (isDir) ? new Set([pathName]) : new Set();
}
if (isDir){
$('title').text(`Image Tagging Job: ${path.dirname(pathName)}`); //set title indicator
if(isRecords) $('title').text(`Image Tagging from Records Job: ${path.dirname(pathName)}`); //set title indicator
if(isRecords) {
setAppTitle(`Image Tagging from Records Job: ${path.dirname(pathName)}`);
} else {
setAppTitle(`Image Tagging Job: ${path.dirname(pathName)}`);
}
//get list of images in directory
var files = fs.readdirSync(pathName);
@@ -493,11 +493,13 @@ function openPath(pathName, isDir, isRecords = false) {
});
}
$('head title').text(`Image Tagging Job: ${videotagging.imagelist[0]}`); //set title indicator
if(isRecords) $('head title').text(`Image Tagging from Records Job: ${videotagging.imagelist[0]}`); //set title indicator
if (videotagging.imagelist.length){
//Check if tagging was done in previous version of VOTT
if(!isNaN(Array.from(visitedFrames)[0])){
visitedFramesNumber = visitedFrames;
visitedFrames = new Set(Array.from(visitedFramesNumber).map(frame => videotagging.imagelist[parseInt(frame)]))
visitedFrames = new Set(Array.from(visitedFrames).map(frame => videotagging.imagelist[parseInt(frame)]))
//Replace the keys of the frames object
Object.keys(videotagging.inputframes).map(function(key, index) {
@@ -506,16 +508,19 @@ function openPath(pathName, isDir, isRecords = false) {
}, this);
}
videotagging.imagelist = videotagging.imagelist.map((filepath) => {return path.join(pathName,filepath)});
videotagging.src = pathName;
//track visited frames
$("#video-tagging").off("stepFwdClicked-AfterStep", updateVisitedFrames);
$("#video-tagging").on("stepFwdClicked-AfterStep", () => {
//update title to match src
if(videotagging.currTFRecord) {
if(!visitedFrames.has(videotagging.getCurrentFrameId())) getRegionsFromRecord(videotagging.currTFRecord);
$('title').text(`Image Tagging from Records Job: ${path.basename(videotagging.imagelist[videotagging.imageIndex])}`);
} else $('title').text(`Image Tagging Job: ${path.basename(videotagging.curImg.src)}`);
if(!visitedFrames.has(videotagging.getCurrentFrameId())) {
getRegionsFromRecord(videotagging.currTFRecord);
}
setAppTitle(`Image Tagging from Records Job: ${path.basename(videotagging.imagelist[videotagging.imageIndex])}`);
} else {
setAppTitle(`Image Tagging Job: ${path.basename(videotagging.curImg.src)}`);
}
updateVisitedFrames();
@@ -523,10 +528,14 @@ function openPath(pathName, isDir, isRecords = false) {
$("#video-tagging").on("stepBwdClicked-AfterStep", () => {
//update title to match src
if(videotagging.currTFRecord) {
if(!visitedFrames.has(videotagging.getCurrentFrameId())) getRegionsFromRecord(videotagging.currTFRecord);
$('title').text(`Image Tagging from Records Job: ${path.basename(videotagging.imagelist[videotagging.imageIndex])}`);
if(!visitedFrames.has(videotagging.getCurrentFrameId())) {
getRegionsFromRecord(videotagging.currTFRecord);
}
setAppTitle(`Image Tagging from Records Job: ${path.basename(videotagging.imagelist[videotagging.imageIndex])}`);
}
else {
setAppTitle(`Image Tagging Job: ${path.basename(videotagging.curImg.src)}`);
}
else $('title').text(`Image Tagging Job: ${path.basename(videotagging.curImg.src)}`);
});
@@ -543,7 +552,7 @@ function openPath(pathName, isDir, isRecords = false) {
return folderSelected();
}
} else {
$('title').text(`Video Tagging Job: ${path.basename(pathName, path.extname(pathName))}`); //set title indicator
setAppTitle(`Video Tagging Job: ${path.basename(pathName, path.extname(pathName))}`);
videotagging.disableImageDir();
videotagging.src = pathName;
//set start time
@@ -566,7 +575,6 @@ function openPath(pathName, isDir, isRecords = false) {
}
//init detection
//detection = new DetectionExtension(videotagging, visitedFramesNumber);
detection = new DetectionExtension(videotagging, visitedFrames);
$('#load-form-container').hide();
@@ -581,12 +589,10 @@ function openPath(pathName, isDir, isRecords = false) {
}
function getRegionsFromRecord(tfRecord){
if(videotagging.sourceWidth == 0 || videotagging.sourceHeight == 0){
videotagging.sourceWidth = tfRecord.features.feature['image/width'].int64List.value[0];
videotagging.sourceHeight = tfRecord.features.feature['image/height'].int64List.value[0];
}
let widthRatio = videotagging.overlay.width / videotagging.sourceWidth;
let heightRatio = videotagging.overlay.height / videotagging.sourceHeight;
videotagging.sourceWidth = tfRecord.features.feature['image/width'].int64List.value[0];
videotagging.sourceHeight = tfRecord.features.feature['image/height'].int64List.value[0];
let widthRatio = videotagging.frameWidth / videotagging.sourceWidth;
let heightRatio = videotagging.frameHeight / videotagging.sourceHeight;
for (let i = 0; i < tfRecord.features.feature['image/object/bbox/xmin'].floatList.value.length; i++) {
@@ -601,115 +607,6 @@ function getRegionsFromRecord(tfRecord){
}
}
async function writeRecord(recordPath, example = null, outputDir = null) {
return new Promise(async (resolve, reject) => {
let widthRatio = videotagging.overlay.width / videotagging.sourceWidth;
let heightRatio = videotagging.overlay.height / videotagging.sourceHeight;
const id = recordPath.split(path.sep).pop();
outputDir = outputDir ? outputDir : recordPath.split(path.sep).slice(0,-1).join(path.sep);
let recordId = id.split(".").slice(0,-1);
recordId.push("tfrecord")
recordId = recordId.join(".")
let outputPath = outputDir + path.sep + recordId;
if(!example){
const regions = videotagging.getRegions(id);
const builder = tfrecord.createBuilder();
let xmin = [];
let ymin = [];
let xmax = [];
let ymax = [];
let tags = [];
let difficult_obj = [];
let truncated = [];
let poses = [];
regions.forEach(region => {
xmin.push(region.x1 / videotagging.sourceWidth)
ymin.push(region.y1 / videotagging.sourceHeight)
xmax.push(region.x2 / videotagging.sourceWidth)
ymax.push(region.y2 / videotagging.sourceHeight)
tags.push(region.tags[0])
difficult_obj.push(0)
truncated.push(0)
poses.push(encode_Uint8("Unspecified"))
});
getDataUri(recordPath).then((data) => {
builder.setIntegers('image/height', [videotagging.sourceHeight]);
builder.setIntegers('image/width', [videotagging.sourceWidth]);
builder.setBinaries('image/filename', [encode_Uint8(id)]);
builder.setBinaries('image/source_id', [encode_Uint8(id)]);
builder.setBinaries('image/key/sha256', [encode_Uint8(CryptoJS.SHA256(data).toString(CryptoJS.enc.Base64))]);
builder.setBinaries('image/encoded', [data]);
if(videotagging.currTFRecord) builder.setBytes('image/format', [encode_Uint8(videotagging.getCurrentFrameId().split(".").slice(-2)[0])]);
else builder.setBinaries('image/format', [encode_Uint8(videotagging.getCurrentFrameId().split(".").slice(-1)[0])]);
function getFirstRegion(frames){
for(frame of Object.keys(frames)){
if(frames[frame] && frames[frame].length) return frames[frame][0]
}
return {width: videotagging.sourceWidth, height: videotagging.sourceHeight}
}
let firstRegion = getFirstRegion(videotagging.frames)
let widthMult = firstRegion.width / videotagging.sourceWidth;
let heightMult = firstRegion.height / videotagging.sourceHeight;
builder.setFloats('image/object/bbox/xmin', xmin.map((x) => x / widthMult));
builder.setFloats('image/object/bbox/ymin', ymin.map((y) => y / heightMult));
builder.setFloats('image/object/bbox/xmax', xmax.map((x) => x / widthMult));
builder.setFloats('image/object/bbox/ymax', ymax.map((y) => y / heightMult));
builder.setBinaries('image/object/class/text', tags.map(tag => encode_Uint8(tag)));
builder.setIntegers('image/object/class/label', tags.map(tag => videotagging.inputtagsarray.indexOf(tag)));
builder.setIntegers('image/object/difficult', difficult_obj);
builder.setIntegers('image/object/truncated', truncated);
builder.setBinaries('image/object/view', poses);
example = builder.releaseExample();
console.log('new example built')
return example
}).then(async (example) => {
// console.log(example)
const writer = await tfrecord.createWriter(outputPath);
await writer.writeExample(example);
await writer.close();
resolve();
});
} else{
const writer = await tfrecord.createWriter(outputPath);
await writer.writeExample(example);
await writer.close();
resolve();
}
});
}
function getDataUri(url) {
return new Promise((resolve,reject) => {
var image = new Image();
image.onload = function () {
var canvas = document.createElement('canvas');
canvas.width = this.naturalWidth; // or 'width' if you want a special/scaled size
canvas.height = this.naturalHeight; // or 'height' if you want a special/scaled size
canvas.getContext('2d').drawImage(this, 0, 0);
// Get raw image data
resolve(canvas.toDataURL('image/png').replace(/^data:image\/(png|jpg);base64,/, ''));
};
image.src = url;
})
}
async function readRecord(pathname, recordName) {
const reader = await tfrecord.createReader(pathname + path.sep + recordName);
@@ -753,8 +650,6 @@ function save() {
let regions = videotagging.getRegions(videotagging.getCurrentFrameId());
videotagging.sourceHeight = videotagging.currTFRecord.features.feature['image/height'].int64List.value[0]
videotagging.sourceWidth = videotagging.currTFRecord.features.feature['image/width'].int64List.value[0]
let widthRatio = videotagging.overlay.width / videotagging.sourceWidth;
let heightRatio = videotagging.overlay.height / videotagging.sourceHeight;
regions.forEach(region => {
xmin.push(region.x1 / videotagging.sourceWidth)
@@ -770,9 +665,6 @@ function save() {
videotagging.currTFRecord.features.feature['image/object/bbox/ymax'].floatList.value = ymax;
videotagging.currTFRecord.features.feature['image/object/class/text'].bytesList.value = tags;
videotagging.recordlist[videotagging.imageIndex] = videotagging.currTFRecord;
// writeRecord(videotagging.imagelist[videotagging.imageIndex],videotagging.currTFRecord).then(()=>{
// console.log(`record saved: ${videotagging.imagelist[videotagging.imageIndex]}`)
// });
}
setTimeout(() => {
saveLock = false;
@@ -786,7 +678,6 @@ function encode_Uint8(s) {
var enc = new TextEncoder();
return enc.encode(s)
// return Uint8Array.from(s);
}
function decode_Uint8(uint8Arr) {
@@ -0,0 +1,143 @@
const async = require('async');
const fs = require('fs');
const path = require('path');
const util = require('util');
const detectionUtils = require('../detectionUtils.js');
const DEFAULT_DATA_SET_NAME = 'obj';
const CFG_TEMPLATE_FILE_PATH = path.join(__dirname, 'yolo-obj.cfg.template');
const OBJ_DATA_TEMPLATE = 'classes = %s\n' +
'train = data/train.txt\n' +
'valid = data/test.txt\n' +
'names = data/%s.names\n' +
'backup = backup/'
// The Exporter interface - provides a mean to export the tagged frames
// data in the expected data format of the detection algorithm
// Constructor parameters:
// exportDirPath - path to the directory where the exported file will be placed
// classes - list of classes supported by the tagged data
// taggedFramesCount - number of positive tagged frames
// frameWidth - The width (in pixels) of the image frame
// frameHeight - The height (in pixels) of the image frame
function Exporter(exportDirPath, classes, taggedFramesCount, frameWidth, frameHeight, testSplit) {
var self = this;
self.dataSetName = DEFAULT_DATA_SET_NAME;
self.exportDirPath = exportDirPath;
self.trainDirPath = path.join(self.exportDirPath, 'train');
self.trainImagesDirPath = path.join(self.trainDirPath, 'images');
self.trainLabelsDirPath = path.join(self.trainDirPath, 'labels');
self.validDirPath = path.join(self.exportDirPath, 'val');
self.validImagesDirPath = path.join(self.validDirPath, 'images');
self.validLabelsDirPath = path.join(self.validDirPath, 'labels');
self.classes = classes;
self.taggedFramesCount = taggedFramesCount;
self.frameWidth = frameWidth;
self.frameHeight = frameHeight;
self.testFrameIndices = null;
self.testFrameNames = null;
self.posFrameLabelIndex = null;
self.posFrameImageIndex = null;
self.testSplit = testSplit || 0.2;
// Prepare everything for exporting (e.g. create metadata files,
// directories, ..)
// Returns: A Promise object that resolves when the operation completes
this.init = function init() {
self.posFrameLabelIndex = 0;
self.posFrameImageIndex = 0;
self.testFrameNames = [];
self.testFrameIndices = detectionUtils.generateTestIndecies(self.testSplit, taggedFramesCount);
self.filesTouched = {}; // Keep track of files we've touched so far
return new Promise((resolve, reject) => {
async.waterfall([
detectionUtils.ensureDirExists.bind(null, self.exportDirPath),
detectionUtils.ensureDirExists.bind(null, self.trainDirPath),
detectionUtils.ensureDirExists.bind(null, self.trainImagesDirPath),
detectionUtils.ensureDirExists.bind(null, self.trainLabelsDirPath),
detectionUtils.ensureDirExists.bind(null, self.validDirPath),
detectionUtils.ensureDirExists.bind(null, self.validImagesDirPath),
detectionUtils.ensureDirExists.bind(null, self.validLabelsDirPath),
], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
// Export a single frame to the training data
// Parameters:
// frameFileName - The file name to use when saving the image file
// frameBuffer - A buffer with the frame image data
// bboxes - a list of bboxes in the format of x1, y1, x2, y2 where the
// coordinates are in absolute values of the image
// tags - a list of objects containing the tagging data. Each object is in the format of:
// {'x1' : int, 'y1' : int, 'x2' : int, 'y2' : int, 'class' : string 'w': int, 'h' :int}
// Where (x1, y1) and (x2, y2) are the coordinates of the top left and bottom right corner
// and w, h are optional overloads for the frame demensions of the bounding boxes (respectively)
// and 'class' is the name of the class
// Returns: A Promise object that resolves when the operation completes
this.exportFrame = function exportFrame(frameFileName, frameBuffer, tags) {
return new Promise((resolve, reject) => {
async.waterfall([
detectionUtils.ensureDirExists.bind(null, self.trainImagesDirPath),
detectionUtils.ensureDirExists.bind(null, self.trainLabelsDirPath),
detectionUtils.ensureDirExists.bind(null, self.validImagesDirPath),
detectionUtils.ensureDirExists.bind(null, self.validLabelsDirPath),
function saveImage(cb) {
var isTestFrame = (self.testFrameIndices.includes(self.posFrameImageIndex));
var outputDirPath = (isTestFrame ? self.validImagesDirPath : self.trainImagesDirPath)
var imageFilePath = path.join(outputDirPath, frameFileName);
fs.writeFile(imageFilePath, frameBuffer, cb);
self.posFrameImageIndex++;
if(isTestFrame) {
self.testFrameNames.push(frameFileName);
}
},
function saveLabel(cb) {
var isTestFrame = (self.testFrameNames.includes(frameFileName));
var outputDirPath = (isTestFrame ? self.validLabelsDirPath : self.trainLabelsDirPath);
var labelFileName = path.parse(frameFileName).name + '.txt';
var labelFilePath = path.join(outputDirPath, labelFileName);
var labelData = '';
for (var i in tags) {
if (i > 0) {
labelData += '\n';
}
var tag = tags[i];
var type = tag.class
var truncated = 0
var occluded = 0
var alpha = 0
var bbox = [tag.x1, tag.y1, tag.x2, tag.y2];
var dimensions = [0, 0, 0]
var location = [0, 0, 0]
var rotation_y = 0
labelData += util.format('%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s',
type, truncated.toFixed(1), occluded, alpha.toFixed(1),
bbox[0].toFixed(2), bbox[1].toFixed(2), bbox[2].toFixed(2), bbox[3].toFixed(2),
dimensions[0].toFixed(1), dimensions[1].toFixed(1), dimensions[2].toFixed(1),
location[0].toFixed(1), location[1].toFixed(1), location[2].toFixed(1),
rotation_y.toFixed(1));
}
fs.writeFile(labelFilePath, labelData, cb);
self.posFrameLabelIndex++;
},
], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
}
exports.Exporter = Exporter;
+2
Ver Arquivo
@@ -0,0 +1,2 @@
module.exports.Exporter = require('./exporter.js').Exporter
module.exports.displayName = 'KITTI'
@@ -0,0 +1,3 @@
{
"main" : "main.js"
}
@@ -0,0 +1,19 @@
const KittiExporter = require('../main.js').Exporter;
const path = require('path');
var exporter = new KittiExporter(path.join(__dirname, 'export_test'), ['cat', 'dog', 'mouse'], 1000, 1000);
var p = exporter.init();
p.then(() => {
var p1 = exporter.exportFrame('1.jpg', 'test_data',
[{class : 'dog', x1 : 300, y1 : 250, x2 : 400, y2: 500}]);
p1.then(()=> {
var p2 = exporter.exportFrame('2.jpg', 'test_data_2',
[{class : 'mouse', x1 : 600, y1 : 310, x2 : 720, y2: 492}]);
p2.then(() => {console.info('done')}, (err) => {console.info('err', err)});
}, (err) => {console.info('err:', err)});
}, (err) => {
console.info('Error occured during init:', err);
});
@@ -77,7 +77,7 @@ function Exporter(exportDirPath, classes, taggedFramesCount, frameWidth, frameHe
async.waterfall(
[
//clear old write
async.each.bind(null, [path.join(self.jpgImgsDirPath, frameFileName), `${path.join(self.annDirPath, frameFileName).slice(0, -4)}.xml`], detectionUtils.deleteFileIfExists),
async.each.bind(null, [path.join(self.jpgImgsDirPath, frameFileName), `${path.join(self.annDirPath, frameFileName).slice(0, -4).replace('.','')}.xml`], detectionUtils.deleteFileIfExists),
function saveImage(cb) {
var imageFilePath = path.join(self.jpgImgsDirPath, frameFileName);
@@ -88,7 +88,7 @@ function Exporter(exportDirPath, classes, taggedFramesCount, frameWidth, frameHe
function saveBboxes(cb) {
xmlData = `<annotation verified="yes">
<folder>Annotation</folder>
<filename>${frameFileName.slice(0, -4)}</filename>
<filename>${frameFileName.slice(0, -4).replace('.','')}</filename>
<path>${path.join(self.jpgImgsDirPath, frameFileName)}</path>
<source>
<database>Unknown</database>
@@ -117,12 +117,12 @@ function Exporter(exportDirPath, classes, taggedFramesCount, frameWidth, frameHe
`
}
xmlData += '</annotation\>'
var outpath = `${path.join(self.annDirPath, frameFileName).slice(0, -4)}.xml`
var outpath = `${path.join(self.annDirPath, frameFileName).slice(0, -4).replace('.','')}.xml`
fs.writeFile(outpath, xmlData, cb);
},
function cleanOldMainData(cb){
replace({
regex: new RegExp(`^${frameFileName.slice(0, -4)}+(.*)\s*[\r\n]`,'m'),
regex: new RegExp(`^${frameFileName.slice(0, -4).replace('.','')}+(.*)\s*[\r\n]`,'m'),
replacement: "",
paths: [self.mainDirPath],
recursive: true,
@@ -140,7 +140,7 @@ function Exporter(exportDirPath, classes, taggedFramesCount, frameWidth, frameHe
var classPath = path.join(self.mainDirPath, element);
// determine wheter to write to train or test path
var outPath = self.testFrameIndecies.includes(self.posFrameIndex) ? `${classPath}_val.txt` : `${classPath}_train.txt`;
var line = tagsClassSet.has(element) ? `${frameFileName.slice(0, -4)} 1\r\n` : `${frameFileName.slice(0, -4)} -1\r\n` ;
var line = tagsClassSet.has(element) ? `${frameFileName.slice(0, -4).replace('.','')} 1\r\n` : `${frameFileName.slice(0, -4).replace('.','')} -1\r\n` ;
outs.push({'outPath':outPath, 'line':line});
});
async.each(outs, (out, callback)=>{
@@ -0,0 +1,155 @@
const async = require('async');
const fs = require('fs');
const path = require('path');
const replace = require("replace");
const detectionUtils = require('../detectionUtils.js');
const tfrecord = require('tfrecord');
const CryptoJS = require("crypto-js");
// The Exporter interface - provides a mean to export the tagged frames
// data in the expected data format of the detection algorithm
// Constructor parameters:
// exportDirPath - path to the directory where the exported file will be placed
// classes - list of classes supported by the tagged data
// taggedFramesCount - number of positive tagged frames
// frameWidth - The width (in pixels) of the image frame
// frameHeight - The height (in pixels) of the image frame
// testSplit - the percent of tragged frames to reserve for test set defaults to 20%
function Exporter(exportDirPath, classes, taggedFramesCount, frameWidth, frameHeight, testSplit) {
var self = this;
self.exportDirPath = exportDirPath;
self.labelMap = path.join(self.exportDirPath,"tf_label_map.pbtxt");
self.classes = classes;
self.taggedFramesCount = taggedFramesCount;
self.frameWidth = frameWidth;
self.frameHeight = frameHeight;
self.testSplit = testSplit || 0.2;
self.posFrameIndex = null;
self.testFrameIndecies = null;
// Prepare everything for exporting (e.g. create metadata files,
// directories, ..)
// Returns: A Promise object that resolves when the operation completes
this.init = function init() {
self.posFrameIndex = 0;
self.testFrameIndecies = detectionUtils.generateTestIndecies(self.testSplit, taggedFramesCount);
return new Promise((resolve, reject) => {
async.waterfall([
async.eachSeries.bind(null,[self.exportDirPath], detectionUtils.ensureDirExists),
detectionUtils.deleteFileIfExists.bind(null,self.labelMap),
function generateLabelMap(cb){
var labelMapData = '';
classes.forEach((element,i)=> {
labelMapData += `item {\r\n id: ${i+1}\r\n name: '${element}'\r\n}\r\n`
});
fs.appendFile(self.labelMap, labelMapData, cb);
}
], (err) => {
if (err) {
reject(err);
return;
}
resolve();
});
});
}
// Export a single frame to the training data
// Parameters:
// frameFileName - The file name to use when saving the image file
// frameBuffer - A buffer with the frame image data
// bboxes - a list of bboxes in the format of x1, y1, x2, y2 where the
// coordinates are in absolute values of the image
// tags - a list of objects containing the tagging data. Each object is in the format of:
// {'x1' : int, 'y1' : int, 'x2' : int, 'y2' : int, 'class' : string}
// Where (x1,y1) and (x2,y2) are the coordinates of the top left and bottom right corner
// of the bounding boxes (respectively), and 'class' is the name of the class.
// Returns: A Promise object that resolves when the operation completes
this.exportFrame = function exportFrame(frameFileName, frameBuffer, tags) {
return new Promise((resolve, reject) => {
var frameName = path.parse(frameFileName).base;
var b64 = frameBuffer.toString('base64');
async.waterfall([
function buildRecord(cb) {
const builder = tfrecord.createBuilder();
let xmin = [];
let ymin = [];
let xmax = [];
let ymax = [];
let classes = [];
let difficult_obj = [];
let truncated = [];
let poses = [];
tags.forEach(tag => {
xmin.push(tag.x1 / tag.w)
ymin.push(tag.y1 / tag.h)
xmax.push(tag.x2 / tag.w)
ymax.push(tag.y2 / tag.h)
classes.push(tag.class)
difficult_obj.push(0)
truncated.push(0)
poses.push(encode_Uint8("Unspecified"))
});
if(tags.length){
builder.setIntegers('image/height', [tags[0].h]);
builder.setIntegers('image/width', [tags[0].w]);
} else {
builder.setIntegers('image/height', [self.frameHeight]);
builder.setIntegers('image/width', [self.frameWidth]);
}
builder.setBinaries('image/filename', [encode_Uint8(frameName)]);
builder.setBinaries('image/source_id', [encode_Uint8(frameName)]);
builder.setBinaries('image/key/sha256', [encode_Uint8(CryptoJS.SHA256(b64).toString(CryptoJS.enc.Base64))]);
builder.setBinaries('image/encoded', [b64]);
if (videotagging.currTFRecord) {
builder.setBytes('image/format', [encode_Uint8(videotagging.getCurrentFrameId().split(".").slice(-2)[0])]);
} else if(videotagging.imagelist){
builder.setBinaries('image/format', [encode_Uint8(videotagging.getCurrentFrameId().split(".").slice(-1)[0])]);
} else {
builder.setBinaries('image/format', [encode_Uint8('mp4')]);
}
builder.setFloats('image/object/bbox/xmin', xmin);
builder.setFloats('image/object/bbox/ymin', ymin);
builder.setFloats('image/object/bbox/xmax', xmax);
builder.setFloats('image/object/bbox/ymax', ymax);
builder.setBinaries('image/object/class/text', classes.map(tag => encode_Uint8(tag)));
builder.setIntegers('image/object/class/label', classes.map(tag => videotagging.inputtagsarray.indexOf(tag)));
builder.setIntegers('image/object/difficult', difficult_obj);
builder.setIntegers('image/object/truncated', truncated);
builder.setBinaries('image/object/view', poses);
example = builder.releaseExample();
console.log('new example built')
cb(null,example);
},
async function writeRecord(example){
let outputPath = self.exportDirPath + path.sep + frameName + '.tfrecord';
const writer = await tfrecord.createWriter(outputPath);
await writer.writeExample(example);
await writer.close();
}
], (err) => {
if (err) {
return reject(err);
}
return resolve();
});
});
}
}
exports.Exporter = Exporter;
@@ -0,0 +1,2 @@
module.exports.Exporter = require('./exporter.js').Exporter
module.exports.displayName = 'TFRecords'
@@ -0,0 +1,3 @@
{
"main": "main.js"
}
@@ -0,0 +1,19 @@
const CNKTExporter = require('../main.js').Exporter;
const path = require('path');
var exporter = new Pascal_PoCExporter(path.join(__dirname, 'export_test'), ['cat', 'dog', 'mouse'], 1000, 1000);
var p = exporter.init();
p.then(() => {
var p1 = exporter.exportFrame('1.jpg', 'test_data',
[{class : 'dog', x1 : 300, y1 : 250, x2 : 400, y2: 500}]);
p1.then(()=> {
var p2 = exporter.exportFrame('2.jpg', 'test_data_2',
[{class : 'mouse', x1 : 600, y1 : 310, x2 : 720, y2: 492}]);
p2.then(() => {console.info('done')}, (err) => {console.info('err', err)});
}, (err) => {console.info('err:', err)});
}, (err) => {
console.info('Error occured during init:', err);
});
+62 -54
Ver Arquivo
@@ -10,7 +10,7 @@ function Detection(videotagging, visitedFrames) {
var self = this;
//maps every frame in the video to an imageCanvas until a specified point NOTE mapVideo clears the oncanplay eventListener
this.mapVideo = function (frameHandler, until) {
this.mapVideo = function (frameHandler, isLastFrame) {
return new Promise((resolve, reject) => {
//init canvas buffer
var frameCanvas = document.createElement("canvas");
@@ -23,36 +23,16 @@ function Detection(videotagging, visitedFrames) {
self.videotagging.video.currentTime = 0;
self.videotagging.playingCallback();
//resolve export until
var isLastFrame;
if (until === "tagged") {
isLastFrame = function (frameId) {
return (!Object.keys(self.videotagging.frames).length) || (frameId >= parseInt(Object.keys(self.videotagging.frames)[Object.keys(self.videotagging.frames).length - 1]));
}
}
else if (until === "visited") {
isLastFrame = function (frameId) {
var lastVisitedFrameId = Math.max.apply(Math, Array.from(self.visitedFrames));
return (frameId >= lastVisitedFrameId);
}
}
else { //last
isLastFrame = function (frameId) {
return (self.videotagging.video.currentTime >= self.videotagging.video.duration);
}
}
function iterateFrames() {
var frameId = self.videotagging.getCurrentFrameNumber();
var lastFrame = isLastFrame(frameId);
var frameNumber = self.videotagging.getCurrentFrameNumber();
var lastFrame = isLastFrame(frameNumber);
if (lastFrame) {
self.videotagging.video.oncanplay = null;
resolve();
}
var frameName = `${path.basename(self.videotagging.src, path.extname(self.videotagging.src))}_frame_${frameId}.jpg`
frameHandler(frameName, frameId, frameCanvas, canvasContext, (err) => {
var frameName = `${path.basename(self.videotagging.src, path.extname(self.videotagging.src))}_frame_${frameNumber}.jpg`
frameHandler(frameName, frameNumber, frameCanvas, canvasContext, (err) => {
if (err) {
reject(err);
}
@@ -65,28 +45,32 @@ function Detection(videotagging, visitedFrames) {
}
//this maps dir of images for exporting
this.mapDir = function (frameHandler, dir) {
this.mapDir = function (frameHandler, dir, isLastFrame) {
return new Promise((resolve, reject) => {
imagesProcessed = 0;
dir.forEach((imagePath, index) => {
var img = new Image();
img.src = imagePath;
img.onload = function () {
var frameCanvas = document.createElement("canvas");
frameCanvas.width = img.width;
frameCanvas.height = img.height;
// Copy the image contents to the canvas
var canvasContext = frameCanvas.getContext("2d");
canvasContext.drawImage(img, 0, 0);
frameHandler(path.basename(imagePath), index, frameCanvas, canvasContext, (err) => {
if (err) {
reject(err);
}
imagesProcessed += 1;
if (imagesProcessed == dir.length) {
resolve();
}
});
if(!isLastFrame(index)){
var img = new Image();
img.src = imagePath;
img.onload = function () {
var frameCanvas = document.createElement("canvas");
frameCanvas.width = img.width;
frameCanvas.height = img.height;
// Copy the image contents to the canvas
var canvasContext = frameCanvas.getContext("2d");
canvasContext.drawImage(img, 0, 0);
frameHandler(path.basename(imagePath), index, frameCanvas, canvasContext, (err) => {
if (err) {
reject(err);
}
imagesProcessed += 1;
if (isLastFrame(imagesProcessed)) {
resolve();
}
});
}
} else {
resolve();
}
});
});
@@ -104,14 +88,40 @@ function Detection(videotagging, visitedFrames) {
if (err) {
cb(err);
}
//resolve export until
var isLastFrame;
if (exportUntil === "tagged") {
isLastFrame = function (frameId) {
let taggedFrames = Object.keys(self.videotagging.frames)
.filter(key => self.videotagging.frames[key].length)
.reduce((res, key) => (res[key] = self.videotagging.frames[key], res), {}); // eslint-disable-line
let condition = (self.videotagging.imagelist) ? self.videotagging.imagelist.indexOf(Object.keys(taggedFrames)[Object.keys(taggedFrames).length - 1]) : parseInt(Object.keys(taggedFrames)[Object.keys(taggedFrames).length - 1])
return (!Object.keys(taggedFrames).length) || (frameId >= condition)
}
}
else if (exportUntil === "visited") {
isLastFrame = function (frameId) {
let lastVisitedFrameId = (self.videotagging.imagelist) ? self.videotagging.imagelist.indexOf(Array.from(self.visitedFrames)[Array.from(self.visitedFrames).length -1]) : Math.max.apply(Math, Array.from(visitedFrames));
return (frameId >= lastVisitedFrameId);
}
}
else { //last
isLastFrame = function (frameId) {
if(self.videotagging.imagelist){
return (self.videotagging.imageIndex >= self.videotagging.imagelist.length);
} else{
return (self.videotagging.video.currentTime + 1 >= self.videotagging.video.duration);
}
}
}
if (dir) {
this.mapDir(exportFrame.bind(err, exporter), dir).then(exportFinished, (err) => {
this.mapDir(exportFrame.bind(err, exporter), dir, isLastFrame).then(exportFinished, (err) => {
console.info(`Error on ${method} init:`, err);
cb(err);
});
} else {
this.mapVideo(exportFrame.bind(err, exporter), exportUntil).then(exportFinished, (err) => {
this.mapVideo(exportFrame.bind(err, exporter), isLastFrame).then(exportFinished, (err) => {
console.info(`Error on ${method} init:`, err);
cb(err);
});
@@ -149,15 +159,13 @@ function Detection(videotagging, visitedFrames) {
//var stanH = (self.videotagging.imagelist) ? frameCanvas.height / region.height : self.videotagging.video.videoHeight / region.height;
var tag = {
class: region.tags[region.tags.length - 1],
x1: parseInt(region.x1 * stanW),
y1: parseInt(region.y1 * stanH),
x2: parseInt(region.x2 * stanW),
y2: parseInt(region.y2 * stanH)
x1: region.box.x1,
y1: region.box.y1,
x2: region.box.x2,
y2: region.box.y2,
w: region.width,
h: region.height
};
if (self.videotagging.imagelist) {
tag.w = parseInt(frameCanvas.width);
tag.h = parseInt(frameCanvas.height);
}
frameTags.push(tag);
});
+79 -38
Ver Arquivo
@@ -18,59 +18,100 @@
<body>
<title>Help</title>
<div id ='help-container' style = "padding: 20px 20px 20px 20px;">
<div class="key-group">
<label for="exampleTextarea" title="(Which keys to press to make stuff happen)">Keyboard Shortcuts: </label>
<h2>App/Frame level shortcuts</h2>
<div class="app-group">
<h3>Open a file or directory</h3>
<ul>
<li><strong>Open Video:</strong> Ctrl + O</li>
<li><strong>Open Image Directory:</strong> Ctrl + I</li>
<li><strong>Save Tags:</strong> Ctrl + S</li>
<li><strong>Toggle Play/Pause:</strong> Space</li>
<li><strong>Toggle Tracking:</strong> Ctrl + T</li>
<li><strong>Toggle Exclusive Add Mode:</strong> Crtl + N</li>
<li><strong>Export Tags:</strong> Ctrl + E</li>
<li><strong>Active Learning:</strong> Ctrl + L</li>
<li><strong>Open TFRecord Directory:</strong> Ctrl + Space</li>
</ul>
<h3>Frame control</h3>
<ul>
<li><strong>Next Frame:</strong>Right Arrow or E</li>
<li><strong>Previous Frame:</strong>Left Arrow or Q</li>
<li><strong>Duplicate last frame regions:</strong> D</li>
<li><strong>Delete Frame:</strong> Shift + Del</li>
<li><strong>Active Learning:</strong> Ctrl + L</li>
</ul>
<h3>Tags collection</h3>
<ul>
<li><strong>Save Tags:</strong> Ctrl + S</li>
<li><strong>Export Tags:</strong> Ctrl + E</li>
</ul>
<h3>Play control</h3>
<ul>
<li><strong>Toggle Play/Pause:</strong> Space</li>
<li><strong>Toggle Tracking:</strong> Ctrl + T</li>
</ul>
<h3>App control</h3>
<ul>
<li><strong>Open Developer Console:</strong> Ctrl + D</li>
<li><strong>Refresh App:</strong> Ctrl + Space</li>
<li><strong>Refresh App:</strong> Ctrl + R</li>
<li><strong>Show Help:</strong> Ctrl + H</li>
<li><strong>Select All:</strong> Ctrl + (A or 1 on number pad)</li>
<li><strong>Cut:</strong> Ctrl + X</li>
<li><strong>Copy:</strong> Ctrl + C</li>
<li><strong>Paste:</strong> Ctrl + V</li>
</ul>
</div>
<div class="mouse-group">
<label for="exampleTextarea">Mouse Commands: </label>
<h2>Region level shortcuts</h2>
<div class="selection-group">
<h3>Region creation tools (rectangular)</h3>
<ul>
<li><strong>Two-point mode:</strong> Hold down Ctrl while creating a region</li>
<li><strong>Square mode:</strong> Hold down Shift while creating a region</li>
<li><strong>Multi-select:</strong> Hold down Shift while selecting regions</li>
<li><strong>Exclusive Tracking mode:</strong> Crtl + N to block frame UI allowing a user to create a region on top of existing regions</li>
<li><strong>Rectangular box:</strong> R key.
<ul>
<li><strong>Two-point mode:</strong> Hold down Ctrl while creating a region</li>
<li><strong>Square mode:</strong> Hold down Shift while creating a region</li>
</ul>
</li>
<li><strong>Template based box:</strong> T key.</li>
<li><strong>Point surrounding box:</strong> P key.</li>
<li><strong>Polyline surrounding box:</strong> Y key.</li>
<li><strong>Exclusive selection:</strong> L key to block/unblock regions manipulation.</li>
</ul>
</div>
<div class="arrow-group">
<label for="exampleTextarea">Region Manipulation with Arrow Keys: </label>
<p>VOTT allows you to fine tune the bounding boxes using the arrow keys in a few different ways. While a region is selected:</p>
<ul>
<li><strong>Move region:</strong> Hold Ctrl + Direction</li>
<li><strong>Shrink region::</strong> Hold Ctrl + Alt + Direction</li>
<li><strong>Expand region:</strong> Hold Ctrl + Shift + Direction</li>
</ul>
</div>
<div class="visual-group">
<label for="exampleTextarea">Styling and Visual manipulations: </label>
<p>Tuning visual display of regions:
<ul>
<li><strong>Toggle region background:</strong> Ctrl + B</li>
</ul>
<div class="manipulation-group">
<h3>Region selection & copying</h3>
<ul>
<li><strong>Multi-select:</strong> Hold down Shift while selecting regions</li>
<li><strong>Select next:</strong> Tab</li>
<li><strong>Select All:</strong> Ctrl + A</li>
<li><strong>Cut:</strong> Ctrl + X</li>
<li><strong>Copy:</strong> Ctrl + C</li>
<li><strong>Paste:</strong> Ctrl + V</li>
</ul>
<h3>Region Manipulation with Arrow Keys</h3>
<p>VOTT allows you to fine tune the bounding boxes using the arrow keys in a few different ways. While a region is selected:</p>
<ul>
<li><strong>Move region:</strong> Hold Ctrl + Direction</li>
<li><strong>Shrink region::</strong> Hold Ctrl + Alt + Direction</li>
<li><strong>Expand region:</strong> Hold Ctrl + Shift + Direction</li>
</ul>
<h3>Styling and Visual manipulations</h3>
<p>Tuning visual display of regions:
<ul>
<li><strong>Toggle region background:</strong> Ctrl + B</li>
</ul>
</div>
<h2>Tags level shortcuts</h2>
<div class="tag-group">
<label for="exampleTextarea">Tagging: </label>
<p>Use the number keys to quickly tag a selected region <br><i>(Only works for single digits 0-9</i>)</p>
<h3>Tagging: </h3>
<ul>
<li><strong>Quick access to tags</strong>: 1-9 numeric keys</li>
<li><strong>Auto applying a tag</strong>: Alt + 1-9 numeric key</li>
</ul>
<h3>Changing Tags</h3>
<ul>
<li><strong>Change mapped hotkey</strong>: hold Shift + 1-9 numeric key <i>and</i> click on the tag that you want to assign to that hotkey.</li>
<li><strong>Delete tag from the list</strong>: Ctrl + click on the tag you want removed. It will remove the tag from the list and untag any images that use that tag.</li>
</ul>
</div>
<div id="cancelButton" class="btn btn-primary" style="display:inline-block;">Cancel</div>
</div>
</body>
</html>
+1 -6
Ver Arquivo
@@ -9,7 +9,6 @@ window.onload = function(){
ipcRenderer.on('configs', (event, configs) => {
$('#output').val(configs.assetFolder);
$('#format').empty(); // remove old options
$('#format').append($("<option></option>").attr("value", "tfrecord").text("TF Record"));
configs.supportedFormats.forEach( (algorithm) => {
$('#format').append($("<option></option>").attr("value", algorithm).text(algorithm));
});
@@ -22,11 +21,7 @@ function getExportConfiguration() {
exportPath: $('#output').val(),
exportFormat: $('#format').val()
};
if(exportConfig.exportFormat === "tfrecord") {
ipcRenderer.send('export-records', exportConfig);
} else {
ipcRenderer.send('export-tags', exportConfig);
}
ipcRenderer.send('export-tags', exportConfig);
closeWindow();
}
@@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="isolation:isolate" viewBox="0 0 64 64" width="64" height="64">
<g>
<circle cx="52" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="52" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="12" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="12" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="12" y1="16" x2="12" y2="48" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="12" x2="48" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="52" y1="16" x2="52" y2="48" stroke-width="1" stroke="rgb(0,0,0)"/>
<line x1="16" y1="52" x2="48" y2="52" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="16" x2="48" y2="48" stroke-width="1" stroke="rgb(0,0,0)" stroke-dasharray="3" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 1.0 KiB

@@ -0,0 +1,31 @@
<?xml version="1.0" standalone="no"?><!-- Generator: Gravit.io -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 64 64" width="64" height="64">
<g >
<g>
<line class="" x1="8" y1="20" x2="8" y2="56" stroke-width="1" stroke="rgb(0,0,0)" />
<line class="" x1="8" y1="56" x2="44" y2="56" stroke-width="1" stroke="rgb(0,0,0)" />
<line class="" x1="8" y1="20" x2="12" y2="20" stroke-width="1" stroke="rgb(0,0,0)" />
<line class="" x1="44" y1="56" x2="44" y2="52" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
<g>
<circle class="accent-f" cx="52" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="52" cy="44" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="20" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="20" cy="44" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="20" y1="16" x2="20" y2="24" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="24" y1="12" x2="32" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="48" y1="12" x2="40" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="52" y1="16" x2="52" y2="24" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="20" y1="40" x2="20" y2="32" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="24" y1="44" x2="32" y2="44" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="48" y1="44" x2="40" y2="44" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="52" y1="40" x2="52" y2="32" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
<g>
<line x1="36" y1="8" x2="36" y2="48" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="28" x2="56" y2="28" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 2.0 KiB

@@ -0,0 +1,33 @@
<?xml version="1.0" standalone="no"?><!-- Generator: Gravit.io -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 64 64" width="64" height="64">
<defs>
<clipPath id="_clipPath_xT69JNLss05WYPqZrC8QiRch78i8vo4U">
<rect width="64" height="64"/>
</clipPath>
</defs>
<g clip-path="url(#_clipPath_xT69JNLss05WYPqZrC8QiRch78i8vo4U)">
<g/>
<line x1="0" y1="0" x2="0" y2="0" stroke-width="1" stroke="rgb(0,0,0)" />
<g>
<circle cx="12" cy="28" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="12" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="52" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
<line x1="12" y1="32" x2="12" y2="48" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="52" x2="48" y2="52" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="28" x2="28" y2="28" stroke-width="1" stroke="rgb(0,0,0)" />
<path class="accent-f" d=" M 39 38 L 42 37 L 39 28 L 46 29 L 32 12 L 31 34 L 36 29 L 39 38 Z " fill="rgb(255,255,255)" stroke-width="1" stroke="rgb(0,0,0)" />
<g>
<path d=" M 28 12 C 28 9.725 29.831 8 32 8 C 34.169 8 36 9.962 36 12" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="12" y1="12" x2="12" y2="16" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="12" y1="12" x2="16" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="20" y1="12" x2="24" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="12" y1="20" x2="12" y2="24" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="32" y1="52" x2="32" y2="48" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="32" y1="44" x2="32" y2="40" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
<line x1="52" y1="48" x2="52" y2="28.5" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="50" y1="28" x2="52" y2="28" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 2.1 KiB

@@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?><!-- Generator: Gravit.io -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 64 64" width="64" height="64">
<g>
<line x1="32" y1="8" x2="32" y2="16" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="32" y1="48" x2="32" y2="56" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="48" y1="32" x2="56" y2="32" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="8" y1="32" x2="16" y2="32" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="32" cy="32" r="4" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 694 B

@@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?><!-- Generator: Gravit.io -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 64 64" width="64" height="64">
<g>
<path d=" M 20 12 L 12 32 L 24 52 L 52 40 L 48 16 L 20 12 Z " fill="rgb(255,255,255)" fill-opacity="0.5" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="52" cy="40" r="4" fill="rgb(255,255,255)" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="20" cy="12" r="4" fill="rgb(255,255,255)" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="48" cy="16" r="4" fill="rgb(255,255,255)" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="12" cy="32" r="4" fill="rgb(255,255,255)" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="24" cy="52" r="4" fill="rgb(255,255,255)" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 970 B

@@ -0,0 +1,12 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 64 64" width="64" height="64">
<g >
<path d=" M 12 12 L 28 24 L 32 52 L 40 36 L 52 24" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="52" cy="24" r="4" fill="white" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="12" cy="12" r="4" fill="white" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="28" cy="24" r="4" fill="white" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="32" cy="52" r="4" fill="white" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="40" cy="36" r="4" fill="white" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 845 B

@@ -0,0 +1,14 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="isolation:isolate" viewBox="0 0 64 64" width="64" height="64">
<g>
<g>
<rect x="12" y="12" width="40" height="40" fill="rgb(255,255,255)" fill-opacity="0.2" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="52" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="52" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="12" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle class="accent-f" cx="12" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 807 B

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?><!-- Generator: Gravit.io -->
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" style="isolation:isolate" viewBox="0 0 64 64" width="64" height="64">
<defs>
<clipPath id="_clipPath_GV4tHvkDcbdqnU000cJbyxPo6AT3ZZBk">
<rect width="64" height="64"/>
</clipPath>
</defs>
<g clip-path="url(#_clipPath_GV4tHvkDcbdqnU000cJbyxPo6AT3ZZBk)">
<line x1="8" y1="48" x2="8" y2="16" vector-effect="non-scaling-stroke" stroke-width="1" stroke="rgb(0,0,0)" stroke-linejoin="miter" stroke-linecap="square" stroke-miterlimit="3"/>
<line x1="8" y1="16" x2="32" y2="16" vector-effect="non-scaling-stroke" stroke-width="1" stroke="rgb(0,0,0)" stroke-linejoin="miter" stroke-linecap="square" stroke-miterlimit="3"/>
<line x1="8" y1="48" x2="48" y2="48" vector-effect="non-scaling-stroke" stroke-width="1" stroke="rgb(0,0,0)" stroke-linejoin="miter" stroke-linecap="square" stroke-miterlimit="3"/>
<line x1="48" y1="48" x2="48" y2="40" vector-effect="non-scaling-stroke" stroke-width="1" stroke="rgb(0,0,0)" stroke-linejoin="miter" stroke-linecap="square" stroke-miterlimit="3"/>
<rect x="40" y="20" width="16" height="12" transform="matrix(1,0,0,1,0,0)" fill="transparent" vector-effect="non-scaling-stroke" stroke-width="1" stroke="rgb(0,0,0)" stroke-linejoin="miter" stroke-linecap="square" stroke-miterlimit="2"/>
<path d=" M 44 19.981 Q 44.083 20.602 44 13.356 C 43.917 6.111 52.083 6.318 52 13.356 Q 51.917 20.395 52 19.981" fill="none" vector-effect="non-scaling-stroke" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 1.6 KiB

@@ -0,0 +1,21 @@
<?xml version="1.0" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
style="isolation:isolate" viewBox="0 0 64 64" width="64" height="64">
<g>
<circle cx="52" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="52" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="12" cy="12" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<circle cx="12" cy="52" r="4" fill="none" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="32" y1="8" x2="32" y2="56" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="8" y1="32" x2="56" y2="32" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="12" y1="16" x2="12" y2="24" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="12" x2="24" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="48" y1="12" x2="40" y2="12" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="52" y1="16" x2="52" y2="24" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="12" y1="48" x2="12" y2="40" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="16" y1="52" x2="24" y2="52" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="48" y1="52" x2="40" y2="52" stroke-width="1" stroke="rgb(0,0,0)" />
<line x1="52" y1="48" x2="52" y2="40" stroke-width="1" stroke="rgb(0,0,0)" />
</g>
</svg>

Depois

Largura:  |  Altura:  |  Tamanho: 1.4 KiB

@@ -1,120 +0,0 @@
/*
.regionStyle
--> .tagsLayer
--> .primaryTagRectStyle
.primaryTagTextBGStyle
.primaryTagTextStyle
//.tagStyle [n]
--> .dragLayer
--> .dragRectStyle
--> .anchorsLayer
--> .anchorStyle [4]
.anchorStyle.ghost
--> .menuLayer
--> .menuRectStyle
*/
.dragRectStyle {
fill: transparent;
stroke-width: 0;
pointer-events: all;
cursor: move;
}
.primaryTagRectStyle {
fill: rgba(64, 64, 64, 0.4);
stroke-width: 2;
stroke:rgba(196, 196, 196, 0.6);
stroke-dasharray: 2 6;
stroke-linecap: round;
filter: url(#black-glow);
}
.primaryTagTextStyle {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-size: 9pt;
fill: #fff;
}
.primaryTagTextBGStyle {
stroke-width: 0;
fill: rgba(0, 0, 0, 0.5);
}
.regionStyle:hover .primaryTagRectStyle {
fill: rgba(128, 128, 128, 0.4);
stroke: rgb(255, 255, 255);
}
.regionStyle.selected .primaryTagRectStyle {
fill: rgba(128, 128, 128, 0.4);
stroke: rgb(255, 255, 255);
stroke-dasharray: none;
}
.anchorStyle {
stroke-width: 2;
stroke: #ccc;
fill: #333;
}
.anchorStyle.TL, .anchorStyle.BR {
cursor: nwse-resize;
}
.anchorStyle.TR, .anchorStyle.BL {
cursor: nesw-resize;
}
.anchorStyle.ghost, .anchorStyle.ghost:hover{
stroke-width: 0;
fill: rgba(255, 0, 0, 0);
}
.anchorStyle.ghost:hover {
fill: rgba(255,255,255,0.5);
}
.anchorStyle:hover {
stroke: rgb(25, 119, 96);
fill:rgb(7, 189, 143);
}
.regionStyle:hover .anchorStyle {
stroke: #fff;
}
.regionStyle.selected .anchorStyle {
stroke: rgb(7, 189, 143);
}
svg:not(:root) .menuLayer {
overflow: visible;
}
.menuRectStyle {
stroke-width:0;
fill: rgba(64, 64, 64, 0.8);
filter: url(#black-glow);
}
.menuItemBack {
stroke-width: 1.5;
stroke: rgba(198, 198, 198, 0.2);
fill: rgb(32, 32, 32);
}
.menuIcon {
font-family: 'Segoe UI Emoji', Tahoma, Geneva, Verdana, sans-serif;
font-size: 10pt;
fill: rgb(64, 64, 64);
}
.menuItem {
stroke-width: 1.5;
stroke: rgba(198, 198, 198, 0.2);
fill:transparent;
}
.menuItem:hover {
stroke: rgba(198, 198, 198, 0.8);
}
@@ -1,31 +0,0 @@
#selectionOverlay {
position: relative;
width: 100%;
height: 100%;
}
.crossStyle line {
stroke-width:1;
stroke-dasharray: 3 3;
stroke: #666;
}
.overlayStyle {
fill: #000;
fill-opacity: 0.5;
stroke-width: 0;
}
.overlayMaskStyle {
fill: #fff;
stroke-width: 0;
visibility: visible;
}
.selectionBoxMaskStyle {
/* Transparent crop*/
fill: #000;
stroke-width: 0;
visibility: visible;
}
@@ -0,0 +1,385 @@
/*General*/
:host {
display: block;
box-sizing: border-box;
}
.controlWrapper{
width:100%;
height:100%;
border: 0px;
border-style: solid;
border-color: green;
display: none;
overflow: hidden;
background-color: rgb(64,64,64);
}
.clickableControl{
cursor: pointer;
}
.relativeDiv{
position:relative;
}
/* Grid */
#controlWrapper {
grid-template-rows: auto 60px 120px;
}
#videoWrapper {
align-content: center;
justify-content: center;
}
#videoWrapper .video-tagging {
text-align: center;
}
#vid {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
#videoControls {
grid-row: 2;
}
#videoTagControls {
grid-row: 3;
}
#frameText {
background-color: transparent;
border-color: #ccc;
border-width: 1px;
text-decoration: none;
border-style: solid;
}
/* CANVAS TOOLS */
#ctZone {
display: grid;
width: 100%;
height: 100%;
grid-template-columns: 64px 1fr;
grid-template-rows: 1fr;
background: rgb(64,64,64);
}
.toolbarStyle {
grid-row: 1;
grid-column: 1/2;
}
.toolbarStyle svg {
width: 64px;
height: 100%;
margin-right:5px;
background: #666;
box-shadow: 0 0 0.5px #999, 5px 0px 10px -5px rgb(0,0,0);
}
#selectionZone {
grid-row: 1;
grid-column: 2/3;
justify-self: center;
align-self: center;
background: rgb(64,64,64);
margin: 5px;
width: calc(100% - 10px);
height: calc(100% - 10px);
}
#editorZone svg {
box-shadow: 0px 0px 0.25px #999, 0px 0px 10px rgb(0,0,0);
}
/*Video area*/
.overlaystyle {
position: absolute;
z-index: 20;
border:0px solid green;
display:none;
}
/* .frameCanvasStyle {
position: absolute;
z-index: 10;
} */
/* .selectionZoneStyle {
z-index: 30;
position: absolute;
width: 100%;
height: 100%;
box-shadow: 0px 0px 0.25px #999, 0px 0px 10px rgb(0,0,0);
} */
.videoStyle {
position:relative;
z-index: 2;
grid-row: 1;
grid-column: 2/3;
justify-self: center;
align-self: center;
visibility: hidden;
width: 100%;
height: 100%;
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
box-sizing: border-box;
}
/*playback*/
.playSpeedControl {
background:black;
z-index:20000;
color:white;
position: absolute;
border:.08em solid white;
display: none;
}
.playSpeedValues{
padding:.2em;
color: rgb(191,191,191);
text-align: center;
font-family: Arial;
}
.playSpeedValueSelected{
color: rgb(255,255,255);
}
/*region div and label*/
.regionLabel{
background:rgb(59,56,56);
position:absolute;
z-index:50000;
color:white;
font-family: "Arial";
display: block;
opacity:.90;
width:40px;height:20px;
}
.regionLabelSpan{
padding-left: .3em;
color:rgb(175,171,171);
}
.regionCanvas{
z-index: 100;
background-color: transparent;
position: absolute !important;
/* border: 2px solid; */
box-shadow: inset 0 0 0 2px;
border-radius: 8px;
}
.regionPoint{
border: none;
/*add an x from http://stackoverflow.com/questions/18012420/draw-diagonal-lines-in-div-background-with-css*/
background:
linear-gradient(to top left,
rgba(0,0,0,0) 0%,
rgba(0,0,0,0) calc(50% - 0.8px),
rgba(0,0,0,1) 50%,
rgba(0,0,0,0) calc(50% + 0.8px),
rgba(0,0,0,0) 100%),
linear-gradient(to top right,
rgba(0,0,0,0) 0%,
rgba(0,0,0,0) calc(50% - 0.8px),
rgba(0,0,0,1) 50%,
rgba(0,0,0,0) calc(50% + 0.8px),
rgba(0,0,0,0) 100%);
}
.regionCanvasSelected{
/* border: 2px solid; */
box-shadow: inset 0 0 0 2px;
border-radius: 8px;
background: repeating-linear-gradient(-45deg, rgba(200, 200, 200, 0.1) 10px, rgba(200, 200, 200, 0.1) 20px, rgba(0, 0, 0, 0.2) 20px, rgba(0, 0, 0, 0.2) 30px);
}
.closeRegion{
float:right;
color: rgb(255,0,0);
padding-right: .2em;
}
/*video controls*/
.videoControls {
width:100%;
margin-top: 0px;
box-shadow: 0 0 10px #000;
background-color: #333;
border-top: 0.25px solid rgba(64,64,64,0.7)
}
.videoControlsTable{
border: 0px solid red;
margin-left:1%;
margin-right:1%;
}
.videoControlCell{
text-align:center;
padding-top: 1em;
padding-bottom: 1em;
}
.seekCell{
padding-left: 1em;
padding-right: 1.5em;
}
.volumeControlCell{
text-align:left;
}
.simpleControl {
width:5%;
}
.longControl{
width:10%;
}
.frameNumber{
width:15%;
}
.textElements{
font-family:arial;
color:white;
text-align: center;
}
/*tagging controls*/
.videoTagControls {
border: rgb(38,38,38,0.25);
background-color: black;
margin-top:.15em;
padding-bottom: .5em;
}
.optionalTags{
float: left;
width: 85%;
margin: 1em 0 0 1.5em;
overflow-y: auto;
height: 100%;
}
.labelControls {
float:right;
margin: 1em 0 0;
width:10%;
}
.addTag {
padding-right: 1.2em;
}
.tagInput {
max-width: 100px;
}
.lockTag{
padding-left: .4em;
}
.taggingControls{
color:rgb(255,255,255);
font-size: 130%;
cursor:pointer;
text-align: center;
}
.controlOn{
color:rgb(255,255,255);
}
.controlOff{
color:rgb(89,89,89);
}
.tagButtons{
margin-bottom: 10px;
margin-left: 5px;
background : rgb(59,56,56);
color:rgb(127,127,127);
max-width: 200px;
min-width: 20px;
font-family: Arial;
font-size: 15px;
border: 1px solid rgb(64,64,64);
border-radius: 5px;
user-select: none;
}
.tagButtons sup {
user-select: none;
}
.tagOn{
border-style: solid;
border-color: rgb(217,217,217);
border-width: thick;
}
.tagOff{
background:rgb(59,56,56);
}
.tagButtons.auto::after {
position: relative;
content: "A";
background: rgba(21, 127, 240, 1.0);
color: white;
border-radius: 50%;
width: 12px;
height: 12px;
font-size: 9px;
right: -5px;
top: -5px;
overflow: visible;
display: inline-block;
text-align: center;
}
.tagButtons.hotkey span {
position: relative;
background: rgba(255, 255, 255, 1);
color: black;
border-radius: 50%;
width: 12px;
height: 12px;
font-size: 9px;
right: -3px;
top: -5px;
overflow: visible;
display: inline-block;
text-align: center;
}
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
+70 -22
Ver Arquivo
@@ -11,8 +11,8 @@
<template>
<style include="video-taggingstyles"></style>
<div id = "tagsContainer">
<div id = "buttonsContainer"></div>
</div>
<div id = "buttonsContainer"></div>
</div>
</template>
<script>
@@ -54,6 +54,7 @@
createTagControls: function(tagsArray) {
this.tags = tagsArray;
this.clearTagsState();
let videotagging = document.getElementById('video-tagging');
$('#buttonsContainer').empty();
if (!this.colors){
@@ -69,15 +70,36 @@
var self = this;
btn.addEventListener("click", (e) => {
let tag = e.currentTarget.id;
if (this.tagsState[tag] == "empty" || this.tagsState[tag] == "partial") {
if((videotagging.newTagIndex || videotagging.newTagIndex == 0) && e.shiftKey){
let originalIndex = videotagging.inputtagsarray.indexOf(tag)
if(originalIndex != videotagging.newTagIndex){
let temp = videotagging.inputtagsarray[originalIndex];
videotagging.inputtagsarray[originalIndex] = videotagging.inputtagsarray[videotagging.newTagIndex];
videotagging.inputtagsarray[videotagging.newTagIndex] = temp;
temp = this.colors[originalIndex];
this.colors[originalIndex] = this.colors[videotagging.newTagIndex];
this.colors[videotagging.newTagIndex] = temp;
this.createTagControls(videotagging.inputtagsarray);
videotagging.showAllRegions();
}
} else if(e.ctrlKey){
let index = videotagging.inputtagsarray.indexOf(tag);
if (index !== -1){
videotagging.inputtagsarray.splice(index, 1);
videotagging.optionalTags.colors.splice(index, 1);
}
document.getElementById(tag).remove();
videotagging.checkRemovedTags();
} else if (e.altKey) {
self.toggleAutoState(e.currentTarget);
} else if (this.tagsState[tag] === 'empty' || this.tagsState[tag] === 'partial') {
this.tagsState[tag] = "full";
self.setSelected(e.currentTarget);
} else {
this.tagsState[tag] = "empty";
self.setUnselected(e.currentTarget);
}
self.fire("ontagschanged", { selected: this.projectSelectedTags(), state: this.tagsState});
}
self.fire("ontagschanged", { selected: this.projectSelectedTags(), state: this.tagsState});
});
btn.style.color = this.colors[index];
@@ -85,7 +107,9 @@
// Add indexes to map keyboard
if (index <= 9) {
btn.innerHTML = `${tagsArray[index]}<sup>[${index < 10 ? index + 1 : ""}]</sup>`;
btn.innerHTML = `${tagsArray[index]}<span>${index < 10 ? index + 1 : ""}</span>`;
btn.dataset.hotkey = index + 1;
btn.classList.add("hotkey");
} else {
btn.innerHTML = `${tagsArray[index]}`;
}
@@ -93,27 +117,47 @@
Polymer.dom(this.$.buttonsContainer).appendChild(btn);
}
window.addEventListener("keyup", (e) => {
if (e.keyCode >= 49 && e.keyCode <= 57) {
if(videotagging.hotkeysAssigned){
window.removeEventListener("keyup", this.assignHotkeys);
}
window.addEventListener("keyup", this.assignHotkeys);
videotagging.hotkeysAssigned = true;
},
assignHotkeys(e) {
if (e.keyCode >= 49 && e.keyCode <= 57) {
if (e.altKey) {
let index = e.keyCode - 49;
if (index < this.tags.length) {
let tag = this.tags[index];
let btn = this.getBtnByTag(tag);
if(!btn.disabled) {
if (this.tagsState[tag] == "empty" || this.tagsState[tag] == "partial") {
this.tagsState[tag] = "full";
self.setSelected(btn);
if (index < videotagging.optionalTags.tags.length) {
let tag = videotagging.optionalTags.tags[index];
let btn = videotagging.optionalTags.getBtnByTag(tag);
videotagging.optionalTags.toggleAutoState(btn);
}
} else if (!e.shiftKey) {
// debugger;
let index = e.keyCode - 49;
if (index < videotagging.optionalTags.tags.length) {
let tag = videotagging.optionalTags.tags[index];
let btn = videotagging.optionalTags.getBtnByTag(tag);
if (!btn.disabled) {
if (videotagging.optionalTags.tagsState[tag] == "empty" || videotagging.optionalTags.tagsState[tag] == "partial") {
videotagging.optionalTags.tagsState[tag] = "full";
videotagging.optionalTags.setSelected(btn);
} else {
this.tagsState[tag] = "empty";
self.setUnselected(btn);
videotagging.optionalTags.tagsState[tag] = "empty";
videotagging.optionalTags.setUnselected(btn);
}
self.fire("ontagschanged", { selected: this.projectSelectedTags(), state: this.tagsState});
videotagging.optionalTags.fire("ontagschanged", { selected: videotagging.optionalTags.projectSelectedTags(), state: videotagging.optionalTags.tagsState });
}
}
}
}
});
},
}
},
toggleAutoState(btn) {
btn.classList.toggle('auto');
},
getBtnByTag(tag) {
for (var i=0;i<this.buttonsContainer.childNodes.length;i++) {
@@ -149,6 +193,10 @@
getSelectedTags :function(){
return $(this.buttonsContainer).find(".tagOn");
},
getAutoTags :function(){
return $(this.buttonsContainer).find(".auto");
},
resetSelected: function(){
var self = this;
$(this.buttonsContainer).find('.tagButtons').each(function(index){
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
@@ -7,308 +7,7 @@
<template>
<style>
/*General*/
:host {
display: block;
box-sizing: border-box;
}
.controlWrapper{
width:100%;
height:100%;
border: 0px;
border-style: solid;
border-color: green;
display: none;
overflow: hidden;
background-color: rgb(64,64,64);
}
.clickableControl{
cursor: pointer;
}
.relativeDiv{
position:relative;
}
/* Grid */
#controlWrapper {
grid-template-rows: auto 60px 120px;
}
#videoWrapper {
grid-row: 1;
display:grid;
grid-template-rows: 1fr;
grid-template-columns: 1fr;
align-content: center;
justify-content: center;
}
#videoWrapper .video-tagging {
grid-row: 1;
grid-column: 1;
text-align: center;
}
#vid {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
#videoControls {
grid-row: 2;
}
#videoTagControls {
grid-row: 3;
}
#frameText {
background-color: transparent;
border-color: #ccc;
border-width: 1px;
text-decoration: none;
border-style: solid;
}
/*Video area*/
.overlaystyle {
position: absolute;
z-index: 20;
border:0px solid green;
display:none;
}
.frameCanvasStyle {
position: absolute;
z-index: 10;
/* border:1px solid green; */
/* width: auto; */
/* height: 100%; */
}
.selectionZoneStyle {
z-index: 30;
position: absolute;
width: 100%;
height: 100%;
}
.videoStyle {
z-index: 2;
width: 100%;
height: 100%;
background: #333;
background-size: contain;
background-repeat: no-repeat;
background-position: 50% 50%;
box-sizing: border-box;
}
/*playback*/
.playSpeedControl {
background:black;
z-index:20000;
color:white;
position: absolute;
border:.08em solid white;
display: none;
}
.playSpeedValues{
padding:.2em;
color: rgb(191,191,191);
text-align: center;
font-family: Arial;
}
.playSpeedValueSelected{
color: rgb(255,255,255);
}
/*region div and label*/
.regionLabel{
background:rgb(59,56,56);
position:absolute;
z-index:50000;
color:white;
font-family: "Arial";
display: block;
opacity:.90;
width:40px;height:20px;
}
.regionLabelSpan{
padding-left: .3em;
color:rgb(175,171,171);
}
.regionCanvas{
z-index: 100;
background-color: transparent;
position: absolute !important;
/* border: 2px solid; */
box-shadow: inset 0 0 0 2px;
border-radius: 8px;
}
.regionPoint{
border: none;
/*add an x from http://stackoverflow.com/questions/18012420/draw-diagonal-lines-in-div-background-with-css*/
background:
linear-gradient(to top left,
rgba(0,0,0,0) 0%,
rgba(0,0,0,0) calc(50% - 0.8px),
rgba(0,0,0,1) 50%,
rgba(0,0,0,0) calc(50% + 0.8px),
rgba(0,0,0,0) 100%),
linear-gradient(to top right,
rgba(0,0,0,0) 0%,
rgba(0,0,0,0) calc(50% - 0.8px),
rgba(0,0,0,1) 50%,
rgba(0,0,0,0) calc(50% + 0.8px),
rgba(0,0,0,0) 100%);
}
.regionCanvasSelected{
/* border: 2px solid; */
box-shadow: inset 0 0 0 2px;
border-radius: 8px;
background: repeating-linear-gradient(-45deg, rgba(200, 200, 200, 0.1) 10px, rgba(200, 200, 200, 0.1) 20px, rgba(0, 0, 0, 0.2) 20px, rgba(0, 0, 0, 0.2) 30px);
}
.closeRegion{
float:right;
color: rgb(255,0,0);
padding-right: .2em;
}
/*video controls*/
.videoControls {
width:100%;
background-color: black;
margin-top: 0px;
}
.videoControlsTable{
border: 0px solid red;
margin-left:1%;
margin-right:1%;
}
.videoControlCell{
text-align:center;
padding-top: 1em;
padding-bottom: 1em;
}
.seekCell{
padding-left: 1em;
padding-right: 1.5em;
}
.volumeControlCell{
text-align:left;
}
.simpleControl {
width:5%;
}
.longControl{
width:10%;
}
.frameNumber{
width:15%;
}
.textElements{
font-family:arial;
color:white;
text-align: center;
}
/*tagging controls*/
.videoTagControls {
border: rgb(38,38,38,0.25);
background-color: black;
margin-top:.15em;
padding-bottom: .5em;
}
.optionalTags{
float: left;
width: 85%;
margin: 1em 0 0 1.5em;
}
.labelControls {
float:right;
margin: 1em 0 0 0;
width:10%;
}
.lockTag{
padding-left: .4em;
}
.taggingControls{
color:rgb(255,255,255);
font-size: 130%;
cursor:pointer;
text-align: center;
}
.controlOn{
color:rgb(255,255,255);
}
.controlOff{
color:rgb(89,89,89);
}
.tagButtons{
margin-bottom: 10px;
margin-left: 5px;
background : rgb(59,56,56);
color:rgb(127,127,127);
max-width: 200px;
min-width: 20px;
font-family: Arial;
font-size: 15px;
border: 1px solid rgb(64,64,64);
border-radius: 5px;
user-select: none;
}
.tagButtons sup {
user-select: none;
}
.tagOn{
background:rgb(217,217,217);
}
.tagOff{
background:rgb(59,56,56);
}
</style>
</template>