By using this site, you agree to have cookies stored on your device, strictly for functional purposes, such as storing your session and preferences.

Dismiss

 via_demo.html

View raw Download
text/html • 612.46 kiB
HTML document, ASCII text, with very long lines (738)
        
            
1
<!DOCTYPE html>
2
<html lang="en">
3
<head>
4
<meta charset="UTF-8">
5
<!--
6
VGG Image Annotator (via)
7
www.robots.ox.ac.uk/~vgg/software/via/
8
9
Copyright (c) 2016-2018, Abhishek Dutta, Visual Geometry Group, Oxford University and VIA Contributors..
10
All rights reserved.
11
12
Redistribution and use in source and binary forms, with or without
13
modification, are permitted provided that the following conditions are met:
14
15
Redistributions of source code must retain the above copyright notice, this
16
list of conditions and the following disclaimer.
17
Redistributions in binary form must reproduce the above copyright notice,
18
this list of conditions and the following disclaimer in the documentation
19
and/or other materials provided with the distribution.
20
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
24
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30
POSSIBILITY OF SUCH DAMAGE.
31
-->
32
<title>VGG Image Annotator</title>
33
<meta name="author" content="Abhishek Dutta">
34
<meta name="description" content="VIA is a standalone image annotator application packaged as a single HTML file (< 400 KB) that runs on most modern web browsers.">
35
36
<!--
37
Development of VIA is supported by the EPSRC programme grant
38
Seebibyte: Visual Search for the Era of Big Data (EP/M013774/1).
39
Using Google Analytics, we record the usage of this application.
40
This data is used in reporting of this programme grant.
41
42
If you do not wish to share this data, you can safely remove the
43
javascript code below.
44
-->
45
<script type="text/javascript">
46
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
47
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
48
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
49
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
50
ga('create', 'UA-20555581-2', 'auto');
51
ga('set', 'page', 'via-2.0.0.html');
52
ga('send', 'pageview');
53
</script>
54
55
<!-- START: Contents of file: via.css-->
56
<style>
57
/*
58
CSS style definitions for VIA 2.x.y
59
http://www.robots.ox.ac.uk/~vgg/software/via/
60
Author: Abhishek Dutta <adutta@robots.ox.ac.uk>
61
Date: moved from index.html to via.css on 13 June 2019
62
*/
63
64
body { min-width:200px; padding:0; margin:0; font-family:sans-serif; }
65
* { box-sizing: border-box; }
66
input[type=text] { border:1px solid #cccccc; margin:0.6rem 0; padding:0.2rem 0.4rem; }
67
a { text-decoration:none; }
68
textarea { border:1px solid #cccccc; margin:0.6rem 0; padding:0.2rem 0.4rem; }
69
70
/* Top panel : #navbar, #toolbar */
71
.top_panel { font-size:0.9rem; display:block; background-color:#212121; color:#ffffff; z-index:1001; margin-bottom:1rem;}
72
73
/* Navigation menu bar that appear at the top */
74
.menubar { display:inline-block; height:1.8rem; } /* height needed to avoid extra bottom border */
75
.menubar a:link { color:#eeeeee; text-decoration:none; }
76
.menubar a:visited { color:#eeeeee; text-decoration:none; }
77
.menubar ul { display:block; padding:0; margin:0; }
78
.menubar li { display:inline; float:left; padding:0.45rem 1rem; }
79
.menubar li:hover { background-color:#616161; cursor:default; }
80
81
.menubar li ul { display:none; background-color:#212121; border:1px solid #616161; min-width:10rem; position:absolute; z-index:100; margin:0.4rem -1rem;}
82
.menubar li ul li { display:block; float:none; color:#eeeeee; margin:0; padding:0.6rem 1rem; }
83
.menubar li ul li:hover { cursor:pointer; }
84
.menubar li ul li.submenu_divider { margin:0 0.4rem; padding:0; height:1px; border-bottom:1px solid #424242; }
85
.menubar li:hover ul { display:block; }
86
87
/* toolbar containing small icons for tools */
88
.toolbar { display:inline-block; margin-left:1rem; }
89
.toolbar svg { fill:white; margin: 0.2rem 0.1rem; height:1.2rem; -moz-user-select:none; -webkit-user-select:none; -ms-user-select:none;}
90
.toolbar svg:hover { fill:yellow; cursor:pointer; }
91
.toolbar svg:active { fill:white; }
92
.toolbar ul { display:inline-block; padding:0.2rem; margin:0; }
93
.toolbar li { display:inline; float:left; padding:0rem 0.3rem; border:1px solid white;} /* main menu items */
94
.toolbar li:hover { color:red; cursor:pointer; }
95
96
/* Middle panel: containing #image_panel, #leftsidebar */
97
.middle_panel { display:table; table-layout:fixed; width:100%; z-index:1; padding:0;}
98
#leftsidebar { display:none; z-index:10; vertical-align:top;}
99
#display_area { display:table-cell; width:100%; z-index:1; margin:0; padding-left:1em; vertical-align:top; }
100
/* layers of canvas */
101
#image_panel { position:relative; outline:none; }
102
#image_panel img { visibility:hidden; opacity:0; position:absolute; top:0px; left:0px; width:100%; height:100%; outline:none; }
103
#image_panel canvas { position:absolute; top:0px; left:0px; outline:none;}
104
#image_panel .visible { visibility:visible !important; opacity:1 !important; }
105
#image_panel label>img { visibility:visible; opacity:1; position:relative; width:auto; height:4em; outline:none; }
106
107
/* image buffer
108
#image_panel .fadein { visibility:visible; opacity:1; transition: visibility 0s linear 0s, opacity 300ms; }
109
#image_panel .fadeout { visibility:hidden; opacity:0; transition: visibility 0s linear 300ms, opacity 300ms; }
110
*/
111
112
/* image grid view */
113
#image_grid_panel { position:relative; margin:0; padding:0; width:100%; }
114
#image_grid_panel #image_grid_toolbar { display:block; font-size:small; padding:0.5rem 0;}
115
#image_grid_panel #image_grid_toolbar select { font-size:small; }
116
#image_grid_panel #image_grid_toolbar .tool { display:inline; margin:0 0.5rem;}
117
#image_grid_panel #image_grid_group_panel { font-size:small; }
118
#image_grid_panel #image_grid_group_panel select { font-size:small; }
119
#image_grid_panel #image_grid_group_panel .image_grid_group_toolbar { display:inline; margin-left: 2rem;}
120
#image_grid_panel #image_grid_group_panel .image_grid_group_toolbar select { margin:0 0.2rem; padding:0; font-size:small;}
121
122
#image_grid_panel #image_grid_nav { display:inline; font-size:small; padding-left:0.5rem; margin-top:0.2rem; }
123
#image_grid_panel #image_grid_nav span { margin: 0 0.2rem; }
124
#image_grid_panel #image_grid_content { position:relative; overflow:hidden; margin:0; padding:0; outline:none; }
125
#image_grid_panel #image_grid_content #image_grid_content_img img { margin:0.3em; padding:0; border:0.2em solid #ffffff; outline:0.1em solid #0066ff;}
126
#image_grid_panel #image_grid_content #image_grid_content_img .not_sel { opacity:0.6; outline:none; }
127
#image_grid_panel #image_grid_content #image_grid_content_img { position:absolute; top:0; left:0; width:100%; height:100%; }
128
#image_grid_panel #image_grid_content #image_grid_content_rshape { position:absolute; top:0; left:0; width:100%; height:100%; pointer-events:none; }
129
#image_grid_panel #image_grid_content img { float:left; margin:0; }
130
131
#leftsidebar_collapse_panel { display:none; position:relative; z-index:10; vertical-align:top; }
132
#leftsidebar_show_button { font-size:large; margin-left:0.1rem; }
133
#leftsidebar_show_button:hover { color:red; cursor: pointer; }
134
135
/* Left sidebar accordion */
136
button.leftsidebar_accordion { font-size:large; background-color:#f2f2f2; cursor:pointer; padding:0.5em 0.5em; width:100%; text-align:left; border:0; outline:none; }
137
button.leftsidebar_accordion:focus { outline: none; }
138
button.leftsidebar_accordion.active, button.leftsidebar_accordion:hover { background-color: #e6e6e6; }
139
button.leftsidebar_accordion:after { content:'\02795'; color:#4d4d4d; float:right; }
140
button.leftsidebar_accordion.active:after { content: '\2796'; }
141
.leftsidebar_accordion_panel { display:none; padding:0 0.5em; font-size:small; border-right:2px solid #f2f2f2; border-bottom:2px solid #f2f2f2; }
142
.leftsidebar_accordion_panel.show { display:block; }
143
144
/* Keyboard shortcut panel */
145
.leftsidebar_accordion_panel table { border-collapse:collapse; }
146
.leftsidebar_accordion_panel td { border:1px solid #f2f2f2; padding:0.2rem 0.4rem; }
147
148
/* buttons */
149
.button_panel { display:inline-block; width:100%; margin:0.2rem 0; }
150
.button_panel .text_button, .text_button { color: #0000ff; padding: 0.2rem 0.2rem; -moz-user-select:none; -webkit-user-select:none; -ms-user-select:none; }
151
.button_panel .flush_right { float:right; }
152
.button_panel .text_button:hover, .text_button:hover { cursor:pointer; }
153
.button_panel .text_button:active, .text_button:active { color: #000000; }
154
.button_panel .active { border-bottom:1px solid black; }
155
.button_panel .button { display:inline-block; padding:0.35rem 0.5rem; margin:0 0.05rem; cursor:pointer; background-color:#cccccc; border-radius:0.2rem; -moz-user-select:none; -webkit-user-select:none; -ms-user-select:none; }
156
.button_panel .button:hover { background-color:black; color:white; }
157
158
/* Attributes properties: name, description, type, ... */
159
#attribute_properties { display:table; width:100%; border-collapse:collapse; margin:1rem 0; border:1px solid #cccccc; }
160
#attribute_properties .property { display:table-row;}
161
#attribute_properties .property span { display:table-cell; padding: 0.2rem 0.4rem; }
162
#attribute_properties .property span input { width: 100%; border:1px solid #cccccc; margin: 0;}
163
#attribute_properties .property span input:focus { border:1px solid black; }
164
#attribute_properties .property span select { width: 100%; border:1px solid #cccccc; margin: 0;}
165
166
/* Attributes options: options for attribute type={checkbox,radio,...} */
167
#attribute_options { display:table; width:100%; border-collapse:collapse; margin:1rem 0; border:1px solid #cccccc; table-layout:fixed; }
168
#attribute_options .new_option_id_entry { display:inline-block; padding:1rem 0.2rem; }
169
#attribute_options .new_option_id_entry input {border:none; border-bottom:1px solid #cccccc; margin: 0; font-size: 0.8rem;}
170
#attribute_options .property { display:table-row;}
171
#attribute_options .property span { display:table-cell; padding: 0.2rem 0.2rem; font-weight:bold; }
172
#attribute_options .property input { display:table-cell; width:94%; border:none; border-bottom:1px solid #cccccc; margin: 0; font-size: 0.8rem;}
173
#attribute_options .property input:focus { border-bottom:1px solid #000000; background-color:#f2f2f2; color:#000000; }
174
#attribute_options .property span input[type=checkbox] { vertical-align:middle; }
175
#attribute_options .property span input[type=radio] { vertical-align:middle; }
176
177
/* overlay panel used to gather user inputs before invoking a function using invoke_with_user_inputs() */
178
#user_input_panel { position:fixed; display:none; width:100%; height:100%; top:0; left:0; right:0; bottom:0; background-color: rgba(0,0,0,0.6); z-index:1002; }
179
#user_input_panel .content { position:fixed; background-color:white; top:50%; left:50%; transform:translate(-50%,-50%); -webkit-transform: translate(-50%, -50%); -moz-transform: translate(-50%, -50%); -o-transform: translate(-50%, -50%); -ms-transform: translate(-50%, -50%); padding:2rem 4rem;}
180
#user_input_panel .content .title { font-size:large; font-weight:bold; }
181
#user_input_panel .content .user_inputs { display:table; width:100%; border-collapse:collapse;}
182
#user_input_panel .content .user_inputs .row { display:table-row; }
183
#user_input_panel .content .user_inputs .cell { display:table-cell; padding:1rem 0.5rem; vertical-align:middle; border:1px solid #f2f2f2; }
184
#user_input_panel .content .user_confirm { display:table; width:100%; text-align:center; margin:2rem 0;}
185
#user_input_panel .content .user_confirm .ok { display:table-cell; width:48%; }
186
#user_input_panel .content .user_confirm .cancel { display:table-cell; width:48%; }
187
#user_input_panel .content .warning { color:red; }
188
189
/* Attribute editor */
190
#annotation_editor_panel { position:fixed; display:none; width:100%; left:0; bottom:0; background-color:white; border-top:2px solid #cccccc; padding:0.2em 1em; overflow:auto; z-index:1001; box-shadow: 0 0 1em #cccccc;}
191
#annotation_editor { display:table; margin-bottom:3em; border-collapse:collapse; font-size:inherit; position: absolute; background-color:white; }
192
#annotation_editor .row { display:table-row; }
193
#annotation_editor .highlight .col { background-color:#e6e6e6;}
194
195
#annotation_editor .col { display:table-cell; padding:0.4em 0.6em; border:1px solid #000000; vertical-align:middle; font-size:inherit; }
196
#annotation_editor .header { font-weight:bold; }
197
#annotation_editor .id { font-weight:bold; }
198
#annotation_editor .col input[type=checkbox] { vertical-align:middle; }
199
#annotation_editor .col input[type=radio] { vertical-align:middle; font-size:inherit; }
200
#annotation_editor .col label { vertical-align:middle; font-size:inherit; }
201
#annotation_editor .col textarea { border:0.1em solid #cccccc; padding:0; margin:0; font-size:inherit; background-color:#ffffff; }
202
#annotation_editor .col textarea:focus { border:0.1em dashed #cccccc; }
203
#annotation_editor .col span { display:block; }
204
#annotation_editor .col horizontal_container { display:inline-block; }
205
206
#annotation_editor .col .img_options { display:inline; }
207
#annotation_editor .col .img_options .imrow { display:block; }
208
#annotation_editor .col .img_options span { display:inline-block; margin: 0.2rem 0.4rem;}
209
#annotation_editor .col .img_options span img { height:4em; }
210
#annotation_editor .col .img_options p { margin:0; padding:0; font-size:inherit; }
211
#annotation_editor .col .img_options input[type=radio] { display:none; }
212
#annotation_editor .col .img_options input[type=radio] + label { display:block; cursor:pointer; text-align:center;}
213
#annotation_editor .col .img_options input[type=radio]:checked + label { border: 0.1em solid black; background-color:#cccccc; cursor:default; font-size:inherit; }
214
215
#project_info_panel { display:table; border-collapse:collapse; font-size:0.8rem; }
216
#project_info_panel .row { display:table-row; }
217
#project_info_panel .col { display:table-cell; padding:0.4rem 0.1rem; border:none; }
218
#project_info_panel .col input[type=text] { font-size:0.8rem; border:none; border-bottom:1px solid #cccccc; margin: 0; width:100%;}
219
#project_info_panel .col input:focus { border-bottom:1px solid #000000; background-color:#f2f2f2; color:#000000; }
220
221
/* Region shape selection panel inside leftsidebar */
222
ul.region_shape { font-size:xx-large; list-style-type:none; overflow:hidden; padding:0.4em 0; margin:0; }
223
ul.region_shape li { float:left; padding:0 0.2em; fill:#ffffff; stroke:#000000; }
224
ul.region_shape li:hover { cursor:pointer; fill:#ffffff; stroke:#ff0000; }
225
ul.region_shape .selected { fill:#ffffff; stroke:#ff0000; }
226
227
/* cursor coordinates inside region shape selection panel in leftsidebar */
228
#region_info { font-size:0.8em; margin-bottom:0.4em; }
229
230
/* Loaded image list shown in leftsidebar panel */
231
#img_fn_list { display:none; font-size:small; overflow:scroll; min-height:10rem; max-height:25rem; }
232
#img_fn_list ul { position:relative; line-height:1.3em; margin:0; padding:0; list-style-type:none;}
233
#img_fn_list li { white-space:nowrap; display:block; padding:0 0.4rem; }
234
#img_fn_list li:hover { background-color:#d5e5ff; cursor:pointer; }
235
#img_fn_list .error { color:red; }
236
#img_fn_list .sel { border-left:0.2rem solid black !important; font-weight:bold; }
237
#img_fn_list .buffered { border-left:0.2rem solid #cccccc; }
238
239
#message_panel { display:block; width:100%; position:fixed; bottom:0px; z-index:9999; text-align:center; }
240
#message_panel .content { display:inline; margin:auto; background-color:#000000; color:#ffff00; font-size:small; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; line-height:2rem; padding: 0.5rem 2rem;}
241
242
.text_panel { display:none; margin:auto; font-size:medium; line-height:1.3em; margin: 0; max-width:700px; }
243
.text_panel li { margin:1em 0; text-align:left; }
244
.text_panel p { text-align:left; }
245
246
.svg_button:hover { cursor:pointer; }
247
248
/* Loading spinbar */
249
.loading_spinbox { display:inline-block; border:0.4em solid #cccccc; border-radius:50%; border-top:0.4em solid black; border-bottom:0.4em solid black;-webkit-animation:spin 2s linear infinite; animation:spin 2s linear infinite; }
250
@-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg); } 100% { -webkit-transform: rotate(360deg); } }
251
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
252
253
#invisible_file_input { width:0.1px; height:0.1px; opacity:0; overflow:hidden; position:absolute; z-index:-1; }
254
255
.display_none { display:none !important; }
256
.display_block { display:block !important; }
257
258
/* project settings */
259
#settings_panel { font-size:1rem; border-collapse:collapse; width:95%; }
260
#settings_panel a { border: 1px solid #f2f2f2; }
261
#settings_panel .row { display:table-row; border-bottom:1px solid #f2f2f2; }
262
#settings_panel .variable { display:table-cell; width:60%; padding:0.5rem 0.5rem; }
263
#settings_panel .variable div { display:block; }
264
#settings_panel .variable .name { }
265
#settings_panel .variable .desc { font-size:0.7em; color:#808080; padding:0.2rem 0rem; }
266
#settings_panel .value { display:table-cell; vertical-align:middle; padding-left:1rem; }
267
268
/* page {about, help, file not found, etc.} */
269
.display_area_content { } /* this class is used to clear the display area content */
270
.narrow_page_content li { font-size:0.9rem; margin: 0.4rem 0; }
271
.narrow_page_content { width:60%; }
272
273
.force_small_font { font-size:small !important; }
274
.key { font-family:monospace; padding:1px 6px; background:linear-gradient(to bottom,#f0f0f0,#fcfcfc);; border:1px solid #e0e0e0; white-space:nowrap; color:#303030; border-bottom-width:2px; border-radius:3px; font-size:1.2em; }
275
</style>
276
<!-- END: Contents of file: via.css-->
277
</head>
278
279
<body onload="_via_init()" onresize="_via_update_ui_components()">
280
<!--
281
SVG icon set definitions
282
Material icons downloaded from https://material.io/icons
283
-->
284
<svg style="display:none;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
285
<defs>
286
<symbol id="via_logo">
287
<!-- Logo designed by Abhishek Dutta <adutta@robots.ox.ac.uk>, May 2018 -->
288
<title>VGG Image Annotator Logo</title>
289
<rect width="400" height="160" x="0" y="0" fill="#212121"></rect>
290
291
<text x="56" y="130" font-family="Serif" font-size="100" fill="white">V</text>
292
<text x="180" y="130" font-family="Serif" font-size="100" fill="white">I</text>
293
<text x="270" y="130" font-family="Serif" font-size="100" fill="white">A</text>
294
295
<rect width="80" height="100" x="52" y="40" stroke="yellow" fill="none" stroke-width="2"></rect>
296
<text x="72" y="30" font-family="'Comic Sans MS', cursive, sans-serif" font-size="18" fill="yellow">VGG</text>
297
298
<rect width="50" height="100" x="175" y="45" stroke="yellow" fill="none" stroke-width="2"></rect>
299
<text x="175" y="35" font-family="'Comic Sans MS', cursive, sans-serif" font-size="18" fill="yellow">Image</text>
300
301
<rect width="80" height="100" x="265" y="40" stroke="yellow" fill="none" stroke-width="2"></rect>
302
<text x="265" y="30" font-family="'Comic Sans MS', cursive, sans-serif" font-size="18" fill="yellow">Annotator</text>
303
</symbol>
304
<symbol id="shape_rectangle">
305
<title>Rectangular region shape</title>
306
<rect width="20" height="12" x="6" y="10" stroke-width="2"></rect>
307
</symbol>
308
<symbol id="shape_circle">
309
<title>Circular region shape</title>
310
<circle r="10" cx="16" cy="16" stroke-width="2"></circle>
311
</symbol>
312
<symbol id="shape_ellipse">
313
<title>Elliptical region shape</title>
314
<ellipse rx="12" ry="8" cx="16" cy="16" stroke-width="2"></ellipse>
315
</symbol>
316
<symbol id="shape_polygon">
317
<title>Polygon region shape</title>
318
<path d="M 15.25,2.2372 3.625,11.6122 6,29.9872 l 20.75,-9.625 2.375,-14.75 z" stroke-width="2"></path>
319
</symbol>
320
<symbol id="shape_point">
321
<title>Point region shape</title>
322
<circle r="3" cx="16" cy="16" stroke-width="2"></circle>
323
</symbol>
324
<symbol id="shape_polyline">
325
<title>Polyline region shape</title>
326
<path d="M 2,12 10,24 18,12 24,18" stroke-width="2"></path>
327
<circle r="1" cx="2" cy="12" stroke-width="2"></circle>
328
<circle r="1" cx="10" cy="24" stroke-width="2"></circle>
329
<circle r="1" cx="18" cy="12" stroke-width="2"></circle>
330
<circle r="1" cx="24" cy="18" stroke-width="2"></circle>
331
</symbol>
332
333
<!-- Material icons downloaded from https://material.io/icons -->
334
<symbol id="icon_settings">
335
<path d="M19.43 12.98c.04-.32.07-.64.07-.98s-.03-.66-.07-.98l2.11-1.65c.19-.15.24-.42.12-.64l-2-3.46c-.12-.22-.39-.3-.61-.22l-2.49 1c-.52-.4-1.08-.73-1.69-.98l-.38-2.65C14.46 2.18 14.25 2 14 2h-4c-.25 0-.46.18-.49.42l-.38 2.65c-.61.25-1.17.59-1.69.98l-2.49-1c-.23-.09-.49 0-.61.22l-2 3.46c-.13.22-.07.49.12.64l2.11 1.65c-.04.32-.07.65-.07.98s.03.66.07.98l-2.11 1.65c-.19.15-.24.42-.12.64l2 3.46c.12.22.39.3.61.22l2.49-1c.52.4 1.08.73 1.69.98l.38 2.65c.03.24.24.42.49.42h4c.25 0 .46-.18.49-.42l.38-2.65c.61-.25 1.17-.59 1.69-.98l2.49 1c.23.09.49 0 .61-.22l2-3.46c.12-.22.07-.49-.12-.64l-2.11-1.65zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"></path>
336
</symbol>
337
<symbol id="icon_save">
338
<path d="M17 3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V7l-4-4zm-5 16c-1.66 0-3-1.34-3-3s1.34-3 3-3 3 1.34 3 3-1.34 3-3 3zm3-10H5V5h10v4z"></path>
339
</symbol>
340
<symbol id="icon_open">
341
<path d="M20 6h-8l-2-2H4c-1.1 0-1.99.9-1.99 2L2 18c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V8c0-1.1-.9-2-2-2zm0 12H4V8h16v10z"></path>
342
</symbol>
343
<symbol id="icon_gridon">
344
<path d="M20 2H4c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h16c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zM8 20H4v-4h4v4zm0-6H4v-4h4v4zm0-6H4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4zm6 12h-4v-4h4v4zm0-6h-4v-4h4v4zm0-6h-4V4h4v4z"></path>
345
</symbol>
346
<symbol id="icon_gridoff">
347
<path d="M8 4v1.45l2 2V4h4v4h-3.45l2 2H14v1.45l2 2V10h4v4h-3.45l2 2H20v1.45l2 2V4c0-1.1-.9-2-2-2H4.55l2 2H8zm8 0h4v4h-4V4zM1.27 1.27L0 2.55l2 2V20c0 1.1.9 2 2 2h15.46l2 2 1.27-1.27L1.27 1.27zM10 12.55L11.45 14H10v-1.45zm-6-6L5.45 8H4V6.55zM8 20H4v-4h4v4zm0-6H4v-4h3.45l.55.55V14zm6 6h-4v-4h3.45l.55.54V20zm2 0v-1.46L17.46 20H16z"></path>
348
</symbol>
349
<symbol id="icon_next">
350
<path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
351
</symbol>
352
<symbol id="icon_prev">
353
<path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path>
354
</symbol>
355
<symbol id="icon_list">
356
<path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"></path>
357
</symbol>
358
<symbol id="icon_zoomin">
359
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"></path>
360
<path d="M12 10h-2v2H9v-2H7V9h2V7h1v2h2v1z"/>
361
</symbol>
362
<symbol id="icon_zoomout">
363
<path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14zM7 9h5v1H7z"></path>
364
</symbol>
365
<symbol id="icon_copy">
366
<path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"></path>
367
</symbol>
368
<symbol id="icon_paste">
369
<path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"></path>
370
</symbol>
371
<symbol id="icon_pasten">
372
<path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"></path>
373
<text x="8" y="18">n</text>
374
</symbol>
375
<symbol id="icon_pasteundo">
376
<path d="M19 2h-4.18C14.4.84 13.3 0 12 0c-1.3 0-2.4.84-2.82 2H5c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-7 0c.55 0 1 .45 1 1s-.45 1-1 1-1-.45-1-1 .45-1 1-1zm7 18H5V4h2v3h10V4h2v16z"></path>
377
<text x="8" y="18">x</text>
378
</symbol>
379
<symbol id="icon_selectall">
380
<path d="M3 5h2V3c-1.1 0-2 .9-2 2zm0 8h2v-2H3v2zm4 8h2v-2H7v2zM3 9h2V7H3v2zm10-6h-2v2h2V3zm6 0v2h2c0-1.1-.9-2-2-2zM5 21v-2H3c0 1.1.9 2 2 2zm-2-4h2v-2H3v2zM9 3H7v2h2V3zm2 18h2v-2h-2v2zm8-8h2v-2h-2v2zm0 8c1.1 0 2-.9 2-2h-2v2zm0-12h2V7h-2v2zm0 8h2v-2h-2v2zm-4 4h2v-2h-2v2zm0-16h2V3h-2v2zM7 17h10V7H7v10zm2-8h6v6H9V9z"></path>
381
</symbol>
382
<symbol id="icon_close">
383
<path d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"></path>
384
</symbol>
385
<symbol id="icon_insertcomment">
386
<path d="M20 2H4c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h14l4 4V4c0-1.1-.9-2-2-2zm-2 12H6v-2h12v2zm0-3H6V9h12v2zm0-3H6V6h12v2z"></path>
387
</symbol>
388
<symbol id="icon_checkbox">
389
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"></path>
390
</symbol>
391
<symbol id="icon_fileupload">
392
<path d="M9 16h6v-6h4l-7-7-7 7h4zm-4 2h14v2H5z"></path>
393
</symbol>
394
<symbol id="icon_filedownload">
395
<path d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"></path>
396
</symbol>
397
</defs>
398
</svg>
399
400
<!-- used by invoke_with_user_inputs() to gather user inputs -->
401
<div id="user_input_panel"></div>
402
403
<!-- to show status messages -->
404
<div id="message_panel">
405
<div id="message_panel_content" class="content"></div>
406
</div>
407
408
<!-- spreadsheet like editor for annotations -->
409
<div id="annotation_editor_panel">
410
<div class="button_panel">
411
<span class="text_button" onclick="edit_region_metadata_in_annotation_editor()" id="button_edit_region_metadata" title="Manual annotations of regions">Region Annotations</span>
412
<span class="text_button" onclick="edit_file_metadata_in_annotation_editor()" id="button_edit_file_metadata" title="Manual annotations of a file">File Annotations</span>
413
414
<span class="button" style="float:right;margin-right:0.2rem;" onclick="annotation_editor_toggle_all_regions_editor()" title="Close this window of annotation editor">&times;</span>
415
<span class="button" style="float:right;margin-right:0.2rem;" onclick="annotation_editor_increase_panel_height()" title="Increase the height of this panel">&uarr;</span>
416
<span class="button" style="float:right;margin-right:0.2rem;" onclick="annotation_editor_decrease_panel_height()" title="Decrease the height of this panel">&darr;</span>
417
<span class="button" style="float:right;margin-right:0.2rem;" onclick="annotation_editor_increase_content_size()" title="Increase size of contents in annotation editor">&plus;</span>
418
<span class="button" style="float:right;margin-right:0.2rem;" onclick="annotation_editor_decrease_content_size()" title="Decrease size of contents in annotation editor">&minus;</span>
419
</div>
420
<!-- here, a child div with id="annotation_editor" is added by javascript -->
421
</div>
422
423
<div class="top_panel" id="ui_top_panel">
424
<!-- menu bar -->
425
<div class="menubar">
426
<ul>
427
<li onclick="show_home_panel()" style="cursor:pointer;">Home</li>
428
<li>Project
429
<ul>
430
<li onclick="project_open_select_project_file()" title="Load a VIA project (from a JSON file)">Load</li>
431
<li onclick="project_save_with_confirm()" title="Save this VIA project (as a JSON file)">Save</li>
432
<li onclick="settings_panel_toggle()" title="Show/edit project settings">Settings</li>
433
<li class="submenu_divider"></li>
434
<li onclick="sel_local_images()" title="Add images locally stored in this computer">Add local files</li>
435
<li onclick="project_file_add_url_with_input()" title="Add images from a web URL (e.g. http://www.robots.ox.ac.uk/~vgg/software/via/images/swan.jpg)">Add files from URL</li>
436
<li onclick="project_file_add_abs_path_with_input()" title="Add images using absolute path of file (e.g. /home/abhishek/image1.jpg)">Add file using absolute path</li>
437
<li onclick="sel_local_data_file('files_url')" title="Add images from a list of web url or absolute path stored in a text file (one url or path per line)">Add url or path from text file</li>
438
<li onclick="project_file_remove_with_confirm()" title="Remove selected file (i.e. file currently being shown)">Remove file</li>
439
<li class="submenu_divider"></li>
440
<li onclick="sel_local_data_file('attributes')" title="Import region and file attributes from a JSON file">Import region/file attributes</li>
441
<li onclick="project_save_attributes()" title="Export region and file attributes to a JSON file">Export region/file attributes</li>
442
</ul>
443
</li>
444
445
<li>Annotation
446
<ul>
447
<li onclick="download_all_region_data('csv')" title="Export annotations to a CSV file">Export Annotations (as csv)</li>
448
<li onclick="download_all_region_data('json')" title="Export annotaitons to a JSON file">Export Annotations (as json)</li>
449
<li onclick="download_all_region_data('coco', 'json')" title="Export annotaitons to COCO (http://cocodataset.org) format">Export Annotations (COCO format)</li>
450
<li onclick="" class="submenu_divider"></li>
451
<li onclick="sel_local_data_file('annotations')" title="Import annotations from a CSV file">Import Annotations (from csv)</li>
452
<li onclick="sel_local_data_file('annotations')" title="Import annotations from a JSON file">Import Annotations (from json)</li>
453
<li onclick="sel_local_data_file('annotations_coco')" title="Import annotations from a COCO (http://cocodataset.org) formatted JSON file">Import Annotations (COCO format)</li>
454
455
<li class="submenu_divider"></li>
456
<li onclick="show_annotation_data()" title="Show a preview of annotations (opens in a new browser windows)">Preview Annotations</li>
457
<li onclick="download_as_image()" title="Download an image containing the annotations">Download as Image</li>
458
</ul>
459
</li>
460
461
<li>View
462
<ul>
463
<li onclick="image_grid_toggle()" title="Toggle between single image view and image grid view">Toggle image grid view</li>
464
<li onclick="leftsidebar_toggle()" title="Show or hide the sidebar shown in left hand side">Toggle left sidebar</li>
465
<li onclick="toggle_img_fn_list_visibility()" title="Show or hide a panel to update annotations corresponding to file and region">Toggle image filename list</li>
466
<li onclick="toggle_message_visibility()" title="Show or hide status messages that appears at the bottom of the page">Toggle status message visibility</li>
467
<li class="submenu_divider"></li>
468
<li onclick="toggle_attributes_editor()" title="Show or hide a panel to update file and region attributes">Toggle attributes editor</li>
469
<li onclick="annotation_editor_toggle_all_regions_editor()" title="Show or hide a panel to update annotations corresponding to file and region">Toggle annotation editor (Space)</li>
470
<li class="submenu_divider"></li>
471
<li onclick="toggle_region_boundary_visibility()" title="Show or hide the region boundaries">Show/hide region boundaries (b)</li>
472
<li onclick="toggle_region_id_visibility()" title="Show or hide the region id labels">Show/hide region labels (l)</li>
473
<li onclick="toggle_region_info_visibility()" title="Show or hide the image coordinates">Show/hide region info.</li>
474
</ul>
475
</li>
476
477
<li>Help
478
<ul>
479
<li onclick="set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_GETTING_STARTED)" title="Show a guide to get started with this application">Getting Started</li>
480
<li title="Visit the project page for this application"><a href="http://www.robots.ox.ac.uk/~vgg/software/via/" target="_blank">VGG Project Page</a></li>
481
<li onclick="" title="Report an issue to the developers of this application (requires an account at gitlab.com)"><a href="https://gitlab.com/vgg/via/issues" target="_blank">Report issues</a></li>
482
<li class="submenu_divider"></li>
483
<li><a target="_blank" href="https://gitlab.com/vgg/via/blob/master/Contributors.md" title="List of people who have contributed towards the development of VIA">Contributors</a></li>
484
<li onclick="set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_LICENSE)" title="View license of this application">License</li>
485
<li onclick="set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_ABOUT)" title="Show more details about this application">About VIA</li>
486
</ul>
487
</li>
488
</ul>
489
</div> <!-- end of menubar -->
490
491
<!-- Shortcut toolbar -->
492
<div class="toolbar">
493
<svg onclick="project_open_select_project_file()" viewbox="0 0 24 24"><use xlink:href="#icon_open"></use><title>Open Project</title></svg>
494
<svg onclick="project_save_with_confirm()" viewbox="0 0 24 24"><use xlink:href="#icon_save"></use><title>Save Project</title></svg>
495
<svg onclick="settings_panel_toggle()" viewbox="0 0 24 24"><use xlink:href="#icon_settings"></use><title>Update Project Settings</title></svg>
496
<!--
497
<svg onclick="" viewbox="0 0 24 24"><use xlink:href="#icon_checkbox"></use><title>Locate Files</title></svg>
498
-->
499
500
<svg onclick="sel_local_data_file('annotations')" style="margin-left:1rem;" viewbox="0 0 24 24"><use xlink:href="#icon_fileupload"></use><title>Import Annotations from CSV</title></svg>
501
<svg onclick="download_all_region_data('csv')" viewbox="0 0 24 24"><use xlink:href="#icon_filedownload"></use><title>Download Annotations as CSV</title></svg>
502
503
<svg onclick="image_grid_toggle()" id="toolbar_image_grid_toggle" style="margin-left:1rem;" viewbox="0 0 24 24"><use xlink:href="#icon_gridon"></use><title>Switch to Image Grid View</title></svg>
504
<svg onclick="annotation_editor_toggle_all_regions_editor()" viewbox="0 0 24 24"><use xlink:href="#icon_insertcomment"></use><title>Toggle Annotation Editor</title></svg>
505
506
<svg onclick="move_to_prev_image()" style="margin-left:1rem;" viewbox="0 0 24 24"><use xlink:href="#icon_prev"></use><title>Previous</title></svg>
507
<svg onclick="toggle_img_fn_list_visibility()" viewbox="0 0 24 24"><use xlink:href="#icon_list"></use><title>Toggle Filename List</title></svg>
508
<svg onclick="move_to_next_image()" viewbox="0 0 24 24"><use xlink:href="#icon_next"></use><title>Next</title></svg>
509
510
<svg onclick="zoom_in()" style="margin-left:1rem;" viewbox="0 0 24 24"><use xlink:href="#icon_zoomin"></use><title>Zoom In</title></svg>
511
<svg onclick="zoom_out()" viewbox="0 0 24 24"><use xlink:href="#icon_zoomout"></use><title>Zoom Out</title></svg>
512
513
<svg onclick="sel_all_regions()" viewbox="0 0 24 24" style="margin-left:1rem;"><use xlink:href="#icon_selectall"></use><title>Select All Regions</title></svg>
514
<svg onclick="copy_sel_regions()" viewbox="0 0 24 24"><use xlink:href="#icon_copy"></use><title>Copy Regions</title></svg>
515
<svg onclick="paste_sel_regions_in_current_image()" viewbox="0 0 24 24"><use xlink:href="#icon_paste"></use><title>Paste Regions</title></svg>
516
<svg onclick="paste_to_multiple_images_with_confirm()" viewbox="0 0 24 24"><use xlink:href="#icon_pasten"></use><title>Paste Region in Multiple Images</title></svg>
517
<svg onclick="del_sel_regions_with_confirm()" viewbox="0 0 24 24"><use xlink:href="#icon_pasteundo"></use><title>Undo Regions Pasted in Multiple Images</title></svg>
518
<svg onclick="del_sel_regions()" viewbox="0 0 24 24"><use xlink:href="#icon_close"></use><title>Delete Region</title></svg>
519
</div>
520
<!-- end of shortcut toolbar -->
521
<input type="file" id="invisible_file_input" name="files[]" style="display:none">
522
</div> <!-- endof #top_panel -->
523
524
<!-- Middle Panel contains a left-sidebar and image display areas -->
525
<div class="middle_panel">
526
<!-- this panel contains a button to shows the left side bar -->
527
<div id="leftsidebar_collapse_panel">
528
<span class="text_button" onclick="leftsidebar_toggle()" title="Show left sidebar">&rtrif;</span>
529
</div>
530
531
<div id="leftsidebar">
532
<div class="leftsidebar_accordion_panel show" style="float:right; border:2px solid #f2f2f2;">
533
<span class="text_button" onclick="leftsidebar_decrease_width()" title="Reduce width of this toolbar panel">&larr;</span>
534
<span class="text_button" onclick="leftsidebar_increase_width()" title="Increase width of this toolbar panel">&rarr;</span>
535
<span class="text_button" onclick="leftsidebar_toggle()" title="Show/hide this toolbar panel">&ltrif;</span>
536
</div>
537
538
<button class="leftsidebar_accordion active">Region Shape</button>
539
<div class="leftsidebar_accordion_panel show">
540
<ul class="region_shape">
541
<li id="region_shape_rect" class="selected" onclick="select_region_shape('rect')" title="Rectangle (Shortcut key 1)"><svg height="32" viewbox="0 0 32 32"><use xlink:href="#shape_rectangle"></use></svg></li>
542
<li id="region_shape_circle" onclick="select_region_shape('circle')" title="Circle (Shortcut key 2)"><svg height="32" viewbox="0 0 32 32"><use xlink:href="#shape_circle"></use></svg></li>
543
<li id="region_shape_ellipse" onclick="select_region_shape('ellipse')" title="Ellipse (Shortcut key 3)"><svg height="32" viewbox="0 0 32 32"><use xlink:href="#shape_ellipse"></use></svg></li>
544
<li id="region_shape_polygon" onclick="select_region_shape('polygon')" title="Polygon (Shortcut key 4)"><svg height="32" viewbox="0 0 32 32"><use xlink:href="#shape_polygon"></use></svg></li>
545
<li id="region_shape_point" onclick="select_region_shape('point')" title="Point (Shortcut key 5)"><svg height="32" viewbox="0 0 32 32"><use xlink:href="#shape_point"></use></svg></li>
546
<li id="region_shape_polyline" onclick="select_region_shape('polyline')" title="Polyline (Shortcut key 6)"><svg height="32" viewbox="0 0 32 32"><use xlink:href="#shape_polyline"></use></svg></li>
547
</ul>
548
<div id="region_info" class="display_none">&nbsp;</div>
549
</div>
550
551
<!-- Project -->
552
<button class="leftsidebar_accordion active" id="project_panel_title">Project</button>
553
<div class="leftsidebar_accordion_panel show" id="img_fn_list_panel">
554
<div id="project_info_panel">
555
<div class="row">
556
<span class="col"><label for="project_name">Name: </label></span>
557
<span class="col"><input type="text" value="" onchange="project_on_name_update(this)" id="project_name" title="VIA project name"></span>
558
</div>
559
</div>
560
<div id="project_tools_panel">
561
<div class="button_panel" style="margin:0.1rem 0;" >
562
<select style="width:48%" id="filelist_preset_filters_list" onchange="img_fn_list_onpresetfilter_select()" title="Filter file list using predefined filters">
563
<option value="all">All files</option>
564
<option value="files_without_region">Show files without regions</option>
565
<option value="files_missing_region_annotations">Show files missing region annotations</option>
566
<option value="files_missing_file_annotations">Show files missing file annotations</option>
567
<option value="files_error_loading">Files that could not be loaded</option>
568
<option value="regex">Regular Expression</option>
569
<option value="files_region_attribute">Filter by Region Attribute</option>
570
</select>
571
<input style="width:50%" type="text" placeholder="regular expression" oninput="img_fn_list_onregex()" id="img_fn_list_regex" title="Filter using regular expression">
572
</div>
573
</div>
574
<div id="img_fn_list"></div>
575
<p>
576
<div class="button_panel">
577
<span class="button" onclick="sel_local_images()" title="Add new file from local disk">Add Files</span>
578
<span class="button" onclick="project_file_add_url_with_input()" title="Add new file using URL">Add URL</span>
579
<span class="button" onclick="project_file_remove_with_confirm()" title="Remove selected file (i.e. file currently being shown) from project">Remove</span>
580
</div>
581
</p>
582
</div>
583
584
<!-- Attributes -->
585
<button class="leftsidebar_accordion" id="attributes_editor_panel_title">Attributes</button>
586
<div class="leftsidebar_accordion_panel" id="attributes_editor_panel">
587
<div class="button_panel" style="padding:1rem 0;">
588
<span class="text_button" onclick="show_region_attributes_update_panel()" id="button_show_region_attributes" title="Show region attributes">Region Attributes</span>
589
<span class="text_button" onclick="show_file_attributes_update_panel()" id="button_show_file_attributes" title="Show file attributes">File Attributes</span>
590
</div>
591
<div id="attributes_update_panel">
592
<div class="button_panel">
593
<input style="width:70%" type="text" placeholder="attribute name" id="user_input_attribute_id" value="">
594
<span id="button_add_new_attribute" class="button" onclick="add_new_attribute_from_user_input()" title="Add new attribute">&plus;</span>
595
<span id="button_del_attribute" class="button" onclick="delete_existing_attribute_with_confirm()" title="Delete existing attribute">&minus;</span>
596
</div>
597
<div class="button_panel" style="margin:0.1rem 0;" >
598
<select style="width:100%" id="attributes_name_list" onchange="update_current_attribute_id(this)" title="List of existing attributes"></select>
599
</div>
600
<div id="attribute_properties"></div>
601
<div id="attribute_options"></div>
602
<p style="text-align:center">
603
<span class="text_button" title="Show a spreadsheet like editor for all manual annotations" onclick="annotation_editor_toggle_all_regions_editor()">Toggle Annotation Editor</span>
604
</p>
605
</div>
606
</div>
607
608
<button class="leftsidebar_accordion">Keyboard Shortcuts</button>
609
<div class="leftsidebar_accordion_panel">
610
<div style="display:block; text-align:center; padding:1rem;">Available only on image focus</div>
611
<table>
612
<tr>
613
<td style="width:8em;"><span class="key">&larr;</span>&nbsp;<span class="key">&uarr;</span>&nbsp;<span class="key">&rarr;</span>&nbsp;<span class="key">&darr;</span></td>
614
<td>Move selected region by 1 px (Shift to jump)</td>
615
</tr>
616
<tr>
617
<td><span class="key">a</span></td>
618
<td>Select all regions</td>
619
</tr>
620
621
<tr>
622
<td><span class="key">c</span></td>
623
<td>Copy selected regions</td>
624
</tr>
625
<tr>
626
<td><span class="key">v</span></td>
627
<td>Paste selected regions</td>
628
</tr>
629
<tr>
630
<td><span class="key">d</span></td>
631
<td>Delete selected regions</td>
632
</tr>
633
<tr>
634
<td><span class="key">Ctrl</span> + Wheel</td>
635
<td>Zoom in/out (mouse cursor is over image)</td>
636
</tr>
637
<tr>
638
<td><span class="key">l</span></td>
639
<td>Toggle region label</td>
640
</tr>
641
<tr>
642
<td><span class="key">b</span></td>
643
<td>Toggle region boundary</td>
644
</tr>
645
<tr>
646
<td><span class="key">Enter</span></td>
647
<td>Finish drawing polyshape</td>
648
</tr>
649
<tr>
650
<td><span class="key">Backspace</span></td>
651
<td>Delete last polyshape vertex</td>
652
</tr>
653
</table>
654
655
<div style="display:block; text-align:center; padding:1rem;">Always Available</div>
656
<table>
657
<tr>
658
<td style="width:8em;"><span class="key">&larr;</span>&nbsp;<span class="key">&rarr;</span></td>
659
<td>Move to next/previous image</td>
660
</tr>
661
<tr>
662
<td><span class="key">+</span>&nbsp;<span class="key">-</span>&nbsp;<span class="key">=</span></td>
663
<td>Zoom in/out/reset</td>
664
</tr>
665
<tr>
666
<td><span class="key">&uarr;</span></td>
667
<td>Update region label</td>
668
</tr>
669
<tr>
670
<td><span class="key">&darr;</span></td>
671
<td>Update region colour</td>
672
</tr>
673
<tr>
674
<td><span class="key">Spacebar</span></td>
675
<td>Toggle annotation editor (Ctrl to toggle on image editor)</td>
676
</tr>
677
<tr>
678
<td><span class="key">Home</span> / <span class="key">h</span></td>
679
<td>Jump to first image</td>
680
</tr>
681
<tr>
682
<td><span class="key">End</span> / <span class="key">e</span></td>
683
<td>Jump to last image</td>
684
</tr>
685
<tr>
686
<td><span class="key">PgUp</span> / <span class="key">u</span></td>
687
<td>Jump several images</td>
688
</tr>
689
<tr>
690
<td><span class="key">PgDown</span> / <span class="key">d</span></td>
691
<td>Jump several images</td>
692
</tr>
693
694
<tr>
695
<td><span class="key">Esc</span></td>
696
<td>Cancel ongoing task</td>
697
</tr>
698
</table>
699
</div>
700
701
</div> <!-- end of leftsidebar -->
702
703
<!-- Main display area: contains image canvas, ... -->
704
<div id="display_area">
705
<div id="image_panel" class="display_area_content display_none">
706
<!-- buffer images using <img> element will be added here -->
707
708
<!-- @todo: in future versions, this canvas will be replaced by a <svg> element -->
709
<canvas id="region_canvas" width="1" height="1" tabindex="1">Sorry, your browser does not support HTML5 Canvas functionality which is required for this application.</canvas>
710
<!-- here, a child div with id="annotation_editor" is added by javascript -->
711
</div>
712
<div id="image_grid_panel" class="display_area_content display_none">
713
714
<div id="image_grid_group_panel">
715
<span class="tool">Group by&nbsp; <select id="image_grid_toolbar_group_by_select" onchange="image_grid_toolbar_onchange_group_by_select(this)"></select></span>
716
</div>
717
718
<div id="image_grid_toolbar">
719
<span>Selected</span>
720
<span id="image_grid_group_by_sel_img_count">0</span>
721
<span>of</span>
722
<span id="image_grid_group_by_img_count">0</span>
723
<span>images in current group, show</span>
724
725
<span>
726
<select id="image_grid_show_image_policy" onchange="image_grid_onchange_show_image_policy(this)">
727
<option value="all">all images (paginated)</option>
728
<option value="first_mid_last">only first, middle and last image</option>
729
<option value="even_indexed">even indexed images (i.e. 0,2,4,...)</option>
730
<option value="odd_indexed">odd indexed images (i.e. 1,3,5,...)</option>
731
<option value="gap5">images 1, 5, 10, 15,...</option>
732
<option value="gap25">images 1, 25, 50, 75, ...</option>
733
<option value="gap50">images 1, 50, 100, 150, ...</option>
734
</select>
735
</span>
736
737
<div id="image_grid_nav"></div>
738
</div>
739
740
<div id="image_grid_content">
741
<div id="image_grid_content_img"></div>
742
<svg xmlns:xlink="http://www.w3.org/2000/svg" id="image_grid_content_rshape"></svg>
743
</div>
744
745
<div id="image_grid_info">
746
</div>
747
</div> <!-- end of image grid panel -->
748
749
<div id="settings_panel" class="display_area_content display_none">
750
<h2>Settings</h2>
751
<div class="row">
752
<div class="variable">
753
<div class="name">Project Name</div>
754
</div>
755
756
<div class="value">
757
<input type="text" id="_via_settings.project.name"/>
758
</div>
759
</div>
760
761
<div class="row">
762
<div class="variable">
763
<div class="name">Default Path</div>
764
<div class="desc">If all images in your project are saved in a single folder, set the default path to the location of this folder. The VIA application will load images from this folder by default. Note: a default path of <code>"./"</code> indicates that the folder containing <code>via.html</code> application file also contains the images in this project. For example: <code>/datasets/VOC2012/JPEGImages/</code> or <code>C:\Documents\data\</code>&nbsp;<strong>(note the trailing <code>/</code> and <code>\</code></strong>)</div>
765
</div>
766
767
<div class="value">
768
<input type="text" id="_via_settings.core.default_filepath" placeholder="/datasets/pascal/voc2012/VOCdevkit/VOC2012/JPEGImages/"/>
769
</div>
770
</div>
771
772
<div class="row">
773
<div class="variable">
774
<div class="name">Search Path List</div>
775
<div class="desc">If you define multiple paths, all these folders will be searched to find images in this project. We do not recommend this approach as it is computationally expensive to search for images in multiple folders. <ol id="_via_settings.core.filepath"></ol></div>
776
</div>
777
778
<div class="value">
779
<input type="text" id="settings_input_new_filepath" placeholder="/datasets/pascal/voc2012/VOCdevkit/VOC2012/JPEGImages"/>
780
</div>
781
</div>
782
783
<div class="row">
784
<div class="variable">
785
<div class="name">Region Label</div>
786
<div class="desc">By default, each region in an image is labelled using the region-id. Here, you can select a more descriptive labelling of regions.</div>
787
</div>
788
789
<div class="value">
790
<select id="_via_settings.ui.image.region_label"></select>
791
</div>
792
</div>
793
794
<div class="row">
795
<div class="variable">
796
<div class="name">Region Colour</div>
797
<div class="desc">By default, each region is drawn using a single colour. Using this setting, you can assign a unique colour to regions grouped according to a region attribute.</div>
798
</div>
799
800
<div class="value">
801
<select id="_via_settings.ui.image.region_color"></select>
802
</div>
803
</div>
804
805
<div class="row">
806
<div class="variable">
807
<div class="name">Region Label Font</div>
808
<div class="desc">Font size and font family for showing region labels.</div>
809
</div>
810
811
<div class="value">
812
<input id="_via_settings.ui.image.region_label_font" placeholder="12px Arial"/>
813
</div>
814
</div>
815
816
<div class="row">
817
<div class="variable">
818
<div class="name">Preload Buffer Size</div>
819
<div class="desc">Images are preloaded in buffer to allow smoother navigation of next/prev images. A large buffer size may slow down the overall browser performance. To disable preloading, set buffer size to 0.</div>
820
</div>
821
<div class="value">
822
<input type="text" id="_via_settings.core.buffer_size" />
823
</div>
824
</div>
825
826
<div class="row">
827
<div class="variable">
828
<div class="name">On-image Annotation Editor</div>
829
<div class="desc">When a single region is selected, the on-image annotation editor is gets activated which the user to update annotations of this region. By default, this on-image annotation editor is placed near the selected region.</div>
830
</div>
831
832
<div class="value">
833
<select id="_via_settings.ui.image.on_image_annotation_editor_placement">
834
<option value="NEAR_REGION">close to selected region</option>
835
<option value="IMAGE_BOTTOM">at the bottom of image being annotated</option>
836
<option value="DISABLE">DISABLE on-image annotation editor</option>
837
</select>
838
</div>
839
</div>
840
841
<div class="row" style="border:none;">
842
<button onclick="settings_save()" value="save_settings" style="margin-top:2rem">Save</button>
843
<button onclick="settings_panel_toggle()" value="cancel_settings" style="margin-left:2rem;">Cancel</button>
844
</div>
845
</div> <!-- end of settings panel -->
846
847
<div id="page_404" class="display_area_content display_none narrow_page_content">
848
<h2>File Not Found</h2>
849
<p>Filename: <span style="font-family:Mono;" id="page_404_filename"></span></p>
850
851
<p>We recommend that you update the default path in <span class="text_button" title="Show Project Settings" onclick="settings_panel_toggle()">project settings</span> to the folder which contains this image.</p>
852
853
<p>A temporary fix is to use <span class="text_button" title="Load or Add Images" onclick="sel_local_images()">browser's file selector</span> to manually locate and add this file. We do not recommend this approach because it requires you to repeat this process every time your load this project in the VIA application.</p>
854
</div> <!-- end of file not found panel -->
855
856
<div id="page_start_info" class="display_area_content display_none narrow_page_content">
857
<ul>
858
<li>To start annotation, <span class="text_button" title="Load or Add Images" onclick="sel_local_images()">select images</span> (or, add images from <span class="text_button" title="Add images from a web URL (e.g. http://www.robots.ox.ac.uk/~vgg/software/via/images/swan.jpg)" onclick="project_file_add_url_with_input()">URL</span> or <span class="text_button" title="Add images using absolute path of file (e.g. /home/abhishek/image1.jpg)" onclick="project_file_add_abs_path_with_input()">absolute path</span>) and draw regions</li>
859
<li>Use <span class="text_button" title="Toggle attributes editor panel" onclick="toggle_attributes_editor()">attribute editor</span> to define attributes (e.g. name) and <span class="text_button" title="Toggle annotations editor panel" onclick="annotation_editor_toggle_all_regions_editor()">annotation editor</span> to describe each region (e.g. cat) using these attributes.</li>
860
<li>Remember to <span class="text_button" onclick="project_save_with_confirm()">save</span> your project before closing this application so that you can <span class="text_button" onclick="project_open_select_project_file()">load</span> it later to continue annotation.</li>
861
<li>For help, see the <span class="text_button" onclick="set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_GETTING_STARTED)">Getting Started</span> page and pre-loaded demo: <a href="http://www.robots.ox.ac.uk/~vgg/software/via/via_demo.html">image annotation</a> and <a href="http://www.robots.ox.ac.uk/~vgg/software/via/via_face_demo.html">face annotation</a>.</li>
862
</ul>
863
864
</div>
865
866
<div id="page_getting_started" class="display_area_content display_none narrow_page_content">
867
<p>A more detailed user guide (with screenshots and descriptions) is <a href="http://www.robots.ox.ac.uk/~vgg/software/via/docs/user_guide.html">available here</a>.</p>
868
<ol>
869
<li><strong>Load Images</strong>: The first step is to load all the images that you wish to annotate. There are multiple ways to add images to a VIA project. Choose the method that suits your use case.
870
<ul>
871
<li>Method 1: Selecting local files using browser's file selector
872
<ol>
873
<li>Click <span class="text_button" title="Load or Add Images" onclick="sel_local_images()"><code>Project &rarr; Add local files</code></span></li>
874
<li>Select desired images and click <code>Open</code></li>
875
</ol>
876
</li>
877
<li>Method 2: Adding files from URL or absolute path
878
<ol>
879
<li>Click <span class="text_button" title="Add images from a web URL (e.g. http://www.robots.ox.ac.uk/~vgg/software/via/images/swan.jpg)" onclick="project_file_add_url_with_input()"><code>Project &rarr; Add files from URL</code></span></li>
880
<li>Enter URL and click <code>OK</code></li>
881
</ol>
882
</li>
883
<li>Method 3: Adding files from list of url or absolute path stored in text file
884
<ol>
885
<li>Create a text file containing URL and absolute path (one per line)</li>
886
<li>Click <span class="text_button" title="Add images from a list of web url or absolute path stored in a text file (one url or path per line)" onclick="sel_local_data_file('files_url')"><code>Project &rarr; Add url or path from text file</code></span></li>
887
<li>Select the text file and click <code>Open</code></li>
888
</ol>
889
</li>
890
</ul>
891
</li>
892
<li><strong>Draw Regions</strong>: Select a region shape (<span class="text_button" onclick="select_region_shape('rect')">rectangle</span>, <span class="text_button" onclick="select_region_shape('circle')">circle</span>, <span class="text_button" onclick="select_region_shape('ellipse')">ellipse</span>, <span class="text_button" onclick="select_region_shape('polygon')">polygon</span>, <span class="text_button" onclick="select_region_shape('point')">point</span>, <span class="text_button" onclick="select_region_shape('polyline')">polyline</span>) from the left sidebar and draw regions as follows:
893
894
<ul>
895
<li>Rectangle, Circle and Ellipse
896
<ul>
897
<li>Press left mouse button, drag mouse cursor and release mouse button.</li>
898
<li>To define a point inside an existing region, click inside the region to select it (if not already selected), now press left mouse button, drag and release to draw region inside existing region.</li>
899
<li>To select, click inside the region. If the click point contains multiple regions, then clicking multiple times at that location shuffles selection through those regions.</li>
900
</ul>
901
</li>
902
</ul>
903
904
<ul>
905
<li>Point
906
<ul>
907
<li>Click to define points.</li>
908
<li>To draw a region inside existing region, click inside the region to select it (if not already selected), now click again to define the point.</li>
909
<li>To select, click on (or near) the existing point.</li>
910
</ul>
911
</li>
912
</ul>
913
914
<ul>
915
<li>Polygon and Polyline
916
<ul>
917
<li>Click to define vertices.</li>
918
<li>Press <strong>[Enter]</strong> to finish drawing the region or press [Esc] to cancel.</li>
919
<li>If the first vertex needs to be defined inside an existing region, click inside the region to select it (if not already selected), now click again to define the vertex.</li>
920
<li>To select, click inside the region. If the click point contains multiple regions, then clicking multiple times at that location shuffles selection through those regions.</li>
921
</ul>
922
</li>
923
</ul>
924
</li>
925
926
<li><strong>Create Annotations</strong>: For a more detailed description of this step, see <a href="http://www.robots.ox.ac.uk/~vgg/software/via/docs/creating_annotations.html">Creating Annotations : VIA User Guide</a>. Click the <span class="text_button" onclick="annotation_editor_toggle_all_regions_editor()"><code>View &rarr; Toggle attributes editor</code></span> to show attributes editor panel in left sidebar and add the desired file or region attributes (e.g. name). Now click <span class="text_button" onclick="annotation_editor_toggle_all_regions_editor()"><code>View &rarr; Toggle annotations editor</code></span> to show the annotation editor panel in the bottom side. Update the annotations for each region.</li>
927
<li><strong>Export Annotations</strong>: To export the annotations in json or csv format, click <span class="text_button" onclick="download_all_region_data('csv')"><code>Annotation &rarr; Export annotations</code></span> in top menubar.</li>
928
<li><strong>Save Project</strong>: To save the project, click <span class="text_button" onclick="project_save_with_confirm()"><code>Project &rarr; Save</code></span> in top menubar.</li>
929
</ol>
930
</div>
931
932
<div id="page_load_ongoing" class="display_area_content narrow_page_content">
933
<div style="text-align:center">
934
<a href="http://www.robots.ox.ac.uk/~vgg/software/via/">
935
<svg height="160" viewbox="0 0 400 160" style="background-color:#212121;">
936
<use xlink:href="#via_logo"></use>
937
</svg>
938
</a>
939
<div style="margin-top:4rem">Loading ...</div>
940
</div>
941
</div>
942
943
<div id="page_about" class="display_area_content display_none" style="width:40rem !important">
944
<div style="text-align:center">
945
<a href="http://www.robots.ox.ac.uk/~vgg/software/via/">
946
<svg height="160" viewbox="0 0 400 160" style="background-color:#212121;">
947
<use xlink:href="#via_logo"></use>
948
</svg>
949
</a>
950
</div>
951
952
<p style="font-family:mono; font-size:0.8em;text-align:center;"><a href="https://gitlab.com/vgg/via/blob/master/CHANGELOG">Version 2.0.12</a></p>
953
<p>VGG Image Annotator (VIA) is an image annotation tool that can be used to define regions in an image and create textual descriptions of those regions. VIA is an <a href="https://gitlab.com/vgg/via/">open source project</a> developed at the <a href="http://www.robots.ox.ac.uk/~vgg/">Visual Geometry Group</a> and released under the BSD-2 clause <a href="https://gitlab.com/vgg/via/blob/master/LICENSE">license</a>.</p>
954
<p>Here is a list of some salient features of VIA:
955
<ul>
956
<li>based solely on HTML, CSS and Javascript (no external javascript libraries)</li>
957
<li>can be used off-line (full application in a single html file of size &lt; 400KB)</li>
958
<li>requires nothing more than a modern web browser (tested on Firefox, Chrome and Safari)</li>
959
<li>supported region shapes: rectangle, circle, ellipse, polygon, point and polyline</li>
960
<li>import/export of region data in csv and json file format</li>
961
</ul>
962
</p>
963
<p>For more details, visit <a href="http://www.robots.ox.ac.uk/~vgg/software/via/">http://www.robots.ox.ac.uk/~vgg/software/via/</a>.</p>
964
<p>&nbsp;</p>
965
<p>Copyright &copy; 2016-2021, <a href="mailto:adutta-removeme@robots.ox.ac.uk">Abhishek Dutta</a>,Visual Geometry Group, Oxford University and <a target="_blank" href="https://gitlab.com/vgg/via/blob/master/Contributors.md">VIA Contributors</a>.</p>
966
</div> <!-- end of page_about -->
967
968
<div id="page_license" class="display_area_content display_none narrow_page_content">
969
<pre>
970
Copyright (c) 2016-2022, Abhishek Dutta, Visual Geometry Group, Oxford University and VIA Contributors.
971
All rights reserved.
972
973
Redistribution and use in source and binary forms, with or without
974
modification, are permitted provided that the following conditions are met:
975
976
Redistributions of source code must retain the above copyright notice, this
977
list of conditions and the following disclaimer.
978
Redistributions in binary form must reproduce the above copyright notice,
979
this list of conditions and the following disclaimer in the documentation
980
and/or other materials provided with the distribution.
981
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot;
982
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
983
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
984
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
985
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
986
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
987
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
988
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
989
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
990
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
991
POSSIBILITY OF SUCH DAMAGE.
992
</pre>
993
</div>
994
</div> <!-- end of display_area -->
995
</div> <!-- end of middle_panel -->
996
997
<!-- this vertical spacer is needed to allow scrollbar to show
998
items like Keyboard Shortcut hidden under the attributes panel -->
999
<div style="width: 100%;" id="vertical_space"></div>
1000
1001
<!-- START: Contents of file: src/demo/_via_basic_demo.js-->
1002
<script>
1003
/*
1004
VGG Image Annotator (via)
1005
www.robots.ox.ac.uk/~vgg/software/via/
1006
1007
Copyright (c) 2016-2018, Abhishek Dutta.
1008
All rights reserved.
1009
1010
Redistribution and use in source and binary forms, with or without
1011
modification, are permitted provided that the following conditions are met:
1012
1013
Redistributions of source code must retain the above copyright notice, this
1014
list of conditions and the following disclaimer.
1015
Redistributions in binary form must reproduce the above copyright notice,
1016
this list of conditions and the following disclaimer in the documentation
1017
and/or other materials provided with the distribution.
1018
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1019
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1020
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1021
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
1022
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1023
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1024
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1025
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1026
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1027
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1028
POSSIBILITY OF SUCH DAMAGE.
1029
*/
1030
1031
function _via_load_submodules() {
1032
_via_basic_demo_load_img();
1033
//_via_basic_demo_draw_default_regions();
1034
_via_basic_demo_define_attributes();
1035
_via_basic_demo_define_annotations();
1036
1037
toggle_attributes_editor();
1038
update_attributes_update_panel();
1039
1040
annotation_editor_show();
1041
}
1042
1043
function _via_basic_demo_load_img() {
1044
// add files
1045
var i, n;
1046
var file_count = 0;
1047
n = _via_basic_demo_img.length;
1048
for ( i = 0; i < n; ++i ) {
1049
project_file_add_base64( _via_basic_demo_img_filename[i], _via_basic_demo_img[i] );
1050
file_count += 1;
1051
}
1052
1053
_via_show_img(0);
1054
update_img_fn_list();
1055
}
1056
1057
function _via_basic_demo_draw_default_regions() {
1058
var csv_annotations = 'filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes\nadutta_swan.jpg,-1,"{}",1,0,"{""name"":""polygon"",""all_points_x"":[116,94,176,343,383,385,369,406,398,364,310,297,304,244,158],""all_points_y"":[157,195,264,273,261,234,222,216,155,124,135,170,188,170,175]}","{}"\nwikimedia_death_of_socrates.jpg,-1,"{}",3,0,"{""name"":""rect"",""x"":174,""y"":139,""width"":108,""height"":227}","{}"\nwikimedia_death_of_socrates.jpg,-1,"{}",3,1,"{""name"":""rect"",""x"":347,""y"":114,""width"":91,""height"":209}","{}"\nwikimedia_death_of_socrates.jpg,-1,"{}",3,2,"{""name"":""ellipse"",""cx"":316,""cy"":180,""rx"":17,""ry"":12}","{}"';
1059
1060
import_annotations_from_csv(csv_annotations);
1061
}
1062
1063
function _via_basic_demo_define_attributes() {
1064
var attributes_json = '{"region":{"name":{"type":"text","description":"Name of the object","default_value":"not_defined"},"type":{"type":"dropdown","description":"Category of object","options":{"bird":"Bird","human":"Human","cup":"Cup (object)","unknown":"Unknown (object)"},"default_options":{"unknown":true}},"image_quality":{"type":"checkbox","description":"Quality of image region","options":{"blur":"Blurred region","good_illumination":"Good Illumination","frontal":"Object in Frontal View"},"default_options":{"good":true,"frontal":true,"good_illumination":true}}},"file":{"caption":{"type":"text","description":"","default_value":""},"public_domain":{"type":"radio","description":"","options":{"yes":"Yes","no":"No"},"default_options":{"no":true}},"image_url":{"type":"text","description":"","default_value":""}}}';
1065
1066
project_import_attributes_from_json(attributes_json);
1067
}
1068
1069
function _via_basic_demo_define_annotations() {
1070
var annotations_json = '{"adutta_swan.jpg-1":{"filename":"adutta_swan.jpg","size":-1,"regions":[{"shape_attributes":{"name":"polygon","all_points_x":[116,94,176,343,383,385,369,406,398,364,310,297,304,244,158],"all_points_y":[157,195,264,273,261,234,222,216,155,124,135,170,188,170,175]},"region_attributes":{"name":"Swan","type":"bird","image_quality":{"good_illumination":true}}}],"file_attributes":{"caption":"Swan in lake Geneve","public_domain":"no","image_url":"http://www.robots.ox.ac.uk/~vgg/software/via/images/swan.jpg"}},"wikimedia_death_of_socrates.jpg-1":{"filename":"wikimedia_death_of_socrates.jpg","size":-1,"regions":[{"shape_attributes":{"name":"rect","x":174,"y":139,"width":108,"height":227},"region_attributes":{"name":"Plato","type":"human","image_quality":{"good_illumination":true}}},{"shape_attributes":{"name":"rect","x":347,"y":114,"width":91,"height":209},"region_attributes":{"name":"Socrates","type":"human","image_quality":{"frontal":true,"good_illumination":true}}},{"shape_attributes":{"name":"ellipse","cx":316,"cy":180,"rx":17,"ry":12},"region_attributes":{"name":"Hemlock","type":"cup"}}],"file_attributes":{"caption":"The Death of Socrates by David","public_domain":"yes","image_url":"https://en.wikipedia.org/wiki/The_Death_of_Socrates#/media/File:David_-_The_Death_of_Socrates.jpg"}}}';
1071
1072
import_annotations_from_json(annotations_json);
1073
}
1074
1075
var _via_basic_demo_img = [];
1076
var _via_basic_demo_img_filename = ['adutta_swan.jpg', 'wikimedia_death_of_socrates.jpg'];
1077
1078
// adutta_swan.jpg
1079
_via_basic_demo_img.push('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4RiJRXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwABAAAAAgAAADEBAgAMAAAAZgAAAGmHBAABAAAAcgAAAAwBAABIAAAAAQAAAEgAAAABAAAAR0lNUCAyLjguMTYABwAAkAcABAAAADAyMjAAoAcABAAAADAxMDABoAMAAQAAAAEAAAACoAQAAQAAAFgCAAADoAQAAQAAAJABAAAFoAQAAQAAAO4AAAAgpAIAIQAAAMwAAAAAAAAAOGVmMjdkYjNjN2I2OGUzOTAwMDAwMDAwMDAwMDAwMDAAAAIAAQACAAQAAABSOTgAAgAHAAQAAAAwMTAwAAAAAAYAAwEDAAEAAAAGAAAAGgEFAAEAAABaAQAAGwEFAAEAAABiAQAAKAEDAAEAAAACAAAAAQIEAAEAAABqAQAAAgIEAAEAAAAXFwAAAAAAAEgAAAABAAAASAAAAAEAAAD/2P/gABBKRklGAAEBAAABAAEAAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAkLicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/AABEIAIIAxAMBIgACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/AIHacHdHNuX0win8yM00TAnE8rqfQXC/yFSFpUYs0NsB/wBNWGTTWuUjG5jCPQRgAfoa7jzx6G3ZSXu8D2Iz+eacssYGy2MswPUiXI/Q1UluUYDzmQL2zExpElQriPCIe4Byf/HaLhYuMNwKNs+khXH/AKCajEksXAa1QDsin+YFMM1ugA+0yY9PKH86hEv2hswW9y4/vRlCP/QTQ2Fiy7tIMs6AewJ/majKM/K2kTj++GCGnKJGGBGI8f8APU4J/EAVHIsYwHRlf/pnhv8A0I0XAd9l8w7vO5H8PDEfjSqoU8TTqR6bh+uartDbn5mlnDdcm2GB+Rpj3NgvDzPIw4w0aDP5nNK6HYsPEjMGkSGTHRmZifzzilYNgD7QsanoGAI/PaagjurYf6jYpPZYwx/Kpftr7cC3Lf7RVI/0PNFwsMa1R2/11vIR/cRifwKgVILZGTIkvEwehSQj+dVZDNMcGVsDsJcfyFRbZl42g/70isfw+b+lIZorbxAbnxn3t8E/+PUjJG3SIBf7xCN+h5/Ws0LKOXyg/vBBTDHbbt0krN/tKFz/ADzTA0WcKAqzWsi9tzlD+hqEyxjrEu/1RicfjjP61XKRnmGW4bHYSFf6GnhY0+eRtvowYyH+QpgBulfKMY2/66s4z+hpuyCUYZ7WMjpyWH58U+WWfG63u7sr6GPA/Qc1G9zdbQ73APrwyn8aQEy2+cACQ9t0WNv5YNPMNsBiea3Yjt9w/wAwKoOwlXeI5JG/vQkioxsfCujq3YzOjH9eaegGiywAYSeIL6M6kfmKhxtP7sq49I5pMfqMVXMbAfeZT2MEOSfyIppmEfyzpNJ/18QBf131LSGi6DuGTBJ+St+pNFZ7OmeLRcdtsoA/9CoosFjfaSbPzQQr/tSWgUfnuqMyxg5e5tw3pGoz/Wq6w2yknezP3+XI/Olxbjhhbsf9p1B/9CNLVCLHny43QPOwH8THA/AZppuJ5RmWUn/ZWPdn9arpbQA7kS2B/wBo7v5GnFlBwIYi3by1kAoYhTJOzAKLqMfXaD+A/wAaSSO6PO0Mv+3K3H4A0vny8h52C/3UDN/M0hnC9HDL6PEv+NKwyII0rYa0t2A/iKt/PNSxpIxIjlNuv/TNmIP6GlWaabKxD5RxnyuPwzTWt48fvtRYN2XdgfoTRYB5UZxM0kx/vb8H8gKQpG2AYWwOhlQL+uc1GYY0xu2TejMf61L5ka4UrGq9MLIG/wDrUtQK7mdM+Ulo6evml/0FRDBxulto2/64P/OrRKDJiuoRntIo/oKTzLdwFbaXHGYlAFG4FSUSFsPcu6jtFDx+ooz5YDGP5P7xQA/yNXwt5sIjeN4/7rHf+lVX2LkSacEY/wAUcLMT+GeKoaGBt43pMxX/AG4sD8+KZIqY/eGHnoRnn8jT0jtnGZpHYjrG8fI/BWppuNMtj+6BVj1Cxgj8fmNMCJYGPMTOPZIyv/jzVIumTynmJwfUyDJ/I0jXkW7MYh+ochh+CCoXk35ZppY2HdYF/RnOaNBlj7E0Wd7PHjqWO4fzqJTAHPlzXM/qpGxP5GoD9m4fNzJIO5Qv+mQKcJ3lH72e4+kqiNf0JpXCxMwG7KW1swPVRgH8+KQrc9I7B1TuyNk/nTBHEVyxt0X+8hBP5nFCw2jdfNmPYkFQPzNDAVohGm43s0I7iT5sfhmiPZcfLFPaSuP70LofzIxQIhCxeOKZvQo6/wAitS+VHIoeWCVm/wCmmCPyqW7DsVme5hYoZ9mOy+Zj9FxRVsPbqMGCIH6MP6UUXCw9heSDMLso7hsVAZZI/lkKzP8AQYH9asfZllcPHfNLIOcRLjP5VYEF3EheacIn+2BiqsSUEMsjAiRj/sqgwPxxUrbQcYLP7Dd/9ala7DnZ9qtyv+4q/rmkM9hABvkEvtHHx+Jzj+dGgCFp2yBaM6euz/CkDMASbGM46Zxmka7gf5jbQKo6H5/8/pUf260Jx5q/Te2B+G2jTuANJPMNjxrGg/hUkf40n7tOBbI5+pP9aJpoJiu64D4/gjU5P5ilE8EK5/s+YD+8Dj+QpWAeHiC4WXY3UqYuB+dOUzjqsSIf4xFk1Wa7LHd5aQp2aUtk043MwHFzEAfViSfwJoHYsCa3jH/HzMpHcwrzSm7EnS6lA9XIT9F61C1xLGAdke3+9sVmP5k0C+BG+S1CJnG9kBY/QcVF7bjsL5SSsSvnXTf721R+uaYHn5jgkgVu8aEj8yOtOF2ZAfKSUr/tRAA/lWlpllf6m+2KzgdF6tjYB9TSTQ7Mx5ILiX5XaJG9Fj3k/wBKgW1vtv7tg0Y6hP8ACu+k8A6xLADDLAFAz5cchOfzArnLlIrWRradFWSNsMJIMkH+lNSj0YOMlujAe3Kcvb3AHqwOKjAt0OfN8on+HaSf51v/AGSBR5nkwMvYIJBn6jFJ5Tj5nWzjTsrIyn/69MVzISSSL/V3Cc+q5P5cipgbl+fLMo/2YBj9BU8z25OyX7Ov+5GRn9aiW30/7/2zyz/dDAf1JqgI/KWRuYVib3JSk8po2Ia8U+gB31KXlH3LhDGOMmPcD+JFV5JZ9x23NqnuqBP1xS0GWEukB2ukbD3i2k/rSR3VsWIFsfxBxUKCB8f6TBJL7yYP6irXk3Hl/u3UH/ZcGiz6AMa8tweI4se+7/Ciq7XF7E2xssR3opfIC+1jADlBx6ICf1aoTbW6NuaKVvq4NQgt0FsGHdt3NIyEKMLcjP8ACelP5Ek7XShCI4gq+pcMar7p2b752nuQAKUYUjfYBF/vE5NTkWso2gMzey4x+NG4ESwsvzfaLc+zPk/lUi3siA7EtOP4hGGP+Fanh3S9IudWWDV38m2kQqJcgCN+x5/L8a3Nb+G2vWI36csF/bn7pgVUYD3B/oTUuVtClBtXRxj6lFL8kocN/eSIUsXnHm1ac+rspJ/AYq8fDXiESi28m6ikPRFQr+uOn41pW/wz8S3a77mSIeiyzH+maq7BROfeKRT5k4IP/PWY4P4DHNMWOzwZHuXU+qqMn6Cu1svhTcC5T7VfWyk9VQF/54rck+E1vJz/AGkOnA+zj+hFDdtxqD6Hlh82JjIk9wkfYtFuNSRyNNIHe8aYjorI2f5V6BL8KNTjybe+hIH3duUJ/Q1zeqeG/EmhgtLbSrGOtwXaRAPw4H40rg4sy2DyEeY7sR0j2n/CvUYNNTQ/DsUqqESMBpCo7nrXlivcFgVEE7g5yowa9+hs4ta8MSWxxi4hwD6EjiuXEtpKx0YdLVs53w34htr7LxSsxjbLoRyB7VV+JWlWkttaa1Ais7sIZHT+MEEqcj6EfjXnGk3uraNq/nJEtuIiyurjCntz681evtX1S18NPoN8889t5yyW9xtCkjaSV+gLD9RWUo8lVWe5qr1KTdtjGMMJclZLkz9tkpABplxBcRKxe9Jzx+9UkD8cVBFdOAyoj7iOWK5xUpmZo/nfy/VnXGa6Lu5yWKi3HGyZPNTP3o1Zh+pFWop4V4iUqB7Y/qadEbOJcmUbm5ygwf5U2+to2UGMzSOewYVpzJkiiWU52TbwT/z02moyFlyzfZzjrufJ/LNJA0SArJGit69SDT2KznbJa20p/hk+4T9eaE0hlZ4La4/5eYYiP9k/0FQSWkcRXbcLJnoVBH86ttaqvJs4In74n/pUFxuRf3S5PovOarfcQ9IrormOWTb/AL1FVBeHGDZsSOO9FVdBYvyGPdgzMw/2Qc/ypY0MY3W7yqT3L8/pTFuIEOBdbz6LECfzPFNmuWj+YPKo9nA/lTsiSz596RiSefb/ALGSaIDaq5LtMX65dRn8s1mtqbsMM5I9XOaT7RBJgM+T6KMUaBZmu9xaM2BncO8rH+lei+AvHUdp/wASnVrtRD0t5WPCf7JJ7emeleUgrgELGB2y3NIbsQtyIl9lGSfxrOW2pcW09D6XnRkJnjcTRychlIJ/A1HDdLLkgEEcc14r4V+IVxpLi1u1Z7LPysuSY/w7ivULLxPo2pSrFFdQvLIMq0Zw2fQg1mp22Z0b7m+kMb5dgC3rU8USP0FVbVmZR/StFI1xleKbnIaiidIyAACal2EjDKGFRoSoqwjA1m2UebeMfhrp9wJNS02FbeY8vGvCE+uB0/CtP4fy3kGkR2V8myaAmMfNkFR0IP0xXcOAyFGXcpGCPWuctNLl0/V3IBMD8qacrThZ7omKtK6PIviV4Zn0rxRNe5/0G9cyox6Bjyyn8efoaxb7XReaXaWQjjDW45kJ5Y9PyxivorXdDtPEOiT6deIGjkX5SRyjdmH0r5bvYWs7+e3FopaB2jcEkjIODThGMrN7olzlBNRej3LMUrrESGX5uwfbUUsgdgXQYz2OaptLOWGIEAHZSeP1qUbyB+4Eee7NmteW5gTtMJCEMTYHfbxUuLQRqkkkRA/gA3H6VRkFzGQJslB2HQ1Kt4OqqI88YArOUWikWTFG0oaJHWNRydmcfkKreeZAzsi4A+UcAk05ZmjO7LCMnBKjn86e1zalQU+8O+0f4VKTApG7jmATyjkH7x4J+oqxb+ZEysyoi5yMnrU9uQPmkgLK3OdwBpJ1UxkLNtYHI3L0/HNVfWwWLLXcJPzBSfdaKzoVkmUstznBI4//AFUUreYC3DxTLiK3SP2UVEmnai4yGSOP1dgoqxJP5xAmeWVuuyHAUflSOIW+aVZsnt0rd3M0VGspAfmnilx2U8UhRgNqJEp/2V5q0YZioMW2FD04wTTVilORwx9xSTQyn9kk+9IQB6s1OlaIokaiMqvIOMnJqdrdo2zMI8e7UxoY5W5lRR2VBQ4oabI1nIHzSIF/uqvNXLC9uILmK7tVZZIW3L0wRUAjto1I8ss/bnio3uJY3BZQAOi4rOVNPVGkZ9z0W0+K99GyCXTYj2bYxH+Ndfp/xR0uRQLm3uYiRk/KGA/WvDBf5OfsxPbg1ct5tqb5S4/2euPpWTjI2U4nvkHxF0Bx89xIvPeJv6CrD/EHw7xi9J+kbf4V4A17HtIzMQRxjj+dRPDMsXnmS7iB4AbuaSjIHNI+jovHfh58f8TAZ9PLf/CpJPHHh1QN19z2HlPn+VfMb3l1EwP2mYg/7Rqe3upbi5gd2Y4OM7j/AFpSTigUlJ7H0LefEXT44W+xxPKwHDSfIv8Aj+lfPGriW81e6uTOGM8zyE9jkk1p6pq6iI2sLFm+657j2rEd9oHyfTPFXRT3ZFVrZAoaBSrITnpzUYaTlcEA9fanNI20/Lx7GoWnmcYCCt7mNiQT3cAIDEr6Hmmrc5JLDn6U0O+duxmPvUqrO4JVFUDuae4D4rvYh5LBuoJzThcKmOD+dVJVuMfNMgHtxUJinxu4YeoOaTiho147lXc/O3PYGor2RA4Ry4DLnDDNUU3RDe5GD2zmp9iXLJlzux7nAqOWzuMYspRdqvlR0zRTGt4QcEt+ByKKfKFy290zEx2yiKMnt3+pp0ccwf5pU577s1JcWxnw4j2ADkk4zVQeRHIqlweeRnNUndEGubkRoFSRAccknNQCa4kzgqU+uKrCaFpsRW8hYe3epykkcR3Dbnsep/CoemxRXkAC5klJHoBzVf7SPuwxYHqetW1S1WQieVwy8FFGasma1Vf9HhA9yMmmrgZBeTOTmp0RpFBkyQOavPaXM6F4oiEx1biqbQlD+9nAHsc07XFcakscXCg59ak8wyttHOe9RlYjkxgtjqTxmkF46goi7e3Sh2GWS0cQ+8rN9elQyTMCCJGA9KgZiGyw6+oppuCOF6UWAn+1fJ157nFaZi8uDfbhtxj3GU4wv05rFWTfy4BA9q3dNvIpbJreTysKOPNXKgfnWFdO10bUbXszn4Vfdvdjuz19alkDs2fnI6jA4rQv9Rt9nlWcMWF+9IsW3cf1OKoQyTSfNjO7p2qozukTKNriLvYgbsfU1N5Mm7cJFNNFtLkny9x9actu6EEluvRecVfMiLCSFiBtcdOgqAwTzY2u30zUsttJGrMjEnrj2qNI3YANuB9qFK4WEFmAcSPz6GlePyuE/wDHTUoBOVZ8IO560xnVT8rZ9CapCKpiY/NsOfTPWrCPKuDgrnrml/eOcgqfxpCm3lm3H60OI7isUOMIvAx160VGSmeh/CiiwjpX0uGdt08snPJG7iqssGkWvKxyzSZ6dqvfZoyA0moKeMERsefy/wAartHbJ+7Ulj1DHjA+neudSY7ECvfzofsqLaxDqwHT8ajL2cDL5l000o5J7ZpcR3abZZnfHO0OQAKYLTT454kaclCemeg/Wr5gSFub+2BGyFGkxyoGSKqtqE7jCRhfwrUv4LeFhsR338DnANUjBPA2WiAGM4HPFEZ6bhZFItdu4Dl/m6ZqSLT5WP7zgHuatS3ys6iIKpxg4GRVOZri4cgMzAH+EVSkg1LZnht8RA7yvYVA9zG5yEANMis2HVGGOvFLLa/JlWCn0JqkxWI2TdnJx+NJHGpbaB+NAUsqtu6ipllhRPmJLegp3QCbIY/vyAn0U1DJPklVGU9qcXEjcgAfSmJCoJw+KJe8rIcdNx8DIXyVAxzjvVw4n3S8wkj7rY/kOlZsgdG68etOjk+UqehrJQs7luV1Y00YhdqyKT67qge68ojauCo+YkdTmo4jEvKlgx71Vdne4ZmzKScnJ6mmkrklpbzIx2PUYpWmyOBio2nkCcQRqPUD/wCvVZ526Fjn2qr9hWJ2LMcng+tMlRsYJGexpguDt4BA9KaJnYgjnHTilcdhrxuhG760plO319qbM5kYcDI6ioxnrmnqxjjIPQ/gaKPMHoKKAOit/wDXIO2KUc30Ge55oormWwEbgK42gDLc4+tV9U4l4/v0UVS3Ebsig2kWQD+7XtURAE20DAKtkfhRRWcfhB7iWEUf2iT92v3QelKWJkbJPXFFFaU/jCWxXkZgWwSOD39qxpCfLc5OcUUVrLclbD4gPsi8VGoHmDiiihjQndqbF0NFFPoAjnkVC39aKKp7DW5KpO08/wAJpsZ+QUUVnEbJG5bn1qvIAJzxRRQgNCRVE4UKANo4xTIgNxOB0NFFQthsz7v/AF5+gqDtRRWvQBO9FFFAH//Z/+EJf2h0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiB4bWxuczpleGlmPSdodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyc+CiAgPGV4aWY6T3JpZW50YXRpb24+VG9wLWxlZnQ8L2V4aWY6T3JpZW50YXRpb24+CiAgPGV4aWY6WFJlc29sdXRpb24+NzI8L2V4aWY6WFJlc29sdXRpb24+CiAgPGV4aWY6WVJlc29sdXRpb24+NzI8L2V4aWY6WVJlc29sdXRpb24+CiAgPGV4aWY6UmVzb2x1dGlvblVuaXQ+SW5jaDwvZXhpZjpSZXNvbHV0aW9uVW5pdD4KICA8ZXhpZjpTb2Z0d2FyZT5QaWNhc2E8L2V4aWY6U29mdHdhcmU+CiAgPGV4aWY6Q29tcHJlc3Npb24+SlBFRyBjb21wcmVzc2lvbjwvZXhpZjpDb21wcmVzc2lvbj4KICA8ZXhpZjpYUmVzb2x1dGlvbj43MjwvZXhpZjpYUmVzb2x1dGlvbj4KICA8ZXhpZjpZUmVzb2x1dGlvbj43MjwvZXhpZjpZUmVzb2x1dGlvbj4KICA8ZXhpZjpSZXNvbHV0aW9uVW5pdD5JbmNoPC9leGlmOlJlc29sdXRpb25Vbml0PgogIDxleGlmOkZsYXNoUGl4VmVyc2lvbj5GbGFzaFBpeCBWZXJzaW9uIDEuMDwvZXhpZjpGbGFzaFBpeFZlcnNpb24+CiAgPGV4aWY6SW50ZXJvcGVyYWJpbGl0eUluZGV4PlI5ODwvZXhpZjpJbnRlcm9wZXJhYmlsaXR5SW5kZXg+CiAgPGV4aWY6SW50ZXJvcGVyYWJpbGl0eVZlcnNpb24+MDEwMDwvZXhpZjpJbnRlcm9wZXJhYmlsaXR5VmVyc2lvbj4KICA8ZXhpZjpPcmllbnRhdGlvbj5Ub3AtbGVmdDwvZXhpZjpPcmllbnRhdGlvbj4KICA8ZXhpZjpYUmVzb2x1dGlvbj43MjwvZXhpZjpYUmVzb2x1dGlvbj4KICA8ZXhpZjpZUmVzb2x1dGlvbj43MjwvZXhpZjpZUmVzb2x1dGlvbj4KICA8ZXhpZjpSZXNvbHV0aW9uVW5pdD5JbmNoPC9leGlmOlJlc29sdXRpb25Vbml0PgogIDxleGlmOlNvZnR3YXJlPkdJTVAgMi44LjE2PC9leGlmOlNvZnR3YXJlPgogIDxleGlmOkNvbXByZXNzaW9uPkpQRUcgY29tcHJlc3Npb248L2V4aWY6Q29tcHJlc3Npb24+CiAgPGV4aWY6WFJlc29sdXRpb24+NzI8L2V4aWY6WFJlc29sdXRpb24+CiAgPGV4aWY6WVJlc29sdXRpb24+NzI8L2V4aWY6WVJlc29sdXRpb24+CiAgPGV4aWY6UmVzb2x1dGlvblVuaXQ+SW5jaDwvZXhpZjpSZXNvbHV0aW9uVW5pdD4KICA8ZXhpZjpGbGFzaFBpeFZlcnNpb24+Rmxhc2hQaXggVmVyc2lvbiAxLjA8L2V4aWY6Rmxhc2hQaXhWZXJzaW9uPgogIDxleGlmOkludGVyb3BlcmFiaWxpdHlJbmRleD5SOTg8L2V4aWY6SW50ZXJvcGVyYWJpbGl0eUluZGV4PgogIDxleGlmOkludGVyb3BlcmFiaWxpdHlWZXJzaW9uPjAxMDA8L2V4aWY6SW50ZXJvcGVyYWJpbGl0eVZlcnNpb24+CiAgPGV4aWY6T3JpZW50YXRpb24+VG9wLWxlZnQ8L2V4aWY6T3JpZW50YXRpb24+CiAgPGV4aWY6WFJlc29sdXRpb24+NzI8L2V4aWY6WFJlc29sdXRpb24+CiAgPGV4aWY6WVJlc29sdXRpb24+NzI8L2V4aWY6WVJlc29sdXRpb24+CiAgPGV4aWY6UmVzb2x1dGlvblVuaXQ+SW5jaDwvZXhpZjpSZXNvbHV0aW9uVW5pdD4KICA8ZXhpZjpTb2Z0d2FyZT5QaWNhc2E8L2V4aWY6U29mdHdhcmU+CiAgPGV4aWY6Q29tcHJlc3Npb24+SlBFRyBjb21wcmVzc2lvbjwvZXhpZjpDb21wcmVzc2lvbj4KICA8ZXhpZjpYUmVzb2x1dGlvbj43MjwvZXhpZjpYUmVzb2x1dGlvbj4KICA8ZXhpZjpZUmVzb2x1dGlvbj43MjwvZXhpZjpZUmVzb2x1dGlvbj4KICA8ZXhpZjpSZXNvbHV0aW9uVW5pdD5JbmNoPC9leGlmOlJlc29sdXRpb25Vbml0PgogIDxleGlmOkV4aWZWZXJzaW9uPkV4aWYgVmVyc2lvbiAyLjI8L2V4aWY6RXhpZlZlcnNpb24+CiAgPGV4aWY6Rmxhc2hQaXhWZXJzaW9uPkZsYXNoUGl4IFZlcnNpb24gMS4wPC9leGlmOkZsYXNoUGl4VmVyc2lvbj4KICA8ZXhpZjpDb2xvclNwYWNlPnNSR0I8L2V4aWY6Q29sb3JTcGFjZT4KICA8ZXhpZjpQaXhlbFhEaW1lbnNpb24+MTI5NjwvZXhpZjpQaXhlbFhEaW1lbnNpb24+CiAgPGV4aWY6UGl4ZWxZRGltZW5zaW9uPjg2NDwvZXhpZjpQaXhlbFlEaW1lbnNpb24+CiAgPGV4aWY6SW1hZ2VVbmlxdWVJRD44ZWYyN2RiM2M3YjY4ZTM5MDAwMDAwMDAwMDAwMDAwMDwvZXhpZjpJbWFnZVVuaXF1ZUlEPgogIDxleGlmOkludGVyb3BlcmFiaWxpdHlJbmRleD5SOTg8L2V4aWY6SW50ZXJvcGVyYWJpbGl0eUluZGV4PgogIDxleGlmOkludGVyb3BlcmFiaWxpdHlWZXJzaW9uPjAxMDA8L2V4aWY6SW50ZXJvcGVyYWJpbGl0eVZlcnNpb24+CiA8L3JkZjpEZXNjcmlwdGlvbj4KCjwvcmRmOlJERj4KPC94OnhtcG1ldGE+Cjw/eHBhY2tldCBlbmQ9J3InPz4K/9sAQwADAgILCggICgoKCAgICAgHBwgICAgICAcHBwcHBwcHBwcICAgIBwcHBwcHBwcKBwcICAkJCQcHDQ0KCA0HCAkI/9sAQwEDBAQGBQYIBgYKDQ0IDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0N/8IAEQgBkAJYAwERAAIRAQMRAf/EABwAAAIDAQEBAQAAAAAAAAAAAAMEAQIFAAYHCP/EABsBAAMBAQEBAQAAAAAAAAAAAAABAgMEBQYH/9oADAMBAAIQAxAAAAGPZ8avPVWW1zXYKWzaIAYpWW+xsFgmpAiB6KL6TRMLEoqk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');
1080
1081
// wikimedia_death_of_socrates.jpg
1082
_via_basic_demo_img.push('data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEASABIAAD/4QDmRXhpZgAATU0AKgAAAAgABwEaAAUAAAABAAAAYgEbAAUAAAABAAAAagEoAAMAAAABAAIAAAE7AAIAAAAOAAAAcgITAAMAAAABAAEAAIKYAAIAAAAzAAAAgIdpAAQAAAABAAAAtAAAAAAAAABIAAAAAQAAAEgAAAABVHJ1amlsbG8gSnVhbgBodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvcHVibGljZG9tYWluL3plcm8vMS4wLwAAAAOQAAAHAAAABDAyMTCgAAAHAAAABDAxMDCgAQADAAAAAf//AAAAAAAA/+EELWh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8APD94cGFja2V0IGJlZ2luPSfvu78nIGlkPSdXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQnPz4KPHg6eG1wbWV0YSB4bWxuczp4PSdhZG9iZTpuczptZXRhLyc+CjxyZGY6UkRGIHhtbG5zOnJkZj0naHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyc+CgogPHJkZjpEZXNjcmlwdGlvbiB4bWxuczpkYz0naHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8nPgogIDxkYzpkZXNjcmlwdGlvbj4KICAgPHJkZjpBbHQ+CiAgICA8cmRmOmxpIHhtbDpsYW5nPSd4LWRlZmF1bHQnPngtZGVmYXVsdDwvcmRmOmxpPgogICA8L3JkZjpBbHQ+CiAgPC9kYzpkZXNjcmlwdGlvbj4KIDwvcmRmOkRlc2NyaXB0aW9uPgoKIDxyZGY6RGVzY3JpcHRpb24geG1sbnM6ZXhpZj0naHR0cDovL25zLmFkb2JlLmNvbS9leGlmLzEuMC8nPgogIDxleGlmOlhSZXNvbHV0aW9uPjcyPC9leGlmOlhSZXNvbHV0aW9uPgogIDxleGlmOllSZXNvbHV0aW9uPjcyPC9leGlmOllSZXNvbHV0aW9uPgogIDxleGlmOlJlc29sdXRpb25Vbml0PkluY2g8L2V4aWY6UmVzb2x1dGlvblVuaXQ+CiAgPGV4aWY6QXJ0aXN0PlRydWppbGxvIEp1YW48L2V4aWY6QXJ0aXN0PgogIDxleGlmOllDYkNyUG9z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');
1083
</script>
1084
<!-- END: Contents of file: src/demo/_via_basic_demo.js-->
1085
<!-- START: Contents of file: via.js-->
1086
<script>
1087
/*
1088
VGG Image Annotator (via)
1089
www.robots.ox.ac.uk/~vgg/software/via/
1090
1091
Copyright (c) 2016-2019, Abhishek Dutta, Visual Geometry Group, Oxford University and VIA Contributors.
1092
All rights reserved.
1093
1094
Redistribution and use in source and binary forms, with or without
1095
modification, are permitted provided that the following conditions are met:
1096
1097
Redistributions of source code must retain the above copyright notice, this
1098
list of conditions and the following disclaimer.
1099
Redistributions in binary form must reproduce the above copyright notice,
1100
this list of conditions and the following disclaimer in the documentation
1101
and/or other materials provided with the distribution.
1102
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1103
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1104
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1105
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
1106
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1107
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1108
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1109
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1110
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1111
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1112
POSSIBILITY OF SUCH DAMAGE.
1113
*/
1114
1115
/*
1116
Links:
1117
- https://gitlab.com/vgg/via/blob/master/Contributors.md : list of developers who have contributed code to the VIA project.
1118
- https://gitlab.com/vgg/via/blob/master/CodeDoc.md : source code documentation
1119
- https://gitlab.com/vgg/via/blob/master/CONTRIBUTING.md : guide for contributors
1120
1121
This source code can be grouped into the following categories:
1122
- Data structure for annotations
1123
- Initialization routine
1124
- Handlers for top navigation bar
1125
- Local file uploaders
1126
- Data Importer
1127
- Data Exporter
1128
- Maintainers of user interface
1129
- Image click handlers
1130
- Canvas update routines
1131
- Region collision routines
1132
- Shortcut key handlers
1133
- Persistence of annotation data in browser cache (i.e. localStorage)
1134
- Handlers for attributes input panel (spreadsheet like user input panel)
1135
*/
1136
1137
"use strict";
1138
1139
var VIA_VERSION = '2.0.12';
1140
var VIA_NAME = 'VGG Image Annotator';
1141
var VIA_SHORT_NAME = 'VIA';
1142
var VIA_REGION_SHAPE = { RECT:'rect',
1143
CIRCLE:'circle',
1144
ELLIPSE:'ellipse',
1145
POLYGON:'polygon',
1146
POINT:'point',
1147
POLYLINE:'polyline'
1148
};
1149
1150
var VIA_ATTRIBUTE_TYPE = { TEXT:'text',
1151
CHECKBOX:'checkbox',
1152
RADIO:'radio',
1153
IMAGE:'image',
1154
DROPDOWN:'dropdown'
1155
};
1156
1157
var VIA_DISPLAY_AREA_CONTENT_NAME = {IMAGE:'image_panel',
1158
IMAGE_GRID:'image_grid_panel',
1159
SETTINGS:'settings_panel',
1160
PAGE_404:'page_404',
1161
PAGE_GETTING_STARTED:'page_getting_started',
1162
PAGE_ABOUT:'page_about',
1163
PAGE_START_INFO:'page_start_info',
1164
PAGE_LICENSE:'page_license'
1165
};
1166
1167
var VIA_ANNOTATION_EDITOR_MODE = {SINGLE_REGION:'single_region',
1168
ALL_REGIONS:'all_regions'};
1169
var VIA_ANNOTATION_EDITOR_PLACEMENT = {NEAR_REGION:'NEAR_REGION',
1170
IMAGE_BOTTOM:'IMAGE_BOTTOM',
1171
DISABLE:'DISABLE'};
1172
1173
var VIA_REGION_EDGE_TOL = 5; // pixel
1174
var VIA_REGION_CONTROL_POINT_SIZE = 2;
1175
var VIA_POLYGON_VERTEX_MATCH_TOL = 10;
1176
var VIA_REGION_MIN_DIM = 3;
1177
var VIA_MOUSE_CLICK_TOL = 2;
1178
var VIA_ELLIPSE_EDGE_TOL = 0.2; // euclidean distance
1179
var VIA_THETA_TOL = Math.PI/18; // 10 degrees
1180
var VIA_POLYGON_RESIZE_VERTEX_OFFSET = 100;
1181
var VIA_CANVAS_DEFAULT_ZOOM_LEVEL_INDEX = 3;
1182
var VIA_CANVAS_ZOOM_LEVELS = [0.25, 0.5, 0.75, 1.0, 1.5, 2.0, 2.5, 3.0, 4, 5, 6, 7, 8, 9, 10];
1183
var VIA_REGION_COLOR_LIST = ["#E69F00", "#56B4E9", "#009E73", "#D55E00", "#CC79A7", "#F0E442", "#ffffff"];
1184
// radius of control points in all shapes
1185
var VIA_REGION_SHAPES_POINTS_RADIUS = 3;
1186
// radius of control points in a point
1187
var VIA_REGION_POINT_RADIUS = 3;
1188
var VIA_REGION_POINT_RADIUS_DEFAULT = 3;
1189
1190
var VIA_THEME_REGION_BOUNDARY_WIDTH = 3;
1191
var VIA_THEME_BOUNDARY_LINE_COLOR = "black";
1192
var VIA_THEME_BOUNDARY_FILL_COLOR = "yellow";
1193
var VIA_THEME_SEL_REGION_FILL_COLOR = "#808080";
1194
var VIA_THEME_SEL_REGION_FILL_BOUNDARY_COLOR = "yellow";
1195
var VIA_THEME_SEL_REGION_OPACITY = 0.5;
1196
var VIA_THEME_MESSAGE_TIMEOUT_MS = 6000;
1197
var VIA_THEME_CONTROL_POINT_COLOR = '#ff0000';
1198
1199
var VIA_CSV_SEP = ',';
1200
var VIA_CSV_QUOTE_CHAR = '"';
1201
var VIA_CSV_KEYVAL_SEP = ':';
1202
1203
var _via_img_metadata = {}; // data structure to store loaded images metadata
1204
var _via_img_src = {}; // image content {abs. path, url, base64 data, etc}
1205
var _via_img_fileref = {}; // reference to local images selected by using browser file selector
1206
var _via_img_count = 0; // count of the loaded images
1207
var _via_canvas_regions = []; // image regions spec. in canvas space
1208
var _via_canvas_scale = 1.0;// current scale of canvas image
1209
1210
var _via_image_id = ''; // id={filename+length} of current image
1211
var _via_image_index = -1; // index
1212
1213
var _via_current_image_filename;
1214
var _via_current_image;
1215
var _via_current_image_width;
1216
var _via_current_image_height;
1217
1218
// a record of image statistics (e.g. width, height)
1219
var _via_img_stat = {};
1220
var _via_is_all_img_stat_read_ongoing = false;
1221
var _via_img_stat_current_img_index = false;
1222
1223
// image canvas
1224
var _via_display_area = document.getElementById('display_area');
1225
var _via_img_panel = document.getElementById('image_panel');
1226
var _via_reg_canvas = document.getElementById('region_canvas');
1227
var _via_reg_ctx; // initialized in _via_init()
1228
var _via_canvas_width, _via_canvas_height;
1229
1230
// canvas zoom
1231
var _via_canvas_zoom_level_index = VIA_CANVAS_DEFAULT_ZOOM_LEVEL_INDEX; // 1.0
1232
var _via_canvas_scale_without_zoom = 1.0;
1233
1234
// state of the application
1235
var _via_is_user_drawing_region = false;
1236
var _via_current_image_loaded = false;
1237
var _via_is_window_resized = false;
1238
var _via_is_user_resizing_region = false;
1239
var _via_is_user_moving_region = false;
1240
var _via_is_user_drawing_polygon = false;
1241
var _via_is_region_selected = false;
1242
var _via_is_all_region_selected = false;
1243
var _via_is_loaded_img_list_visible = false;
1244
var _via_is_attributes_panel_visible = false;
1245
var _via_is_reg_attr_panel_visible = false;
1246
var _via_is_file_attr_panel_visible = false;
1247
var _via_is_canvas_zoomed = false;
1248
var _via_is_loading_current_image = false;
1249
var _via_is_region_id_visible = true;
1250
var _via_is_region_boundary_visible = true;
1251
var _via_is_region_info_visible = false;
1252
var _via_is_ctrl_pressed = false;
1253
var _via_is_debug_mode = false;
1254
var _via_is_message_visible = true;
1255
1256
// region
1257
var _via_current_shape = VIA_REGION_SHAPE.RECT;
1258
var _via_current_polygon_region_id = -1;
1259
var _via_user_sel_region_id = -1;
1260
var _via_click_x0 = 0; var _via_click_y0 = 0;
1261
var _via_click_x1 = 0; var _via_click_y1 = 0;
1262
var _via_region_click_x, _via_region_click_y;
1263
var _via_region_edge = [-1, -1];
1264
var _via_current_x = 0; var _via_current_y = 0;
1265
1266
// region copy/paste
1267
var _via_region_selected_flag = []; // region select flag for current image
1268
var _via_copied_image_regions = [];
1269
var _via_paste_to_multiple_images_input;
1270
1271
// message
1272
var _via_message_clear_timer;
1273
1274
// attributes
1275
var _via_attribute_being_updated = 'region'; // {region, file}
1276
var _via_attributes = { 'region':{}, 'file':{} };
1277
var _via_current_attribute_id = '';
1278
1279
// region group color
1280
var _via_canvas_regions_group_color = {}; // color of each region
1281
1282
// invoke a method after receiving user input
1283
var _via_user_input_ok_handler = null;
1284
var _via_user_input_cancel_handler = null;
1285
var _via_user_input_data = {};
1286
1287
// annotation editor
1288
var _via_annotaion_editor_panel = document.getElementById('annotation_editor_panel');
1289
var _via_metadata_being_updated = 'region'; // {region, file}
1290
var _via_annotation_editor_mode = VIA_ANNOTATION_EDITOR_MODE.SINGLE_REGION;
1291
1292
// persistence to local storage
1293
var _via_is_local_storage_available = false;
1294
var _via_is_save_ongoing = false;
1295
1296
// all the image_id and image_filename of images added by the user is
1297
// stored in _via_image_id_list and _via_image_filename_list
1298
//
1299
// Image filename list (img_fn_list) contains a filtered list of images
1300
// currently accessible by the user. The img_fn_list is visible in the
1301
// left side toolbar. image_grid, next/prev, etc operations depend on
1302
// the contents of _via_img_fn_list_img_index_list.
1303
var _via_image_id_list = []; // array of all image id (in order they were added by user)
1304
var _via_image_filename_list = []; // array of all image filename
1305
var _via_image_load_error = []; // {true, false}
1306
var _via_image_filepath_resolved = []; // {true, false}
1307
var _via_image_filepath_id_list = []; // path for each file
1308
1309
var _via_reload_img_fn_list_table = true;
1310
var _via_img_fn_list_img_index_list = []; // image index list of images show in img_fn_list
1311
var _via_img_fn_list_html = []; // html representation of image filename list
1312
1313
// image grid
1314
var image_grid_panel = document.getElementById('image_grid_panel');
1315
var _via_display_area_content_name = ''; // describes what is currently shown in display area
1316
var _via_display_area_content_name_prev = '';
1317
var _via_image_grid_requires_update = false;
1318
var _via_image_grid_content_overflow = false;
1319
var _via_image_grid_load_ongoing = false;
1320
var _via_image_grid_page_first_index = 0; // array index in _via_img_fn_list_img_index_list[]
1321
var _via_image_grid_page_last_index = -1;
1322
var _via_image_grid_selected_img_index_list = [];
1323
var _via_image_grid_page_img_index_list = []; // list of all image index in current page of image grid
1324
var _via_image_grid_visible_img_index_list = []; // list of images currently visible in grid
1325
var _via_image_grid_mousedown_img_index = -1;
1326
var _via_image_grid_mouseup_img_index = -1;
1327
var _via_image_grid_img_index_list = []; // list of all image index in the image grid
1328
var _via_image_grid_region_index_list = []; // list of all image index in the image grid
1329
var _via_image_grid_group = {}; // {'value':[image_index_list]}
1330
var _via_image_grid_group_var = []; // {type, name, value}
1331
var _via_image_grid_group_show_all = false;
1332
var _via_image_grid_stack_prev_page = []; // stack of first img index of every page navigated so far
1333
1334
// image buffer
1335
var VIA_IMG_PRELOAD_INDICES = [1, -1, 2, 3, -2, 4]; // for any image, preload previous 2 and next 4 images
1336
var VIA_IMG_PRELOAD_COUNT = 4;
1337
var _via_buffer_preload_img_index = -1;
1338
var _via_buffer_img_index_list = [];
1339
var _via_buffer_img_shown_timestamp = [];
1340
var _via_preload_img_promise_list = [];
1341
1342
// via settings
1343
var _via_settings = {};
1344
_via_settings.ui = {};
1345
_via_settings.ui.annotation_editor_height = 25; // in percent of the height of browser window
1346
_via_settings.ui.annotation_editor_fontsize = 0.8;// in rem
1347
_via_settings.ui.leftsidebar_width = 18; // in rem
1348
1349
_via_settings.ui.image_grid = {};
1350
_via_settings.ui.image_grid.img_height = 80; // in pixel
1351
_via_settings.ui.image_grid.rshape_fill = 'none';
1352
_via_settings.ui.image_grid.rshape_fill_opacity = 0.3;
1353
_via_settings.ui.image_grid.rshape_stroke = 'yellow';
1354
_via_settings.ui.image_grid.rshape_stroke_width = 2;
1355
_via_settings.ui.image_grid.show_region_shape = true;
1356
_via_settings.ui.image_grid.show_image_policy = 'all';
1357
1358
_via_settings.ui.image = {};
1359
_via_settings.ui.image.region_label = '__via_region_id__'; // default: region_id
1360
_via_settings.ui.image.region_color = '__via_default_region_color__'; // default color: yellow
1361
_via_settings.ui.image.region_label_font = '10px Sans';
1362
_via_settings.ui.image.on_image_annotation_editor_placement = VIA_ANNOTATION_EDITOR_PLACEMENT.NEAR_REGION;
1363
1364
_via_settings.core = {};
1365
_via_settings.core.buffer_size = 4*VIA_IMG_PRELOAD_COUNT + 2;
1366
_via_settings.core.filepath = {};
1367
_via_settings.core.default_filepath = '';
1368
1369
// UI html elements
1370
var invisible_file_input = document.getElementById("invisible_file_input");
1371
var display_area = document.getElementById("display_area");
1372
var ui_top_panel = document.getElementById("ui_top_panel");
1373
var image_panel = document.getElementById("image_panel");
1374
var img_buffer_now = document.getElementById("img_buffer_now");
1375
1376
var annotation_list_snippet = document.getElementById("annotation_list_snippet");
1377
var annotation_textarea = document.getElementById("annotation_textarea");
1378
1379
var img_fn_list_panel = document.getElementById('img_fn_list_panel');
1380
var img_fn_list = document.getElementById('img_fn_list');
1381
var attributes_panel = document.getElementById('attributes_panel');
1382
var leftsidebar = document.getElementById('leftsidebar');
1383
1384
var BBOX_LINE_WIDTH = 4;
1385
var BBOX_SELECTED_OPACITY = 0.3;
1386
var BBOX_BOUNDARY_FILL_COLOR_ANNOTATED = "#f2f2f2";
1387
var BBOX_BOUNDARY_FILL_COLOR_NEW = "#aaeeff";
1388
var BBOX_BOUNDARY_LINE_COLOR = "#1a1a1a";
1389
var BBOX_SELECTED_FILL_COLOR = "#ffffff";
1390
1391
var VIA_ANNOTATION_EDITOR_HEIGHT_CHANGE = 5; // in percent
1392
var VIA_ANNOTATION_EDITOR_FONTSIZE_CHANGE = 0.1; // in rem
1393
var VIA_IMAGE_GRID_IMG_HEIGHT_CHANGE = 20; // in percent
1394
var VIA_LEFTSIDEBAR_WIDTH_CHANGE = 1; // in rem
1395
var VIA_POLYGON_SEGMENT_SUBTENDED_ANGLE = 5; // in degree (used to approximate shapes using polygon)
1396
var VIA_FLOAT_PRECISION = 3; // number of decimal places to include in float values
1397
1398
// COCO Export
1399
var VIA_COCO_EXPORT_RSHAPE = ['rect', 'circle', 'ellipse', 'polygon', 'point'];
1400
var VIA_COCO_EXPORT_ATTRIBUTE_TYPE = [VIA_ATTRIBUTE_TYPE.DROPDOWN,
1401
VIA_ATTRIBUTE_TYPE.RADIO];
1402
//
1403
// Data structure to store metadata about file and regions
1404
//
1405
function file_metadata(filename, size) {
1406
this.filename = filename;
1407
this.size = size; // file size in bytes
1408
this.regions = []; // array of file_region()
1409
this.file_attributes = {}; // image attributes
1410
}
1411
1412
function file_region() {
1413
this.shape_attributes = {}; // region shape attributes
1414
this.region_attributes = {}; // region attributes
1415
}
1416
1417
//
1418
// Initialization routine
1419
//
1420
function _via_init() {
1421
console.log(VIA_NAME);
1422
show_message(VIA_NAME + ' (' + VIA_SHORT_NAME + ') version ' + VIA_VERSION +
1423
'. Ready !', 2*VIA_THEME_MESSAGE_TIMEOUT_MS);
1424
1425
if ( _via_is_debug_mode ) {
1426
document.getElementById('ui_top_panel').innerHTML += '<span>DEBUG MODE</span>';
1427
}
1428
1429
document.getElementById('img_fn_list').style.display = 'block';
1430
document.getElementById('leftsidebar').style.display = 'table-cell';
1431
1432
// initialize default project
1433
project_init_default_project();
1434
1435
// initialize region canvas 2D context
1436
_via_init_reg_canvas_context();
1437
1438
// initialize user input handlers (for both window and via_reg_canvas)
1439
// handles drawing of regions by user over the image
1440
_via_init_keyboard_handlers();
1441
_via_init_mouse_handlers();
1442
1443
// initialize image grid
1444
image_grid_init();
1445
1446
show_single_image_view();
1447
init_leftsidebar_accordion();
1448
attribute_update_panel_set_active_button();
1449
annotation_editor_set_active_button();
1450
init_message_panel();
1451
1452
// run attached sub-modules (if any)
1453
// e.g. demo modules
1454
if (typeof _via_load_submodules === 'function') {
1455
console.log('Loading VIA submodule');
1456
setTimeout( async function() {
1457
await _via_load_submodules();
1458
}, 100);
1459
}
1460
1461
}
1462
1463
function _via_init_reg_canvas_context() {
1464
_via_reg_ctx = _via_reg_canvas.getContext('2d');
1465
}
1466
1467
function _via_init_keyboard_handlers() {
1468
window.addEventListener('keydown', _via_window_keydown_handler, false);
1469
_via_reg_canvas.addEventListener('keydown', _via_reg_canvas_keydown_handler, false);
1470
_via_reg_canvas.addEventListener('keyup', _via_reg_canvas_keyup_handler, false);
1471
}
1472
1473
// handles drawing of regions over image by the user
1474
function _via_init_mouse_handlers() {
1475
_via_reg_canvas.addEventListener('dblclick', _via_reg_canvas_dblclick_handler, false);
1476
_via_reg_canvas.addEventListener('mousedown', _via_reg_canvas_mousedown_handler, false);
1477
_via_reg_canvas.addEventListener('mouseup', _via_reg_canvas_mouseup_handler, false);
1478
_via_reg_canvas.addEventListener('mouseover', _via_reg_canvas_mouseover_handler, false);
1479
_via_reg_canvas.addEventListener('mousemove', _via_reg_canvas_mousemove_handler, false);
1480
_via_reg_canvas.addEventListener('wheel', _via_reg_canvas_mouse_wheel_listener, false);
1481
// touch screen event handlers
1482
// @todo: adapt for mobile users
1483
_via_reg_canvas.addEventListener('touchstart', _via_reg_canvas_mousedown_handler, false);
1484
_via_reg_canvas.addEventListener('touchend', _via_reg_canvas_mouseup_handler, false);
1485
_via_reg_canvas.addEventListener('touchmove', _via_reg_canvas_mousemove_handler, false);
1486
}
1487
1488
//
1489
// Download image with annotations
1490
//
1491
1492
function download_as_image() {
1493
if ( _via_display_area_content_name !== VIA_DISPLAY_AREA_CONTENT_NAME['IMAGE'] ) {
1494
show_message('This functionality is only available in single image view mode');
1495
return;
1496
} else {
1497
var c = document.createElement('canvas');
1498
1499
// ensures that downloaded image is scaled at current zoom level
1500
c.width = _via_reg_canvas.width;
1501
c.height = _via_reg_canvas.height;
1502
1503
var ct = c.getContext('2d');
1504
// draw current image
1505
ct.drawImage(_via_current_image, 0, 0, _via_reg_canvas.width, _via_reg_canvas.height);
1506
// draw current regions
1507
ct.drawImage(_via_reg_canvas, 0, 0);
1508
1509
var cur_img_mime = 'image/jpeg';
1510
if ( _via_current_image.src.startsWith('data:') ) {
1511
var c1 = _via_current_image.src.indexOf(':', 0);
1512
var c2 = _via_current_image.src.indexOf(';', c1);
1513
cur_img_mime = _via_current_image.src.substring(c1 + 1, c2);
1514
}
1515
1516
// extract image data from canvas
1517
var saved_img = c.toDataURL(cur_img_mime);
1518
saved_img.replace(cur_img_mime, "image/octet-stream");
1519
1520
// simulate user click to trigger download of image
1521
var a = document.createElement('a');
1522
a.href = saved_img;
1523
a.target = '_blank';
1524
a.download = _via_current_image_filename;
1525
1526
// simulate a mouse click event
1527
var event = new MouseEvent('click', {
1528
view: window,
1529
bubbles: true,
1530
cancelable: true
1531
});
1532
1533
a.dispatchEvent(event);
1534
}
1535
}
1536
1537
//
1538
// Display area content
1539
//
1540
function clear_display_area() {
1541
var panels = document.getElementsByClassName('display_area_content');
1542
var i;
1543
for ( i = 0; i < panels.length; ++i ) {
1544
panels[i].classList.add('display_none');
1545
}
1546
}
1547
1548
function is_content_name_valid(content_name) {
1549
var e;
1550
for ( e in VIA_DISPLAY_AREA_CONTENT_NAME ) {
1551
if ( VIA_DISPLAY_AREA_CONTENT_NAME[e] === content_name ) {
1552
return true;
1553
}
1554
}
1555
return false;
1556
}
1557
1558
function show_home_panel() {
1559
show_single_image_view();
1560
}
1561
1562
function set_display_area_content(content_name) {
1563
if ( is_content_name_valid(content_name) ) {
1564
_via_display_area_content_name_prev = _via_display_area_content_name;
1565
clear_display_area();
1566
var p = document.getElementById(content_name);
1567
p.classList.remove('display_none');
1568
_via_display_area_content_name = content_name;
1569
}
1570
}
1571
1572
function show_single_image_view() {
1573
if (_via_current_image_loaded) {
1574
img_fn_list_clear_all_style();
1575
_via_show_img(_via_image_index);
1576
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE);
1577
annotation_editor_update_content();
1578
1579
var p = document.getElementById('toolbar_image_grid_toggle');
1580
p.firstChild.setAttribute('xlink:href', '#icon_gridon');
1581
p.childNodes[1].innerHTML = 'Switch to Image Grid View';
1582
} else {
1583
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_START_INFO);
1584
}
1585
}
1586
1587
function show_image_grid_view() {
1588
if (_via_current_image_loaded) {
1589
img_fn_list_clear_all_style();
1590
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID);
1591
image_grid_toolbar_update_group_by_select();
1592
1593
if ( _via_image_grid_group_var.length === 0 ) {
1594
image_grid_show_all_project_images();
1595
}
1596
annotation_editor_update_content();
1597
1598
var p = document.getElementById('toolbar_image_grid_toggle');
1599
p.firstChild.setAttribute('xlink:href', '#icon_gridoff');
1600
p.childNodes[1].innerHTML = 'Switch to Single Image View';
1601
1602
//edit_file_metadata_in_annotation_editor();
1603
} else {
1604
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_START_INFO);
1605
}
1606
}
1607
1608
//
1609
// Handlers for top navigation bar
1610
//
1611
function sel_local_images() {
1612
// source: https://developer.mozilla.org/en-US/docs/Using_files_from_web_applications
1613
if (invisible_file_input) {
1614
invisible_file_input.setAttribute('multiple', 'multiple');
1615
invisible_file_input.accept = '.jpg,.jpeg,.png,.bmp';
1616
invisible_file_input.onchange = project_file_add_local;
1617
invisible_file_input.click();
1618
}
1619
}
1620
1621
// invoked by menu-item buttons in HTML UI
1622
function download_all_region_data(type, file_extension) {
1623
if ( typeof(file_extension) === 'undefined' ) {
1624
file_extension = type;
1625
}
1626
// Javascript strings (DOMString) is automatically converted to utf-8
1627
// see: https://developer.mozilla.org/en-US/docs/Web/API/Blob/Blob
1628
pack_via_metadata(type).then( function(data) {
1629
var blob_attr = {type: 'text/'+file_extension+';charset=utf-8'};
1630
var all_region_data_blob = new Blob(data, blob_attr);
1631
1632
var filename = 'via_export';
1633
if(typeof(_via_settings) !== 'undefined' &&
1634
_via_settings.hasOwnProperty('project') &&
1635
_via_settings['project']['name'] !== '') {
1636
filename = _via_settings['project']['name'];
1637
}
1638
if ( file_extension !== 'csv' || file_extension !== 'json' ) {
1639
filename += '_' + type + '.' + file_extension;
1640
}
1641
save_data_to_local_file(all_region_data_blob, filename);
1642
}.bind(this), function(err) {
1643
show_message('Failed to download data: [' + err + ']');
1644
}.bind(this));
1645
}
1646
1647
function sel_local_data_file(type) {
1648
if (invisible_file_input) {
1649
switch(type) {
1650
case 'annotations':
1651
invisible_file_input.accept='.csv,.json';
1652
invisible_file_input.onchange = import_annotations_from_file;
1653
break;
1654
1655
case 'annotations_coco':
1656
invisible_file_input.accept='.json';
1657
invisible_file_input.onchange = load_coco_annotations_json_file;
1658
break;
1659
1660
case 'files_url':
1661
invisible_file_input.accept='';
1662
invisible_file_input.onchange = import_files_url_from_file;
1663
break;
1664
1665
case 'attributes':
1666
invisible_file_input.accept='json';
1667
invisible_file_input.onchange = project_import_attributes_from_file;
1668
break;
1669
1670
default:
1671
console.log('sel_local_data_file() : unknown type ' + type);
1672
return;
1673
}
1674
invisible_file_input.removeAttribute('multiple');
1675
invisible_file_input.click();
1676
}
1677
}
1678
1679
//
1680
// Data Importer
1681
//
1682
function import_files_url_from_file(event) {
1683
var selected_files = event.target.files;
1684
var i, file;
1685
for ( i = 0; i < selected_files.length; ++i ) {
1686
file = selected_files[i];
1687
load_text_file(file, import_files_url_from_csv);
1688
}
1689
}
1690
1691
function import_annotations_from_file(event) {
1692
var selected_files = event.target.files;
1693
var i, file;
1694
for ( i = 0; i < selected_files.length; ++i ) {
1695
file = selected_files[i];
1696
switch ( file.type ) {
1697
case '': // Fall-through // Windows 10: Firefox and Chrome do not report filetype
1698
show_message('File type for ' + file.name + ' cannot be determined! Assuming text/plain.');
1699
case 'text/plain': // Fall-through
1700
case 'application/vnd.ms-excel': // Fall-through // @todo: filetype of VIA csv annotations in Windows 10 , fix this (reported by @Eli Walker)
1701
case 'text/csv':
1702
load_text_file(file, import_annotations_from_csv);
1703
break;
1704
1705
case 'text/json': // Fall-through
1706
case 'application/json':
1707
load_text_file(file, import_annotations_from_json);
1708
break;
1709
1710
default:
1711
show_message('Annotations cannot be imported from file of type ' + file.type);
1712
break;
1713
}
1714
}
1715
}
1716
1717
function load_coco_annotations_json_file(event) {
1718
load_text_file(event.target.files[0], import_coco_annotations_from_json);
1719
}
1720
1721
function import_annotations_from_csv(data) {
1722
return new Promise( function(ok_callback, err_callback) {
1723
if ( data === '' || typeof(data) === 'undefined') {
1724
err_callback();
1725
}
1726
1727
var region_import_count = 0;
1728
var malformed_csv_lines_count = 0;
1729
var file_added_count = 0;
1730
1731
var line_split_regex = new RegExp('\n|\r|\r\n', 'g');
1732
var csvdata = data.split(line_split_regex);
1733
1734
var parsed_header = parse_csv_header_line(csvdata[0]);
1735
if ( ! parsed_header.is_header ) {
1736
show_message('Header line missing in the CSV file');
1737
err_callback();
1738
return;
1739
}
1740
1741
var n = csvdata.length;
1742
var i;
1743
var first_img_id = '';
1744
for ( i = 1; i < n; ++i ) {
1745
// ignore blank lines
1746
if (csvdata[i].charAt(0) === '\n' || csvdata[i].charAt(0) === '') {
1747
continue;
1748
}
1749
1750
var d = parse_csv_line(csvdata[i]);
1751
1752
// check if csv line was malformed
1753
if ( d.length !== parsed_header.csv_column_count ) {
1754
malformed_csv_lines_count += 1;
1755
continue;
1756
}
1757
1758
var filename = d[parsed_header.filename_index];
1759
var size = d[parsed_header.size_index];
1760
var img_id = _via_get_image_id(filename, size);
1761
1762
// check if file is already present in this project
1763
if ( ! _via_img_metadata.hasOwnProperty(img_id) ) {
1764
img_id = project_add_new_file(filename, size);
1765
if ( _via_settings.core.default_filepath === '' ) {
1766
_via_img_src[img_id] = filename;
1767
} else {
1768
_via_file_resolve_file_to_default_filepath(img_id);
1769
}
1770
file_added_count += 1;
1771
1772
if ( first_img_id === '' ) {
1773
first_img_id = img_id;
1774
}
1775
}
1776
1777
// copy file attributes
1778
if ( d[parsed_header.file_attr_index] !== '"{}"') {
1779
var fattr = d[parsed_header.file_attr_index];
1780
fattr = remove_prefix_suffix_quotes( fattr );
1781
fattr = unescape_from_csv( fattr );
1782
1783
var m = json_str_to_map( fattr );
1784
for( var key in m ) {
1785
_via_img_metadata[img_id].file_attributes[key] = m[key];
1786
1787
// add this file attribute to _via_attributes
1788
if ( ! _via_attributes['file'].hasOwnProperty(key) ) {
1789
_via_attributes['file'][key] = { 'type':'text' };
1790
}
1791
}
1792
}
1793
1794
var region_i = new file_region();
1795
// copy regions shape attributes
1796
if ( d[parsed_header.region_shape_attr_index] !== '"{}"' ) {
1797
var sattr = d[parsed_header.region_shape_attr_index];
1798
sattr = remove_prefix_suffix_quotes( sattr );
1799
sattr = unescape_from_csv( sattr );
1800
1801
var m = json_str_to_map( sattr );
1802
for ( var key in m ) {
1803
region_i.shape_attributes[key] = m[key];
1804
}
1805
}
1806
1807
// copy region attributes
1808
if ( d[parsed_header.region_attr_index] !== '"{}"' ) {
1809
var rattr = d[parsed_header.region_attr_index];
1810
rattr = remove_prefix_suffix_quotes( rattr );
1811
rattr = unescape_from_csv( rattr );
1812
1813
var m = json_str_to_map( rattr );
1814
for ( var key in m ) {
1815
region_i.region_attributes[key] = m[key];
1816
1817
// add this region attribute to _via_attributes
1818
if ( ! _via_attributes['region'].hasOwnProperty(key) ) {
1819
_via_attributes['region'][key] = { 'type':'text' };
1820
}
1821
}
1822
}
1823
1824
// add regions only if they are present
1825
if (Object.keys(region_i.shape_attributes).length > 0 ||
1826
Object.keys(region_i.region_attributes).length > 0 ) {
1827
_via_img_metadata[img_id].regions.push(region_i);
1828
region_import_count += 1;
1829
}
1830
}
1831
show_message('Import Summary : [' + file_added_count + '] new files, ' +
1832
'[' + region_import_count + '] regions, ' +
1833
'[' + malformed_csv_lines_count + '] malformed csv lines.');
1834
1835
if ( file_added_count ) {
1836
update_img_fn_list();
1837
}
1838
1839
if ( _via_current_image_loaded ) {
1840
if ( region_import_count ) {
1841
update_attributes_update_panel();
1842
annotation_editor_update_content();
1843
_via_load_canvas_regions(); // image to canvas space transform
1844
_via_redraw_reg_canvas();
1845
_via_reg_canvas.focus();
1846
}
1847
} else {
1848
if ( file_added_count ) {
1849
var first_img_index = _via_image_id_list.indexOf(first_img_id);
1850
_via_show_img( first_img_index );
1851
}
1852
}
1853
ok_callback([file_added_count, region_import_count, malformed_csv_lines_count]);
1854
});
1855
}
1856
1857
function parse_csv_header_line(line) {
1858
var header_via_10x = '#filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes'; // VIA versions 1.0.x
1859
var header_via_11x = 'filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes'; // VIA version 1.1.x
1860
1861
if ( line === header_via_10x || line === header_via_11x ) {
1862
return { 'is_header':true,
1863
'filename_index': 0,
1864
'size_index': 1,
1865
'file_attr_index': 2,
1866
'region_shape_attr_index': 5,
1867
'region_attr_index': 6,
1868
'csv_column_count': 7
1869
}
1870
} else {
1871
return { 'is_header':false };
1872
}
1873
}
1874
1875
// see http://cocodataset.org/#format-data
1876
function import_coco_annotations_from_json(data_str) {
1877
return new Promise( function(ok_callback, err_callback) {
1878
if (data_str === '' || typeof(data_str) === 'undefined') {
1879
show_message('Empty file');
1880
return;
1881
}
1882
var coco = JSON.parse(data_str);
1883
if( !coco.hasOwnProperty('info') ||
1884
!coco.hasOwnProperty('categories') ||
1885
!coco.hasOwnProperty('annotations') ||
1886
!coco.hasOwnProperty('images') ) {
1887
show_message('File does not contain valid annotations in COCO format.');
1888
return;
1889
}
1890
1891
// create _via_attributes from coco['categories']
1892
var category_id_to_attribute_name = {};
1893
for( var i in coco['categories'] ) {
1894
var sc = coco['categories'][i]['supercategory'];
1895
var cid = coco['categories'][i]['id'];
1896
var cname = coco['categories'][i]['name'];
1897
if( !_via_attributes['region'].hasOwnProperty(sc)) {
1898
_via_attributes['region'][sc] = {'type':VIA_ATTRIBUTE_TYPE.RADIO,
1899
'description':'coco["categories"][' + i + ']=' + JSON.stringify(coco['categories'][i]),
1900
'options':{},
1901
'default_options':{}
1902
};
1903
}
1904
_via_attributes['region'][sc]['options'][cid] = cname;
1905
category_id_to_attribute_name[cid] = sc;
1906
}
1907
// if more than 5 options, convert the attribute type to DROPDOWN
1908
for( var attr_name in _via_attributes['region'] ) {
1909
if( Object.keys(_via_attributes['region'][attr_name]['options']).length > 5 ) {
1910
_via_attributes['region'][attr_name]['type'] = VIA_ATTRIBUTE_TYPE.DROPDOWN;
1911
}
1912
}
1913
1914
// create an map of image_id and their annotations
1915
var image_id_to_annotation_index = {};
1916
for ( var annotation_index in coco['annotations'] ) {
1917
var coco_image_id = coco.annotations[annotation_index]['image_id'];
1918
if ( !image_id_to_annotation_index.hasOwnProperty(coco_image_id) ) {
1919
image_id_to_annotation_index[coco_image_id] = [];
1920
}
1921
image_id_to_annotation_index[coco_image_id].push( annotation_index );
1922
}
1923
1924
// add all files and annotations
1925
_via_img_metadata = {};
1926
_via_image_id_list = [];
1927
_via_image_filename_list = [];
1928
_via_img_count = 0;
1929
var imported_file_count = 0;
1930
var imported_region_count = 0;
1931
for ( var coco_img_index in coco['images'] ) {
1932
var coco_img_id = coco['images'][coco_img_index]['id'];
1933
var filename;
1934
if ( coco.images[coco_img_index].hasOwnProperty('coco_url') &&
1935
coco.images[coco_img_index]['coco_url'] !== "") {
1936
filename = coco.images[coco_img_index]['coco_url'];
1937
} else {
1938
filename = coco.images[coco_img_index]['file_name'];
1939
}
1940
_via_img_metadata[coco_img_id] = { 'filename':filename,
1941
'size' :-1,
1942
'regions' :[],
1943
'file_attributes': {
1944
'width' :coco.images[coco_img_index]['width'],
1945
'height':coco.images[coco_img_index]['height']
1946
},
1947
};
1948
_via_image_id_list.push(coco_img_id);
1949
_via_image_filename_list.push(filename);
1950
_via_img_count = _via_img_count + 1;
1951
1952
// add all annotations associated with this file
1953
if ( image_id_to_annotation_index.hasOwnProperty(coco_img_id) ) {
1954
for ( var i in image_id_to_annotation_index[coco_img_id] ) {
1955
var annotation_i = coco['annotations'][ image_id_to_annotation_index[coco_img_id][i] ];
1956
var bbox_from_polygon = polygon_to_bbox(annotation_i['segmentation'][0]);
1957
1958
// ensure rectangles get imported as rectangle (and not as polygon)
1959
var is_rectangle = true;
1960
for (var j = 0; j < annotation_i['bbox'].length; ++j) {
1961
if (annotation_i['bbox'][j] !== bbox_from_polygon[j]) {
1962
is_rectangle = false;
1963
break;
1964
}
1965
}
1966
1967
var region_i = { 'shape_attributes': {}, 'region_attributes': {} };
1968
var attribute_name = category_id_to_attribute_name[ annotation_i['category_id'] ];
1969
var attribute_value = annotation_i['category_id'].toString();
1970
region_i['region_attributes'][attribute_name] = attribute_value;
1971
1972
if ( annotation_i['segmentation'][0].length === 8 && is_rectangle ) {
1973
region_i['shape_attributes'] = { 'name':'rect',
1974
'x': annotation_i['bbox'][0],
1975
'y': annotation_i['bbox'][1],
1976
'width': annotation_i['bbox'][2],
1977
'height': annotation_i['bbox'][3]};
1978
} else {
1979
region_i['shape_attributes'] = { 'name':'polygon',
1980
'all_points_x':[],
1981
'all_points_y':[]};
1982
for ( var j = 0; j < annotation_i['segmentation'][0].length; j = j + 2 ) {
1983
region_i['shape_attributes']['all_points_x'].push( annotation_i['segmentation'][0][j] );
1984
region_i['shape_attributes']['all_points_y'].push( annotation_i['segmentation'][0][j+1] );
1985
}
1986
}
1987
_via_img_metadata[coco_img_id]['regions'].push(region_i);
1988
imported_region_count = imported_region_count + 1;
1989
}
1990
}
1991
}
1992
show_message('Import Summary : [' + _via_img_count + '] new files, ' +
1993
'[' + imported_region_count + '] regions.');
1994
1995
if(_via_img_count) {
1996
update_img_fn_list();
1997
}
1998
1999
if(_via_current_image_loaded) {
2000
if(imported_region_count) {
2001
update_attributes_update_panel();
2002
annotation_editor_update_content();
2003
_via_load_canvas_regions(); // image to canvas space transform
2004
_via_redraw_reg_canvas();
2005
_via_reg_canvas.focus();
2006
}
2007
} else {
2008
if(_via_img_count) {
2009
_via_show_img(0);
2010
}
2011
}
2012
ok_callback([_via_img_count, imported_region_count, 0]);
2013
});
2014
}
2015
2016
function import_annotations_from_json(data_str) {
2017
return new Promise( function(ok_callback, err_callback) {
2018
if (data_str === '' || typeof(data_str) === 'undefined') {
2019
return;
2020
}
2021
2022
var d = JSON.parse(data_str);
2023
var region_import_count = 0;
2024
var file_added_count = 0;
2025
var malformed_entries_count = 0;
2026
for (var img_id in d) {
2027
if ( ! _via_img_metadata.hasOwnProperty(img_id) ) {
2028
project_add_new_file(d[img_id].filename, d[img_id].size, img_id);
2029
if ( _via_settings.core.default_filepath === '' ) {
2030
_via_img_src[img_id] = d[img_id].filename;
2031
} else {
2032
_via_file_resolve_file_to_default_filepath(img_id);
2033
}
2034
file_added_count += 1;
2035
}
2036
2037
// copy file attributes
2038
for ( var key in d[img_id].file_attributes ) {
2039
if ( key === '' ) {
2040
continue;
2041
}
2042
2043
_via_img_metadata[img_id].file_attributes[key] = d[img_id].file_attributes[key];
2044
2045
// add this file attribute to _via_attributes
2046
if ( ! _via_attributes['file'].hasOwnProperty(key) ) {
2047
_via_attributes['file'][key] = { 'type':'text' };
2048
}
2049
}
2050
2051
// copy regions
2052
var regions = d[img_id].regions;
2053
for ( var i in regions ) {
2054
var region_i = new file_region();
2055
for ( var sid in regions[i].shape_attributes ) {
2056
region_i.shape_attributes[sid] = regions[i].shape_attributes[sid];
2057
}
2058
for ( var rid in regions[i].region_attributes ) {
2059
if ( rid === '' ) {
2060
continue;
2061
}
2062
2063
region_i.region_attributes[rid] = regions[i].region_attributes[rid];
2064
2065
// add this region attribute to _via_attributes
2066
if ( ! _via_attributes['region'].hasOwnProperty(rid) ) {
2067
_via_attributes['region'][rid] = { 'type':'text' };
2068
}
2069
}
2070
2071
// add regions only if they are present
2072
if ( Object.keys(region_i.shape_attributes).length > 0 ||
2073
Object.keys(region_i.region_attributes).length > 0 ) {
2074
_via_img_metadata[img_id].regions.push(region_i);
2075
region_import_count += 1;
2076
}
2077
}
2078
}
2079
show_message('Import Summary : [' + file_added_count + '] new files, ' +
2080
'[' + region_import_count + '] regions, ' +
2081
'[' + malformed_entries_count + '] malformed entries.');
2082
2083
if ( file_added_count ) {
2084
update_img_fn_list();
2085
}
2086
2087
if ( _via_current_image_loaded ) {
2088
if ( region_import_count ) {
2089
update_attributes_update_panel();
2090
annotation_editor_update_content();
2091
_via_load_canvas_regions(); // image to canvas space transform
2092
_via_redraw_reg_canvas();
2093
_via_reg_canvas.focus();
2094
}
2095
} else {
2096
if ( file_added_count ) {
2097
_via_show_img(0);
2098
}
2099
}
2100
2101
ok_callback([file_added_count, region_import_count, malformed_entries_count]);
2102
});
2103
}
2104
2105
// assumes that csv line follows the RFC 4180 standard
2106
// see: https://en.wikipedia.org/wiki/Comma-separated_values
2107
function parse_csv_line(s, field_separator) {
2108
if (typeof(s) === 'undefined' || s.length === 0 ) {
2109
return [];
2110
}
2111
2112
if (typeof(field_separator) === 'undefined') {
2113
field_separator = ',';
2114
}
2115
var double_quote_seen = false;
2116
var start = 0;
2117
var d = [];
2118
2119
var i = 0;
2120
while ( i < s.length) {
2121
if (s.charAt(i) === field_separator) {
2122
if (double_quote_seen) {
2123
// field separator inside double quote is ignored
2124
i = i + 1;
2125
} else {
2126
//var part = s.substr(start, i - start);
2127
d.push( s.substr(start, i - start) );
2128
start = i + 1;
2129
i = i + 1;
2130
}
2131
} else {
2132
if (s.charAt(i) === '"') {
2133
if (double_quote_seen) {
2134
if (s.charAt(i+1) === '"') {
2135
// ignore escaped double quotes
2136
i = i + 2;
2137
} else {
2138
// closing of double quote
2139
double_quote_seen = false;
2140
i = i + 1;
2141
}
2142
} else {
2143
double_quote_seen = true;
2144
start = i;
2145
i = i + 1;
2146
}
2147
} else {
2148
i = i + 1;
2149
}
2150
}
2151
2152
}
2153
// extract the last field (csv rows have no trailing comma)
2154
d.push( s.substr(start) );
2155
return d;
2156
}
2157
2158
// s = '{"name":"rect","x":188,"y":90,"width":243,"height":233}'
2159
function json_str_to_map(s) {
2160
if (typeof(s) === 'undefined' || s.length === 0 ) {
2161
return {};
2162
}
2163
2164
return JSON.parse(s);
2165
}
2166
2167
// ensure the exported json string conforms to RFC 4180
2168
// see: https://en.wikipedia.org/wiki/Comma-separated_values
2169
function map_to_json(m) {
2170
var s = [];
2171
for ( var key in m ) {
2172
var v = m[key];
2173
var si = JSON.stringify(key);
2174
si += VIA_CSV_KEYVAL_SEP;
2175
si += JSON.stringify(v);
2176
s.push( si );
2177
}
2178
return '{' + s.join(VIA_CSV_SEP) + '}';
2179
}
2180
2181
function escape_for_csv(s) {
2182
return s.replace(/["]/g, '""');
2183
}
2184
2185
function unescape_from_csv(s) {
2186
return s.replace(/""/g, '"');
2187
}
2188
2189
function remove_prefix_suffix_quotes(s) {
2190
if ( s.charAt(0) === '"' && s.charAt(s.length-1) === '"' ) {
2191
return s.substr(1, s.length-2);
2192
} else {
2193
return s;
2194
}
2195
}
2196
2197
function clone_image_region(r0) {
2198
var r1 = new file_region();
2199
2200
// copy shape attributes
2201
for ( var key in r0.shape_attributes ) {
2202
r1.shape_attributes[key] = clone_value(r0.shape_attributes[key]);
2203
}
2204
2205
// copy region attributes
2206
for ( var key in r0.region_attributes ) {
2207
r1.region_attributes[key] = clone_value(r0.region_attributes[key]);
2208
}
2209
return r1;
2210
}
2211
2212
function clone_value(value) {
2213
if ( typeof(value) === 'object' ) {
2214
if ( Array.isArray(value) ) {
2215
return value.slice(0);
2216
} else {
2217
var copy = {};
2218
for ( var p in value ) {
2219
if ( value.hasOwnProperty(p) ) {
2220
copy[p] = clone_value(value[p]);
2221
}
2222
}
2223
return copy;
2224
}
2225
}
2226
return value;
2227
}
2228
2229
function _via_get_image_id(filename, size) {
2230
if ( typeof(size) === 'undefined' ) {
2231
return filename;
2232
} else {
2233
return filename + size;
2234
}
2235
}
2236
2237
function load_text_file(text_file, callback_function) {
2238
if (text_file) {
2239
var text_reader = new FileReader();
2240
text_reader.addEventListener( 'progress', function(e) {
2241
show_message('Loading data from file : ' + text_file.name + ' ... ');
2242
}, false);
2243
2244
text_reader.addEventListener( 'error', function() {
2245
show_message('Error loading data text file : ' + text_file.name + ' !');
2246
callback_function('');
2247
}, false);
2248
2249
text_reader.addEventListener( 'load', function() {
2250
callback_function(text_reader.result);
2251
}, false);
2252
text_reader.readAsText(text_file, 'utf-8');
2253
}
2254
}
2255
2256
function import_files_url_from_csv(data) {
2257
return new Promise( function(ok_callback, err_callback) {
2258
if ( data === '' || typeof(data) === 'undefined') {
2259
err_callback();
2260
}
2261
2262
var malformed_url_count = 0;
2263
var url_added_count = 0;
2264
2265
var line_split_regex = new RegExp('\n|\r|\r\n', 'g');
2266
var csvdata = data.split(line_split_regex);
2267
2268
var percent_completed = 0;
2269
var n = csvdata.length;
2270
var i;
2271
var img_id;
2272
var first_img_id = '';
2273
for ( i=0; i < n; ++i ) {
2274
// ignore blank lines
2275
if (csvdata[i].charAt(0) === '\n' || csvdata[i].charAt(0) === '') {
2276
malformed_url_count += 1;
2277
continue;
2278
} else {
2279
img_id = project_file_add_url(csvdata[i]);
2280
if ( first_img_id === '' ) {
2281
first_img_id = img_id;
2282
}
2283
url_added_count += 1;
2284
}
2285
}
2286
show_message('Added ' + url_added_count + ' files to project');
2287
if ( url_added_count ) {
2288
var first_img_index = _via_image_id_list.indexOf(first_img_id);
2289
_via_show_img(first_img_index);
2290
update_img_fn_list();
2291
}
2292
});
2293
}
2294
2295
//
2296
// Data Exporter
2297
//
2298
function pack_via_metadata(return_type) {
2299
return new Promise( function(ok_callback, err_callback) {
2300
if( return_type === 'csv' ) {
2301
var csvdata = [];
2302
var csvheader = 'filename,file_size,file_attributes,region_count,region_id,region_shape_attributes,region_attributes';
2303
csvdata.push(csvheader);
2304
2305
for ( var image_id in _via_img_metadata ) {
2306
var fattr = map_to_json( _via_img_metadata[image_id].file_attributes );
2307
fattr = escape_for_csv( fattr );
2308
2309
var prefix = '\n' + _via_img_metadata[image_id].filename;
2310
prefix += ',' + _via_img_metadata[image_id].size;
2311
prefix += ',"' + fattr + '"';
2312
2313
var r = _via_img_metadata[image_id].regions;
2314
2315
if ( r.length !==0 ) {
2316
for ( var i = 0; i < r.length; ++i ) {
2317
var csvline = [];
2318
csvline.push(prefix);
2319
csvline.push(r.length);
2320
csvline.push(i);
2321
2322
var sattr = map_to_json( r[i].shape_attributes );
2323
sattr = '"' + escape_for_csv( sattr ) + '"';
2324
csvline.push(sattr);
2325
2326
var rattr = map_to_json( r[i].region_attributes );
2327
rattr = '"' + escape_for_csv( rattr ) + '"';
2328
csvline.push(rattr);
2329
csvdata.push( csvline.join(VIA_CSV_SEP) );
2330
}
2331
} else {
2332
// @todo: reconsider this practice of adding an empty entry
2333
csvdata.push(prefix + ',0,0,"{}","{}"');
2334
}
2335
}
2336
ok_callback(csvdata);
2337
}
2338
2339
// see http://cocodataset.org/#format-data
2340
if( return_type === 'coco' ) {
2341
img_stat_set_all().then( function(ok) {
2342
var coco = export_project_to_coco_format();
2343
ok_callback( [ coco ] );
2344
}.bind(this), function(err) {
2345
err_callback(err);
2346
}.bind(this));
2347
} else {
2348
// default format is JSON
2349
ok_callback( [ JSON.stringify(_via_img_metadata) ] );
2350
}
2351
}.bind(this));
2352
}
2353
2354
function export_project_to_coco_format() {
2355
var coco = { 'info':{}, 'images':[], 'annotations':[], 'licenses':[], 'categories':[] };
2356
coco['info'] = { 'year': new Date().getFullYear(),
2357
'version': '1.0',
2358
'description': 'VIA project exported to COCO format using VGG Image Annotator (http://www.robots.ox.ac.uk/~vgg/software/via/)',
2359
'contributor': '',
2360
'url': 'http://www.robots.ox.ac.uk/~vgg/software/via/',
2361
'date_created': new Date().toString(),
2362
};
2363
coco['licenses'] = [ {'id':0, 'name':'Unknown License', 'url':''} ]; // indicates that license is unknown
2364
2365
var skipped_annotation_count = 0;
2366
// We want to ensure that a COCO project imported in VIA and then exported again back to
2367
// COCO format using VIA retains the image_id and category_id present in the original COCO project.
2368
// A VIA project that has been created by importing annotations from a COCO project contains
2369
// unique image_id of type integer and contains all unique option id. If we detect this, we reuse
2370
// the existing image_id and category_id, otherwise we assign a new unique id sequentially.
2371
// Currently, it is not possible to preserve the annotation_id
2372
var assign_unique_id = false;
2373
for(var img_id in _via_img_metadata) {
2374
if(Number.isNaN(parseInt(img_id))) {
2375
assign_unique_id = true; // since COCO only supports image_id of type integer, we cannot reuse the VIA's image-id
2376
break;
2377
}
2378
}
2379
if(assign_unique_id) {
2380
// check if all the options have unique id
2381
var attribute_option_id_list = [];
2382
for(var attr_name in _via_attributes) {
2383
if( !VIA_COCO_EXPORT_ATTRIBUTE_TYPE.includes(_via_attributes[attr_name]['type']) ) {
2384
continue; // skip this attribute as it will not be included in COCO export
2385
}
2386
2387
for(var attr_option_id in _via_attributes[attr_name]['options']) {
2388
if(attribute_option_id_list.includes(attr_option_id) ||
2389
Number.isNaN(parseInt(attr_option_id)) ) {
2390
assign_unique_id = true;
2391
break;
2392
} else {
2393
attribute_option_id_list.push(assign_unique_id);
2394
}
2395
}
2396
}
2397
}
2398
2399
// add categories
2400
var attr_option_id_to_category_id = {};
2401
var unique_category_id = 1;
2402
for(var attr_name in _via_attributes['region']) {
2403
if( VIA_COCO_EXPORT_ATTRIBUTE_TYPE.includes(_via_attributes['region'][attr_name]['type']) ) {
2404
for(var attr_option_id in _via_attributes['region'][attr_name]['options']) {
2405
var category_id;
2406
if(assign_unique_id) {
2407
category_id = unique_category_id;
2408
unique_category_id = unique_category_id + 1;
2409
} else {
2410
category_id = parseInt(attr_option_id);
2411
}
2412
coco['categories'].push({
2413
'supercategory':attr_name,
2414
'id':category_id,
2415
'name':_via_attributes['region'][attr_name]['options'][attr_option_id]
2416
});
2417
attr_option_id_to_category_id[attr_option_id] = category_id;
2418
}
2419
}
2420
}
2421
2422
// add files and all their associated annotations
2423
var annotation_id = 1;
2424
var unique_img_id = 1;
2425
for( var img_index in _via_image_id_list ) {
2426
var img_id = _via_image_id_list[img_index];
2427
var file_src = _via_settings['core']['default_filepath'] + _via_img_metadata[img_id].filename;
2428
if ( _via_img_fileref[img_id] instanceof File ) {
2429
file_src = _via_img_fileref[img_id].filename;
2430
}
2431
2432
var coco_img_id;
2433
if(assign_unique_id) {
2434
coco_img_id = unique_img_id;
2435
unique_img_id = unique_img_id + 1;
2436
} else {
2437
coco_img_id = parseInt(img_id);
2438
}
2439
2440
coco['images'].push( {
2441
'id':coco_img_id,
2442
'width':_via_img_stat[img_index][0],
2443
'height':_via_img_stat[img_index][1],
2444
'file_name':_via_img_metadata[img_id].filename,
2445
'license':0,
2446
'flickr_url':file_src,
2447
'coco_url':file_src,
2448
'date_captured':'',
2449
} );
2450
2451
// add all annotations associated with this file
2452
for( var rindex in _via_img_metadata[img_id].regions ) {
2453
var region = _via_img_metadata[img_id].regions[rindex];
2454
if( !VIA_COCO_EXPORT_RSHAPE.includes(region.shape_attributes['name']) ) {
2455
skipped_annotation_count = skipped_annotation_count + 1;
2456
continue; // skip this region as COCO does not allow it
2457
}
2458
2459
var coco_annotation = via_region_shape_to_coco_annotation(region.shape_attributes);
2460
coco_annotation['id'] = annotation_id;
2461
coco_annotation['image_id'] = coco_img_id;
2462
2463
var region_aid_list = Object.keys(region['region_attributes']);
2464
for(var region_attribute_id in region['region_attributes']) {
2465
var region_attribute_value = region['region_attributes'][region_attribute_id];
2466
if(attr_option_id_to_category_id.hasOwnProperty(region_attribute_value)) {
2467
coco_annotation['category_id'] = attr_option_id_to_category_id[region_attribute_value];
2468
coco['annotations'].push(coco_annotation);
2469
annotation_id = annotation_id + 1;
2470
} else {
2471
skipped_annotation_count = skipped_annotation_count + 1;
2472
continue; // skip attribute value not supported by COCO format
2473
}
2474
}
2475
}
2476
}
2477
2478
show_message('Skipped ' + skipped_annotation_count + ' annotations. COCO format only supports the following attribute types: ' + JSON.stringify(VIA_COCO_EXPORT_ATTRIBUTE_TYPE) + ' and region shapes: ' + JSON.stringify(VIA_COCO_EXPORT_RSHAPE));
2479
return [ JSON.stringify(coco) ];
2480
}
2481
2482
function via_region_shape_to_coco_annotation(shape_attributes) {
2483
var annotation = { 'segmentation':[[]], 'area':[], 'bbox':[], 'iscrowd':0 };
2484
2485
switch(shape_attributes['name']) {
2486
case 'rect':
2487
var x0 = shape_attributes['x'];
2488
var y0 = shape_attributes['y'];
2489
var w = parseInt(shape_attributes['width']);
2490
var h = parseInt(shape_attributes['height']);
2491
var x1 = x0 + w;
2492
var y1 = y0 + h;
2493
annotation['segmentation'][0] = [x0, y0, x1, y0, x1, y1, x0, y1];
2494
annotation['area'] = w * h ;
2495
2496
annotation['bbox'] = [x0, y0, w, h];
2497
break;
2498
2499
case 'point':
2500
var cx = shape_attributes['cx'];
2501
var cy = shape_attributes['cy'];
2502
// 2 is for visibility - currently set to always inside segmentation.
2503
// see Keypoint Detection: http://cocodataset.org/#format-data
2504
annotation['keypoints'] = [cx, cy, 2];
2505
annotation['num_keypoints'] = 1;
2506
break;
2507
2508
case 'circle':
2509
var a,b;
2510
a = shape_attributes['r'];
2511
b = shape_attributes['r'];
2512
var theta_to_radian = Math.PI/180;
2513
2514
for ( var theta = 0; theta < 360; theta = theta + VIA_POLYGON_SEGMENT_SUBTENDED_ANGLE ) {
2515
var theta_radian = theta * theta_to_radian;
2516
var x = shape_attributes['cx'] + a * Math.cos(theta_radian);
2517
var y = shape_attributes['cy'] + b * Math.sin(theta_radian);
2518
annotation['segmentation'][0].push( fixfloat(x), fixfloat(y) );
2519
}
2520
annotation['bbox'] = polygon_to_bbox(annotation['segmentation'][0]);
2521
annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3];
2522
break;
2523
2524
case 'ellipse':
2525
var a,b;
2526
a = shape_attributes['rx'];
2527
b = shape_attributes['ry'];
2528
var rotation = 0;
2529
// older version of VIA2 did not support rotated ellipse and hence 'theta' attribute may not be available
2530
if( shape_attributes.hasOwnProperty('theta') ) {
2531
rotation = shape_attributes['theta'];
2532
}
2533
2534
var theta_to_radian = Math.PI/180;
2535
2536
for ( var theta = 0; theta < 360; theta = theta + VIA_POLYGON_SEGMENT_SUBTENDED_ANGLE ) {
2537
var theta_radian = theta * theta_to_radian;
2538
var x = shape_attributes['cx'] +
2539
( a * Math.cos(theta_radian) * Math.cos(rotation) ) -
2540
( b * Math.sin(theta_radian) * Math.sin(rotation) );
2541
var y = shape_attributes['cy'] +
2542
( a * Math.cos(theta_radian) * Math.sin(rotation) ) +
2543
( b * Math.sin(theta_radian) * Math.cos(rotation) );
2544
annotation['segmentation'][0].push( fixfloat(x), fixfloat(y) );
2545
}
2546
annotation['bbox'] = polygon_to_bbox(annotation['segmentation'][0]);
2547
annotation['area'] = annotation['bbox'][2] * annotation['bbox'][3];
2548
break;
2549
2550
case 'polygon':
2551
annotation['segmentation'][0] = [];
2552
var x0 = +Infinity;
2553
var y0 = +Infinity;
2554
var x1 = -Infinity;
2555
var y1 = -Infinity;
2556
for ( var i in shape_attributes['all_points_x'] ) {
2557
annotation['segmentation'][0].push( shape_attributes['all_points_x'][i] );
2558
annotation['segmentation'][0].push( shape_attributes['all_points_y'][i] );
2559
if ( shape_attributes['all_points_x'][i] < x0 ) {
2560
x0 = shape_attributes['all_points_x'][i];
2561
}
2562
if ( shape_attributes['all_points_y'][i] < y0 ) {
2563
y0 = shape_attributes['all_points_y'][i];
2564
}
2565
if ( shape_attributes['all_points_x'][i] > x1 ) {
2566
x1 = shape_attributes['all_points_x'][i];
2567
}
2568
if ( shape_attributes['all_points_y'][i] > y1 ) {
2569
y1 = shape_attributes['all_points_y'][i];
2570
}
2571
}
2572
var w = x1 - x0;
2573
var h = y1 - y0;
2574
annotation['bbox'] = [x0, y0, w, h];
2575
annotation['area'] = w * h; // approximate area
2576
}
2577
return annotation;
2578
}
2579
2580
function save_data_to_local_file(data, filename) {
2581
var a = document.createElement('a');
2582
a.href = URL.createObjectURL(data);
2583
a.download = filename;
2584
2585
// simulate a mouse click event
2586
var event = new MouseEvent('click', {
2587
view: window,
2588
bubbles: true,
2589
cancelable: true
2590
});
2591
a.dispatchEvent(event);
2592
2593
// @todo: replace a.dispatchEvent() with a.click()
2594
// a.click() based trigger is supported in Chrome 70 and Safari 11/12 but **not** in Firefox 63
2595
//a.click();
2596
}
2597
2598
//
2599
// Maintainers of user interface
2600
//
2601
2602
function init_message_panel() {
2603
var p = document.getElementById('message_panel');
2604
p.addEventListener('mousedown', function() {
2605
this.style.display = 'none';
2606
}, false);
2607
p.addEventListener('mouseover', function() {
2608
clearTimeout(_via_message_clear_timer); // stop any previous timeouts
2609
}, false);
2610
}
2611
2612
function toggle_message_visibility() {
2613
if(_via_is_message_visible) {
2614
show_message('Disabled status messages');
2615
_via_is_message_visible = false;
2616
} else {
2617
_via_is_message_visible = true;
2618
show_message('Status messages are now visible');
2619
}
2620
}
2621
2622
function show_message(msg, t) {
2623
if ( _via_message_clear_timer ) {
2624
clearTimeout(_via_message_clear_timer); // stop any previous timeouts
2625
}
2626
if ( !_via_is_message_visible ) {
2627
return;
2628
}
2629
2630
var timeout = t;
2631
if ( typeof t === 'undefined' ) {
2632
timeout = VIA_THEME_MESSAGE_TIMEOUT_MS;
2633
}
2634
document.getElementById('message_panel_content').innerHTML = msg;
2635
document.getElementById('message_panel').style.display = 'block';
2636
2637
_via_message_clear_timer = setTimeout( function() {
2638
document.getElementById('message_panel').style.display = 'none';
2639
}, timeout);
2640
}
2641
2642
function _via_regions_group_color_init() {
2643
_via_canvas_regions_group_color = {};
2644
var aid = _via_settings.ui.image.region_color;
2645
if ( aid !== '__via_default_region_color__' ) {
2646
var avalue;
2647
for ( var i = 0; i < _via_img_metadata[_via_image_id].regions.length; ++i ) {
2648
avalue = _via_img_metadata[_via_image_id].regions[i].region_attributes[aid];
2649
_via_canvas_regions_group_color[avalue] = 1;
2650
}
2651
var color_index = 0;
2652
for ( avalue in _via_canvas_regions_group_color ) {
2653
_via_canvas_regions_group_color[avalue] = VIA_REGION_COLOR_LIST[ color_index % VIA_REGION_COLOR_LIST.length ];
2654
color_index = color_index + 1;
2655
}
2656
}
2657
}
2658
2659
// transform regions in image space to canvas space
2660
function _via_load_canvas_regions() {
2661
_via_regions_group_color_init();
2662
2663
// load all existing annotations into _via_canvas_regions
2664
var regions = _via_img_metadata[_via_image_id].regions;
2665
_via_canvas_regions = [];
2666
for ( var i = 0; i < regions.length; ++i ) {
2667
var region_i = new file_region();
2668
for ( var key in regions[i].shape_attributes ) {
2669
region_i.shape_attributes[key] = regions[i].shape_attributes[key];
2670
}
2671
_via_canvas_regions.push(region_i);
2672
2673
switch(_via_canvas_regions[i].shape_attributes['name']) {
2674
case VIA_REGION_SHAPE.RECT:
2675
var x = regions[i].shape_attributes['x'] / _via_canvas_scale;
2676
var y = regions[i].shape_attributes['y'] / _via_canvas_scale;
2677
var width = regions[i].shape_attributes['width'] / _via_canvas_scale;
2678
var height = regions[i].shape_attributes['height'] / _via_canvas_scale;
2679
2680
_via_canvas_regions[i].shape_attributes['x'] = Math.round(x);
2681
_via_canvas_regions[i].shape_attributes['y'] = Math.round(y);
2682
_via_canvas_regions[i].shape_attributes['width'] = Math.round(width);
2683
_via_canvas_regions[i].shape_attributes['height'] = Math.round(height);
2684
break;
2685
2686
case VIA_REGION_SHAPE.CIRCLE:
2687
var cx = regions[i].shape_attributes['cx'] / _via_canvas_scale;
2688
var cy = regions[i].shape_attributes['cy'] / _via_canvas_scale;
2689
var r = regions[i].shape_attributes['r'] / _via_canvas_scale;
2690
_via_canvas_regions[i].shape_attributes['cx'] = Math.round(cx);
2691
_via_canvas_regions[i].shape_attributes['cy'] = Math.round(cy);
2692
_via_canvas_regions[i].shape_attributes['r'] = Math.round(r);
2693
break;
2694
2695
case VIA_REGION_SHAPE.ELLIPSE:
2696
var cx = regions[i].shape_attributes['cx'] / _via_canvas_scale;
2697
var cy = regions[i].shape_attributes['cy'] / _via_canvas_scale;
2698
var rx = regions[i].shape_attributes['rx'] / _via_canvas_scale;
2699
var ry = regions[i].shape_attributes['ry'] / _via_canvas_scale;
2700
// rotation in radians
2701
var theta = regions[i].shape_attributes['theta'];
2702
_via_canvas_regions[i].shape_attributes['cx'] = Math.round(cx);
2703
_via_canvas_regions[i].shape_attributes['cy'] = Math.round(cy);
2704
_via_canvas_regions[i].shape_attributes['rx'] = Math.round(rx);
2705
_via_canvas_regions[i].shape_attributes['ry'] = Math.round(ry);
2706
_via_canvas_regions[i].shape_attributes['theta'] = theta;
2707
break;
2708
2709
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
2710
case VIA_REGION_SHAPE.POLYGON:
2711
var all_points_x = regions[i].shape_attributes['all_points_x'].slice(0);
2712
var all_points_y = regions[i].shape_attributes['all_points_y'].slice(0);
2713
for (var j=0; j<all_points_x.length; ++j) {
2714
all_points_x[j] = Math.round(all_points_x[j] / _via_canvas_scale);
2715
all_points_y[j] = Math.round(all_points_y[j] / _via_canvas_scale);
2716
}
2717
_via_canvas_regions[i].shape_attributes['all_points_x'] = all_points_x;
2718
_via_canvas_regions[i].shape_attributes['all_points_y'] = all_points_y;
2719
break;
2720
2721
case VIA_REGION_SHAPE.POINT:
2722
var cx = regions[i].shape_attributes['cx'] / _via_canvas_scale;
2723
var cy = regions[i].shape_attributes['cy'] / _via_canvas_scale;
2724
2725
_via_canvas_regions[i].shape_attributes['cx'] = Math.round(cx);
2726
_via_canvas_regions[i].shape_attributes['cy'] = Math.round(cy);
2727
break;
2728
}
2729
}
2730
}
2731
2732
// updates currently selected region shape
2733
function select_region_shape(sel_shape_name) {
2734
for ( var shape_name in VIA_REGION_SHAPE ) {
2735
var ui_element = document.getElementById('region_shape_' + VIA_REGION_SHAPE[shape_name]);
2736
ui_element.classList.remove('selected');
2737
}
2738
2739
_via_current_shape = sel_shape_name;
2740
var ui_element = document.getElementById('region_shape_' + _via_current_shape);
2741
ui_element.classList.add('selected');
2742
2743
switch(_via_current_shape) {
2744
case VIA_REGION_SHAPE.RECT: // Fall-through
2745
case VIA_REGION_SHAPE.CIRCLE: // Fall-through
2746
case VIA_REGION_SHAPE.ELLIPSE:
2747
show_message('Press single click and drag mouse to draw ' +
2748
_via_current_shape + ' region');
2749
break;
2750
2751
case VIA_REGION_SHAPE.POLYLINE:
2752
case VIA_REGION_SHAPE.POLYGON:
2753
_via_is_user_drawing_polygon = false;
2754
_via_current_polygon_region_id = -1;
2755
2756
show_message('[Single Click] to define polygon/polyline vertices, ' +
2757
'[Backspace] to delete last vertex, [Enter] to finish, [Esc] to cancel drawing.' );
2758
break;
2759
2760
case VIA_REGION_SHAPE.POINT:
2761
show_message('Press single click to define points (or landmarks)');
2762
break;
2763
2764
default:
2765
show_message('Unknown shape selected!');
2766
break;
2767
}
2768
}
2769
2770
function set_all_canvas_size(w, h) {
2771
_via_reg_canvas.height = h;
2772
_via_reg_canvas.width = w;
2773
2774
image_panel.style.height = h + 'px';
2775
image_panel.style.width = w + 'px';
2776
}
2777
2778
function set_all_canvas_scale(s) {
2779
_via_reg_ctx.scale(s, s);
2780
}
2781
2782
function show_all_canvas() {
2783
image_panel.style.display = 'inline-block';
2784
}
2785
2786
function hide_all_canvas() {
2787
image_panel.style.display = 'none';
2788
}
2789
2790
function jump_to_image(image_index) {
2791
if ( _via_img_count <= 0 ) {
2792
return;
2793
}
2794
2795
switch(_via_display_area_content_name) {
2796
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID:
2797
if ( image_index >= 0 && image_index < _via_img_count) {
2798
// @todo: jump to image grid page view with the given first image index
2799
show_single_image_view();
2800
_via_show_img(image_index);
2801
}
2802
break;
2803
default:
2804
if ( image_index >= 0 && image_index < _via_img_count) {
2805
_via_show_img(image_index);
2806
}
2807
break;
2808
}
2809
}
2810
2811
function count_missing_region_attr(img_id) {
2812
var miss_region_attr_count = 0;
2813
var attr_count = Object.keys(_via_region_attributes).length;
2814
for( var i=0; i < _via_img_metadata[img_id].regions.length; ++i ) {
2815
var set_attr_count = Object.keys(_via_img_metadata[img_id].regions[i].region_attributes).length;
2816
miss_region_attr_count += ( attr_count - set_attr_count );
2817
}
2818
return miss_region_attr_count;
2819
}
2820
2821
function count_missing_file_attr(img_id) {
2822
return Object.keys(_via_file_attributes).length - Object.keys(_via_img_metadata[img_id].file_attributes).length;
2823
}
2824
2825
function toggle_all_regions_selection(is_selected) {
2826
var n = _via_img_metadata[_via_image_id].regions.length;
2827
var i;
2828
_via_region_selected_flag = [];
2829
for ( i = 0; i < n; ++i) {
2830
_via_region_selected_flag[i] = is_selected;
2831
}
2832
_via_is_all_region_selected = is_selected;
2833
annotation_editor_hide();
2834
if ( _via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.ALL_REGIONS ) {
2835
annotation_editor_clear_row_highlight();
2836
}
2837
}
2838
2839
function select_only_region(region_id) {
2840
toggle_all_regions_selection(false);
2841
set_region_select_state(region_id, true);
2842
_via_is_region_selected = true;
2843
_via_is_all_region_selected = false;
2844
_via_user_sel_region_id = region_id;
2845
}
2846
2847
function set_region_select_state(region_id, is_selected) {
2848
_via_region_selected_flag[region_id] = is_selected;
2849
}
2850
2851
function show_annotation_data() {
2852
pack_via_metadata('csv').then( function(data) {
2853
var hstr = '<pre>' + data.join('') + '</pre>';
2854
var window_features = 'toolbar=no,menubar=no,location=no,resizable=yes,scrollbars=yes,status=no';
2855
window_features += ',width=800,height=600';
2856
var annotation_data_window = window.open('', 'Annotations (preview) ', window_features);
2857
annotation_data_window.document.body.innerHTML = hstr;
2858
}.bind(this), function(err) {
2859
show_message('Failed to collect annotation data!');
2860
}.bind(this));
2861
}
2862
2863
//
2864
// Image click handlers
2865
//
2866
2867
// enter annotation mode on double click
2868
function _via_reg_canvas_dblclick_handler(e) {
2869
e.stopPropagation();
2870
// @todo: use double click in future
2871
}
2872
2873
// user clicks on the canvas
2874
function _via_reg_canvas_mousedown_handler(e) {
2875
e.stopPropagation();
2876
_via_click_x0 = e.offsetX; _via_click_y0 = e.offsetY;
2877
_via_region_edge = is_on_region_corner(_via_click_x0, _via_click_y0);
2878
var region_id = is_inside_region(_via_click_x0, _via_click_y0);
2879
2880
if ( _via_is_region_selected ) {
2881
// check if user clicked on the region boundary
2882
if ( _via_region_edge[1] > 0 ) {
2883
if ( !_via_is_user_resizing_region ) {
2884
if ( _via_region_edge[0] !== _via_user_sel_region_id ) {
2885
_via_user_sel_region_id = _via_region_edge[0];
2886
}
2887
// resize region
2888
_via_is_user_resizing_region = true;
2889
}
2890
} else {
2891
var yes = is_inside_this_region(_via_click_x0,
2892
_via_click_y0,
2893
_via_user_sel_region_id);
2894
if (yes) {
2895
if( !_via_is_user_moving_region ) {
2896
_via_is_user_moving_region = true;
2897
_via_region_click_x = _via_click_x0;
2898
_via_region_click_y = _via_click_y0;
2899
}
2900
}
2901
if ( region_id === -1 ) {
2902
// mousedown on outside any region
2903
_via_is_user_drawing_region = true;
2904
// unselect all regions
2905
_via_is_region_selected = false;
2906
_via_user_sel_region_id = -1;
2907
toggle_all_regions_selection(false);
2908
}
2909
}
2910
} else {
2911
if ( region_id === -1 ) {
2912
// mousedown outside a region
2913
if (_via_current_shape !== VIA_REGION_SHAPE.POLYGON &&
2914
_via_current_shape !== VIA_REGION_SHAPE.POLYLINE &&
2915
_via_current_shape !== VIA_REGION_SHAPE.POINT) {
2916
// this is a bounding box drawing event
2917
_via_is_user_drawing_region = true;
2918
}
2919
} else {
2920
// mousedown inside a region
2921
// this could lead to (1) region selection or (2) region drawing
2922
_via_is_user_drawing_region = true;
2923
}
2924
}
2925
}
2926
2927
// implements the following functionalities:
2928
// - new region drawing (including polygon)
2929
// - moving/resizing/select/unselect existing region
2930
function _via_reg_canvas_mouseup_handler(e) {
2931
e.stopPropagation();
2932
_via_click_x1 = e.offsetX; _via_click_y1 = e.offsetY;
2933
2934
var click_dx = Math.abs(_via_click_x1 - _via_click_x0);
2935
var click_dy = Math.abs(_via_click_y1 - _via_click_y0);
2936
2937
// indicates that user has finished moving a region
2938
if ( _via_is_user_moving_region ) {
2939
_via_is_user_moving_region = false;
2940
_via_reg_canvas.style.cursor = "default";
2941
2942
var move_x = Math.round(_via_click_x1 - _via_region_click_x);
2943
var move_y = Math.round(_via_click_y1 - _via_region_click_y);
2944
2945
if (Math.abs(move_x) > VIA_MOUSE_CLICK_TOL ||
2946
Math.abs(move_y) > VIA_MOUSE_CLICK_TOL) {
2947
// move all selected regions
2948
_via_move_selected_regions(move_x, move_y);
2949
} else {
2950
// indicates a user click on an already selected region
2951
// this could indicate the user's intention to select another
2952
// nested region within this region
2953
// OR
2954
// draw a nested region (i.e. region inside a region)
2955
2956
// traverse the canvas regions in alternating ascending
2957
// and descending order to solve the issue of nested regions
2958
var nested_region_id = is_inside_region(_via_click_x0, _via_click_y0, true);
2959
if (nested_region_id >= 0 &&
2960
nested_region_id !== _via_user_sel_region_id) {
2961
_via_user_sel_region_id = nested_region_id;
2962
_via_is_region_selected = true;
2963
_via_is_user_moving_region = false;
2964
2965
// de-select all other regions if the user has not pressed Shift
2966
if ( !e.shiftKey ) {
2967
toggle_all_regions_selection(false);
2968
}
2969
set_region_select_state(nested_region_id, true);
2970
annotation_editor_show();
2971
} else {
2972
// user clicking inside an already selected region
2973
// indicates that the user intends to draw a nested region
2974
toggle_all_regions_selection(false);
2975
_via_is_region_selected = false;
2976
2977
switch (_via_current_shape) {
2978
case VIA_REGION_SHAPE.POLYLINE: // handled by case for POLYGON
2979
case VIA_REGION_SHAPE.POLYGON:
2980
// user has clicked on the first point in a new polygon
2981
// see also event 'mouseup' for _via_is_user_drawing_polygon=true
2982
_via_is_user_drawing_polygon = true;
2983
2984
var canvas_polygon_region = new file_region();
2985
canvas_polygon_region.shape_attributes['name'] = _via_current_shape;
2986
canvas_polygon_region.shape_attributes['all_points_x'] = [Math.round(_via_click_x0)];
2987
canvas_polygon_region.shape_attributes['all_points_y'] = [Math.round(_via_click_y0)];
2988
var new_length = _via_canvas_regions.push(canvas_polygon_region);
2989
_via_current_polygon_region_id = new_length - 1;
2990
break;
2991
2992
case VIA_REGION_SHAPE.POINT:
2993
// user has marked a landmark point
2994
var point_region = new file_region();
2995
point_region.shape_attributes['name'] = VIA_REGION_SHAPE.POINT;
2996
point_region.shape_attributes['cx'] = Math.round(_via_click_x0 * _via_canvas_scale);
2997
point_region.shape_attributes['cy'] = Math.round(_via_click_y0 * _via_canvas_scale);
2998
_via_img_metadata[_via_image_id].regions.push(point_region);
2999
3000
var canvas_point_region = new file_region();
3001
canvas_point_region.shape_attributes['name'] = VIA_REGION_SHAPE.POINT;
3002
canvas_point_region.shape_attributes['cx'] = Math.round(_via_click_x0);
3003
canvas_point_region.shape_attributes['cy'] = Math.round(_via_click_y0);
3004
_via_canvas_regions.push(canvas_point_region);
3005
break;
3006
}
3007
annotation_editor_update_content();
3008
}
3009
}
3010
_via_redraw_reg_canvas();
3011
_via_reg_canvas.focus();
3012
return;
3013
}
3014
3015
// indicates that user has finished resizing a region
3016
if ( _via_is_user_resizing_region ) {
3017
// _via_click(x0,y0) to _via_click(x1,y1)
3018
_via_is_user_resizing_region = false;
3019
_via_reg_canvas.style.cursor = "default";
3020
3021
// update the region
3022
var region_id = _via_region_edge[0];
3023
var image_attr = _via_img_metadata[_via_image_id].regions[region_id].shape_attributes;
3024
var canvas_attr = _via_canvas_regions[region_id].shape_attributes;
3025
3026
switch (canvas_attr['name']) {
3027
case VIA_REGION_SHAPE.RECT:
3028
var d = [canvas_attr['x'], canvas_attr['y'], 0, 0];
3029
d[2] = d[0] + canvas_attr['width'];
3030
d[3] = d[1] + canvas_attr['height'];
3031
3032
var mx = _via_current_x;
3033
var my = _via_current_y;
3034
var preserve_aspect_ratio = false;
3035
3036
// constrain (mx,my) to lie on a line connecting a diagonal of rectangle
3037
if ( _via_is_ctrl_pressed ) {
3038
preserve_aspect_ratio = true;
3039
}
3040
3041
rect_update_corner(_via_region_edge[1], d, mx, my, preserve_aspect_ratio);
3042
rect_standardize_coordinates(d);
3043
3044
var w = Math.abs(d[2] - d[0]);
3045
var h = Math.abs(d[3] - d[1]);
3046
3047
image_attr['x'] = Math.round(d[0] * _via_canvas_scale);
3048
image_attr['y'] = Math.round(d[1] * _via_canvas_scale);
3049
image_attr['width'] = Math.round(w * _via_canvas_scale);
3050
image_attr['height'] = Math.round(h * _via_canvas_scale);
3051
3052
canvas_attr['x'] = Math.round( image_attr['x'] / _via_canvas_scale);
3053
canvas_attr['y'] = Math.round( image_attr['y'] / _via_canvas_scale);
3054
canvas_attr['width'] = Math.round( image_attr['width'] / _via_canvas_scale);
3055
canvas_attr['height'] = Math.round( image_attr['height'] / _via_canvas_scale);
3056
break;
3057
3058
case VIA_REGION_SHAPE.CIRCLE:
3059
var dx = Math.abs(canvas_attr['cx'] - _via_current_x);
3060
var dy = Math.abs(canvas_attr['cy'] - _via_current_y);
3061
var new_r = Math.sqrt( dx*dx + dy*dy );
3062
3063
image_attr['r'] = fixfloat(new_r * _via_canvas_scale);
3064
canvas_attr['r'] = Math.round( image_attr['r'] / _via_canvas_scale);
3065
break;
3066
3067
case VIA_REGION_SHAPE.ELLIPSE:
3068
var new_rx = canvas_attr['rx'];
3069
var new_ry = canvas_attr['ry'];
3070
var new_theta = canvas_attr['theta'];
3071
var dx = Math.abs(canvas_attr['cx'] - _via_current_x);
3072
var dy = Math.abs(canvas_attr['cy'] - _via_current_y);
3073
3074
switch(_via_region_edge[1]) {
3075
case 5:
3076
new_ry = Math.sqrt(dx*dx + dy*dy);
3077
new_theta = Math.atan2(- (_via_current_x - canvas_attr['cx']), (_via_current_y - canvas_attr['cy']));
3078
break;
3079
3080
case 6:
3081
new_rx = Math.sqrt(dx*dx + dy*dy);
3082
new_theta = Math.atan2((_via_current_y - canvas_attr['cy']), (_via_current_x - canvas_attr['cx']));
3083
break;
3084
3085
default:
3086
new_rx = dx;
3087
new_ry = dy;
3088
new_theta = 0;
3089
break;
3090
}
3091
3092
image_attr['rx'] = fixfloat(new_rx * _via_canvas_scale);
3093
image_attr['ry'] = fixfloat(new_ry * _via_canvas_scale);
3094
image_attr['theta'] = fixfloat(new_theta);
3095
3096
canvas_attr['rx'] = Math.round(image_attr['rx'] / _via_canvas_scale);
3097
canvas_attr['ry'] = Math.round(image_attr['ry'] / _via_canvas_scale);
3098
canvas_attr['theta'] = fixfloat(new_theta);
3099
break;
3100
3101
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
3102
case VIA_REGION_SHAPE.POLYGON:
3103
var moved_vertex_id = _via_region_edge[1] - VIA_POLYGON_RESIZE_VERTEX_OFFSET;
3104
3105
if ( e.ctrlKey || e.metaKey ) {
3106
// if on vertex, delete it
3107
// if on edge, add a new vertex
3108
var r = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3109
var shape = r.name;
3110
var is_on_vertex = is_on_polygon_vertex(r['all_points_x'], r['all_points_y'], _via_current_x, _via_current_y);
3111
3112
if ( is_on_vertex === _via_region_edge[1] ) {
3113
// click on vertex, hence delete vertex
3114
if ( _via_polygon_del_vertex(region_id, moved_vertex_id) ) {
3115
show_message('Deleted vertex ' + moved_vertex_id + ' from region');
3116
}
3117
} else {
3118
var is_on_edge = is_on_polygon_edge(r['all_points_x'], r['all_points_y'], _via_current_x, _via_current_y);
3119
if ( is_on_edge === _via_region_edge[1] ) {
3120
// click on edge, hence add new vertex
3121
var vertex_index = is_on_edge - VIA_POLYGON_RESIZE_VERTEX_OFFSET;
3122
var canvas_x0 = Math.round(_via_click_x1);
3123
var canvas_y0 = Math.round(_via_click_y1);
3124
var img_x0 = Math.round( canvas_x0 * _via_canvas_scale );
3125
var img_y0 = Math.round( canvas_y0 * _via_canvas_scale );
3126
canvas_x0 = Math.round( img_x0 / _via_canvas_scale );
3127
canvas_y0 = Math.round( img_y0 / _via_canvas_scale );
3128
3129
_via_canvas_regions[region_id].shape_attributes['all_points_x'].splice(vertex_index+1, 0, canvas_x0);
3130
_via_canvas_regions[region_id].shape_attributes['all_points_y'].splice(vertex_index+1, 0, canvas_y0);
3131
_via_img_metadata[_via_image_id].regions[region_id].shape_attributes['all_points_x'].splice(vertex_index+1, 0, img_x0);
3132
_via_img_metadata[_via_image_id].regions[region_id].shape_attributes['all_points_y'].splice(vertex_index+1, 0, img_y0);
3133
3134
show_message('Added 1 new vertex to ' + shape + ' region');
3135
}
3136
}
3137
} else {
3138
// update coordinate of vertex
3139
var imx = Math.round(_via_current_x * _via_canvas_scale);
3140
var imy = Math.round(_via_current_y * _via_canvas_scale);
3141
image_attr['all_points_x'][moved_vertex_id] = imx;
3142
image_attr['all_points_y'][moved_vertex_id] = imy;
3143
canvas_attr['all_points_x'][moved_vertex_id] = Math.round( imx / _via_canvas_scale );
3144
canvas_attr['all_points_y'][moved_vertex_id] = Math.round( imy / _via_canvas_scale );
3145
}
3146
break;
3147
} // end of switch()
3148
_via_redraw_reg_canvas();
3149
_via_reg_canvas.focus();
3150
return;
3151
}
3152
3153
// denotes a single click (= mouse down + mouse up)
3154
if ( click_dx < VIA_MOUSE_CLICK_TOL ||
3155
click_dy < VIA_MOUSE_CLICK_TOL ) {
3156
// if user is already drawing polygon, then each click adds a new point
3157
if ( _via_is_user_drawing_polygon ) {
3158
var canvas_x0 = Math.round(_via_click_x1);
3159
var canvas_y0 = Math.round(_via_click_y1);
3160
var n = _via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_x'].length;
3161
var last_x0 = _via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_x'][n-1];
3162
var last_y0 = _via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_y'][n-1];
3163
// discard if the click was on the last vertex
3164
if ( canvas_x0 !== last_x0 || canvas_y0 !== last_y0 ) {
3165
// user clicked on a new polygon point
3166
_via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_x'].push(canvas_x0);
3167
_via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_y'].push(canvas_y0);
3168
}
3169
} else {
3170
var region_id = is_inside_region(_via_click_x0, _via_click_y0);
3171
if ( region_id >= 0 ) {
3172
// first click selects region
3173
_via_user_sel_region_id = region_id;
3174
_via_is_region_selected = true;
3175
_via_is_user_moving_region = false;
3176
_via_is_user_drawing_region = false;
3177
3178
// de-select all other regions if the user has not pressed Shift
3179
if ( !e.shiftKey ) {
3180
annotation_editor_clear_row_highlight();
3181
toggle_all_regions_selection(false);
3182
}
3183
set_region_select_state(region_id, true);
3184
3185
// show annotation editor only when a single region is selected
3186
if ( !e.shiftKey ) {
3187
annotation_editor_show();
3188
} else {
3189
annotation_editor_hide();
3190
}
3191
3192
// show the region info
3193
if (_via_is_region_info_visible) {
3194
var canvas_attr = _via_canvas_regions[region_id].shape_attributes;
3195
3196
switch (canvas_attr['name']) {
3197
case VIA_REGION_SHAPE.RECT:
3198
break;
3199
3200
case VIA_REGION_SHAPE.CIRCLE:
3201
var rf = document.getElementById('region_info');
3202
var attr = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3203
rf.innerHTML += ',' + ' Radius:' + attr['r'];
3204
break;
3205
3206
case VIA_REGION_SHAPE.ELLIPSE:
3207
var rf = document.getElementById('region_info');
3208
var attr = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3209
rf.innerHTML += ',' + ' X-radius:' + attr['rx'] + ',' + ' Y-radius:' + attr['ry'];
3210
break;
3211
3212
case VIA_REGION_SHAPE.POLYLINE:
3213
case VIA_REGION_SHAPE.POLYGON:
3214
break;
3215
}
3216
}
3217
3218
show_message('Region selected. If you intended to draw a region, click again inside the selected region to start drawing a region.')
3219
} else {
3220
if ( _via_is_user_drawing_region ) {
3221
// clear all region selection
3222
_via_is_user_drawing_region = false;
3223
_via_is_region_selected = false;
3224
toggle_all_regions_selection(false);
3225
annotation_editor_hide();
3226
} else {
3227
switch (_via_current_shape) {
3228
case VIA_REGION_SHAPE.POLYLINE: // handled by case for POLYGON
3229
case VIA_REGION_SHAPE.POLYGON:
3230
// user has clicked on the first point in a new polygon
3231
// see also event 'mouseup' for _via_is_user_moving_region=true
3232
_via_is_user_drawing_polygon = true;
3233
3234
var canvas_polygon_region = new file_region();
3235
canvas_polygon_region.shape_attributes['name'] = _via_current_shape;
3236
canvas_polygon_region.shape_attributes['all_points_x'] = [ Math.round(_via_click_x0) ];
3237
canvas_polygon_region.shape_attributes['all_points_y'] = [ Math.round(_via_click_y0)] ;
3238
3239
var new_length = _via_canvas_regions.push(canvas_polygon_region);
3240
_via_current_polygon_region_id = new_length - 1;
3241
break;
3242
3243
case VIA_REGION_SHAPE.POINT:
3244
// user has marked a landmark point
3245
var point_region = new file_region();
3246
point_region.shape_attributes['name'] = VIA_REGION_SHAPE.POINT;
3247
point_region.shape_attributes['cx'] = Math.round(_via_click_x0 * _via_canvas_scale);
3248
point_region.shape_attributes['cy'] = Math.round(_via_click_y0 * _via_canvas_scale);
3249
var region_count = _via_img_metadata[_via_image_id].regions.push(point_region);
3250
var new_region_id = region_count - 1;
3251
set_region_annotations_to_default_value( new_region_id );
3252
3253
var canvas_point_region = new file_region();
3254
canvas_point_region.shape_attributes['name'] = VIA_REGION_SHAPE.POINT;
3255
canvas_point_region.shape_attributes['cx'] = Math.round(_via_click_x0);
3256
canvas_point_region.shape_attributes['cy'] = Math.round(_via_click_y0);
3257
_via_canvas_regions.push(canvas_point_region);
3258
3259
annotation_editor_update_content();
3260
break;
3261
}
3262
}
3263
}
3264
}
3265
_via_redraw_reg_canvas();
3266
_via_reg_canvas.focus();
3267
return;
3268
}
3269
3270
// indicates that user has finished drawing a new region
3271
if ( _via_is_user_drawing_region ) {
3272
_via_is_user_drawing_region = false;
3273
var region_x0 = _via_click_x0;
3274
var region_y0 = _via_click_y0;
3275
var region_x1 = _via_click_x1;
3276
var region_y1 = _via_click_y1;
3277
3278
var original_img_region = new file_region();
3279
var canvas_img_region = new file_region();
3280
var region_dx = Math.abs(region_x1 - region_x0);
3281
var region_dy = Math.abs(region_y1 - region_y0);
3282
var new_region_added = false;
3283
3284
if ( region_dx > VIA_REGION_MIN_DIM && region_dy > VIA_REGION_MIN_DIM ) { // avoid regions with 0 dim
3285
switch(_via_current_shape) {
3286
case VIA_REGION_SHAPE.RECT:
3287
// ensure that (x0,y0) is top-left and (x1,y1) is bottom-right
3288
if ( _via_click_x0 < _via_click_x1 ) {
3289
region_x0 = _via_click_x0;
3290
region_x1 = _via_click_x1;
3291
} else {
3292
region_x0 = _via_click_x1;
3293
region_x1 = _via_click_x0;
3294
}
3295
3296
if ( _via_click_y0 < _via_click_y1 ) {
3297
region_y0 = _via_click_y0;
3298
region_y1 = _via_click_y1;
3299
} else {
3300
region_y0 = _via_click_y1;
3301
region_y1 = _via_click_y0;
3302
}
3303
3304
var x = Math.round(region_x0 * _via_canvas_scale);
3305
var y = Math.round(region_y0 * _via_canvas_scale);
3306
var width = Math.round(region_dx * _via_canvas_scale);
3307
var height = Math.round(region_dy * _via_canvas_scale);
3308
original_img_region.shape_attributes['name'] = 'rect';
3309
original_img_region.shape_attributes['x'] = x;
3310
original_img_region.shape_attributes['y'] = y;
3311
original_img_region.shape_attributes['width'] = width;
3312
original_img_region.shape_attributes['height'] = height;
3313
3314
canvas_img_region.shape_attributes['name'] = 'rect';
3315
canvas_img_region.shape_attributes['x'] = Math.round( x / _via_canvas_scale );
3316
canvas_img_region.shape_attributes['y'] = Math.round( y / _via_canvas_scale );
3317
canvas_img_region.shape_attributes['width'] = Math.round( width / _via_canvas_scale );
3318
canvas_img_region.shape_attributes['height'] = Math.round( height / _via_canvas_scale );
3319
3320
new_region_added = true;
3321
break;
3322
3323
case VIA_REGION_SHAPE.CIRCLE:
3324
var cx = Math.round(region_x0 * _via_canvas_scale);
3325
var cy = Math.round(region_y0 * _via_canvas_scale);
3326
var r = Math.round( Math.sqrt(region_dx*region_dx + region_dy*region_dy) * _via_canvas_scale );
3327
3328
original_img_region.shape_attributes['name'] = 'circle';
3329
original_img_region.shape_attributes['cx'] = cx;
3330
original_img_region.shape_attributes['cy'] = cy;
3331
original_img_region.shape_attributes['r'] = r;
3332
3333
canvas_img_region.shape_attributes['name'] = 'circle';
3334
canvas_img_region.shape_attributes['cx'] = Math.round( cx / _via_canvas_scale );
3335
canvas_img_region.shape_attributes['cy'] = Math.round( cy / _via_canvas_scale );
3336
canvas_img_region.shape_attributes['r'] = Math.round( r / _via_canvas_scale );
3337
3338
new_region_added = true;
3339
break;
3340
3341
case VIA_REGION_SHAPE.ELLIPSE:
3342
var cx = Math.round(region_x0 * _via_canvas_scale);
3343
var cy = Math.round(region_y0 * _via_canvas_scale);
3344
var rx = Math.round(region_dx * _via_canvas_scale);
3345
var ry = Math.round(region_dy * _via_canvas_scale);
3346
var theta = 0;
3347
3348
original_img_region.shape_attributes['name'] = 'ellipse';
3349
original_img_region.shape_attributes['cx'] = cx;
3350
original_img_region.shape_attributes['cy'] = cy;
3351
original_img_region.shape_attributes['rx'] = rx;
3352
original_img_region.shape_attributes['ry'] = ry;
3353
original_img_region.shape_attributes['theta'] = theta;
3354
3355
canvas_img_region.shape_attributes['name'] = 'ellipse';
3356
canvas_img_region.shape_attributes['cx'] = Math.round( cx / _via_canvas_scale );
3357
canvas_img_region.shape_attributes['cy'] = Math.round( cy / _via_canvas_scale );
3358
canvas_img_region.shape_attributes['rx'] = Math.round( rx / _via_canvas_scale );
3359
canvas_img_region.shape_attributes['ry'] = Math.round( ry / _via_canvas_scale );
3360
canvas_img_region.shape_attributes['theta'] = theta;
3361
3362
new_region_added = true;
3363
break;
3364
3365
case VIA_REGION_SHAPE.POINT: // handled by case VIA_REGION_SHAPE.POLYGON
3366
case VIA_REGION_SHAPE.POLYLINE: // handled by case VIA_REGION_SHAPE.POLYGON
3367
case VIA_REGION_SHAPE.POLYGON:
3368
// handled by _via_is_user_drawing_polygon
3369
break;
3370
} // end of switch
3371
3372
if ( new_region_added ) {
3373
var n1 = _via_img_metadata[_via_image_id].regions.push(original_img_region);
3374
var n2 = _via_canvas_regions.push(canvas_img_region);
3375
3376
if ( n1 !== n2 ) {
3377
console.log('_via_img_metadata.regions[' + n1 + '] and _via_canvas_regions[' + n2 + '] count mismatch');
3378
}
3379
var new_region_id = n1 - 1;
3380
3381
set_region_annotations_to_default_value( new_region_id );
3382
select_only_region(new_region_id);
3383
if ( _via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.ALL_REGIONS &&
3384
_via_metadata_being_updated === 'region' ) {
3385
annotation_editor_add_row( new_region_id );
3386
annotation_editor_scroll_to_row( new_region_id );
3387
annotation_editor_clear_row_highlight();
3388
annotation_editor_highlight_row( new_region_id );
3389
}
3390
annotation_editor_show();
3391
}
3392
_via_redraw_reg_canvas();
3393
_via_reg_canvas.focus();
3394
} else {
3395
show_message('Prevented accidental addition of a very small region.');
3396
}
3397
return;
3398
}
3399
}
3400
3401
function _via_reg_canvas_mouseover_handler(e) {
3402
// change the mouse cursor icon
3403
_via_redraw_reg_canvas();
3404
_via_reg_canvas.focus();
3405
}
3406
3407
function _via_reg_canvas_mousemove_handler(e) {
3408
if ( !_via_current_image_loaded ) {
3409
return;
3410
}
3411
3412
_via_current_x = e.offsetX; _via_current_y = e.offsetY;
3413
3414
// display the cursor coordinates
3415
var rf = document.getElementById('region_info');
3416
if ( rf != null && _via_is_region_info_visible ) {
3417
var img_x = Math.round( _via_current_x * _via_canvas_scale );
3418
var img_y = Math.round( _via_current_y * _via_canvas_scale );
3419
rf.innerHTML = 'X:' + img_x + ',' + ' Y:' + img_y;
3420
}
3421
3422
if ( _via_is_region_selected ) {
3423
// display the region's info if a region is selected
3424
if ( rf != null && _via_is_region_info_visible && _via_user_sel_region_id !== -1) {
3425
var canvas_attr = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3426
switch (canvas_attr['name']) {
3427
case VIA_REGION_SHAPE.RECT:
3428
break;
3429
3430
case VIA_REGION_SHAPE.CIRCLE:
3431
var rf = document.getElementById('region_info');
3432
var attr = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3433
rf.innerHTML += ',' + ' Radius:' + attr['r'];
3434
break;
3435
3436
case VIA_REGION_SHAPE.ELLIPSE:
3437
var rf = document.getElementById('region_info');
3438
var attr = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3439
rf.innerHTML += ',' + ' X-radius:' + attr['rx'] + ',' + ' Y-radius:' + attr['ry'];
3440
break;
3441
3442
case VIA_REGION_SHAPE.POLYLINE:
3443
case VIA_REGION_SHAPE.POLYGON:
3444
break;
3445
}
3446
}
3447
3448
if ( !_via_is_user_resizing_region ) {
3449
// check if user moved mouse cursor to region boundary
3450
// which indicates an intention to resize the region
3451
_via_region_edge = is_on_region_corner(_via_current_x, _via_current_y);
3452
3453
if ( _via_region_edge[0] === _via_user_sel_region_id ) {
3454
switch(_via_region_edge[1]) {
3455
// rect
3456
case 1: // Fall-through // top-left corner of rect
3457
case 3: // bottom-right corner of rect
3458
_via_reg_canvas.style.cursor = "nwse-resize";
3459
break;
3460
case 2: // Fall-through // top-right corner of rect
3461
case 4: // bottom-left corner of rect
3462
_via_reg_canvas.style.cursor = "nesw-resize";
3463
break;
3464
3465
case 5: // Fall-through // top-middle point of rect
3466
case 7: // bottom-middle point of rect
3467
_via_reg_canvas.style.cursor = "ns-resize";
3468
break;
3469
case 6: // Fall-through // top-middle point of rect
3470
case 8: // bottom-middle point of rect
3471
_via_reg_canvas.style.cursor = "ew-resize";
3472
break;
3473
3474
// circle and ellipse
3475
case 5:
3476
_via_reg_canvas.style.cursor = "n-resize";
3477
break;
3478
case 6:
3479
_via_reg_canvas.style.cursor = "e-resize";
3480
break;
3481
3482
default:
3483
_via_reg_canvas.style.cursor = "default";
3484
break;
3485
}
3486
3487
if (_via_region_edge[1] >= VIA_POLYGON_RESIZE_VERTEX_OFFSET) {
3488
// indicates mouse over polygon vertex
3489
_via_reg_canvas.style.cursor = "crosshair";
3490
show_message('To move vertex, simply drag the vertex. To add vertex, press [Ctrl] key and click on the edge. To delete vertex, press [Ctrl] (or [Command]) key and click on vertex.');
3491
}
3492
} else {
3493
var yes = is_inside_this_region(_via_current_x,
3494
_via_current_y,
3495
_via_user_sel_region_id);
3496
if (yes) {
3497
_via_reg_canvas.style.cursor = "move";
3498
} else {
3499
_via_reg_canvas.style.cursor = "default";
3500
}
3501
3502
}
3503
} else {
3504
annotation_editor_hide() // resizing
3505
}
3506
}
3507
3508
if(_via_is_user_drawing_region) {
3509
// draw region as the user drags the mouse cursor
3510
if (_via_canvas_regions.length) {
3511
_via_redraw_reg_canvas(); // clear old intermediate rectangle
3512
} else {
3513
// first region being drawn, just clear the full region canvas
3514
_via_reg_ctx.clearRect(0, 0, _via_reg_canvas.width, _via_reg_canvas.height);
3515
}
3516
3517
var region_x0 = _via_click_x0;
3518
var region_y0 = _via_click_y0;
3519
3520
var dx = Math.round(Math.abs(_via_current_x - _via_click_x0));
3521
var dy = Math.round(Math.abs(_via_current_y - _via_click_y0));
3522
_via_reg_ctx.strokeStyle = VIA_THEME_BOUNDARY_FILL_COLOR;
3523
3524
switch (_via_current_shape ) {
3525
case VIA_REGION_SHAPE.RECT:
3526
if ( _via_click_x0 < _via_current_x ) {
3527
if ( _via_click_y0 < _via_current_y ) {
3528
region_x0 = _via_click_x0;
3529
region_y0 = _via_click_y0;
3530
} else {
3531
region_x0 = _via_click_x0;
3532
region_y0 = _via_current_y;
3533
}
3534
} else {
3535
if ( _via_click_y0 < _via_current_y ) {
3536
region_x0 = _via_current_x;
3537
region_y0 = _via_click_y0;
3538
} else {
3539
region_x0 = _via_current_x;
3540
region_y0 = _via_current_y;
3541
}
3542
}
3543
3544
_via_draw_rect_region(region_x0, region_y0, dx, dy, false);
3545
3546
// display the current region info
3547
if ( rf != null && _via_is_region_info_visible ) {
3548
rf.innerHTML += ',' + ' W:' + dx + ',' + ' H:' + dy;
3549
}
3550
break;
3551
3552
case VIA_REGION_SHAPE.CIRCLE:
3553
var circle_radius = Math.round(Math.sqrt( dx*dx + dy*dy ));
3554
_via_draw_circle_region(region_x0, region_y0, circle_radius, false);
3555
3556
// display the current region info
3557
if ( rf != null && _via_is_region_info_visible ) {
3558
rf.innerHTML += ',' + ' Radius:' + circle_radius;
3559
}
3560
break;
3561
3562
case VIA_REGION_SHAPE.ELLIPSE:
3563
_via_draw_ellipse_region(region_x0, region_y0, dx, dy, 0, false);
3564
3565
// display the current region info
3566
if ( rf != null && _via_is_region_info_visible ) {
3567
rf.innerHTML += ',' + ' X-radius:' + fixfloat(dx) + ',' + ' Y-radius:' + fixfloat(dy);
3568
}
3569
break;
3570
3571
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
3572
case VIA_REGION_SHAPE.POLYGON:
3573
// this is handled by the if ( _via_is_user_drawing_polygon ) { ... }
3574
// see below
3575
break;
3576
}
3577
_via_reg_canvas.focus();
3578
}
3579
3580
if ( _via_is_user_resizing_region ) {
3581
// user has clicked mouse on bounding box edge and is now moving it
3582
// draw region as the user drags the mouse coursor
3583
if (_via_canvas_regions.length) {
3584
_via_redraw_reg_canvas(); // clear old intermediate rectangle
3585
} else {
3586
// first region being drawn, just clear the full region canvas
3587
_via_reg_ctx.clearRect(0, 0, _via_reg_canvas.width, _via_reg_canvas.height);
3588
}
3589
3590
var region_id = _via_region_edge[0];
3591
var attr = _via_canvas_regions[region_id].shape_attributes;
3592
switch (attr['name']) {
3593
case VIA_REGION_SHAPE.RECT:
3594
// original rectangle
3595
var d = [attr['x'], attr['y'], 0, 0];
3596
d[2] = d[0] + attr['width'];
3597
d[3] = d[1] + attr['height'];
3598
3599
var mx = _via_current_x;
3600
var my = _via_current_y;
3601
var preserve_aspect_ratio = false;
3602
// constrain (mx,my) to lie on a line connecting a diagonal of rectangle
3603
if ( _via_is_ctrl_pressed ) {
3604
preserve_aspect_ratio = true;
3605
}
3606
3607
rect_update_corner(_via_region_edge[1], d, mx, my, preserve_aspect_ratio);
3608
rect_standardize_coordinates(d);
3609
3610
var w = Math.abs(d[2] - d[0]);
3611
var h = Math.abs(d[3] - d[1]);
3612
_via_draw_rect_region(d[0], d[1], w, h, true);
3613
3614
if ( rf != null && _via_is_region_info_visible ) {
3615
rf.innerHTML += ',' + ' W:' + w + ',' + ' H:' + h;
3616
}
3617
break;
3618
3619
case VIA_REGION_SHAPE.CIRCLE:
3620
var dx = Math.abs(attr['cx'] - _via_current_x);
3621
var dy = Math.abs(attr['cy'] - _via_current_y);
3622
var new_r = Math.sqrt( dx*dx + dy*dy );
3623
_via_draw_circle_region(attr['cx'],
3624
attr['cy'],
3625
new_r,
3626
true);
3627
if ( rf != null && _via_is_region_info_visible ) {
3628
var curr_texts = rf.innerHTML.split(",");
3629
rf.innerHTML = "";
3630
rf.innerHTML += curr_texts[0] + ',' + curr_texts[1] + ',' + ' Radius:' + Math.round(new_r);
3631
}
3632
break;
3633
3634
case VIA_REGION_SHAPE.ELLIPSE:
3635
var new_rx = attr['rx'];
3636
var new_ry = attr['ry'];
3637
var new_theta = attr['theta'];
3638
var dx = Math.abs(attr['cx'] - _via_current_x);
3639
var dy = Math.abs(attr['cy'] - _via_current_y);
3640
switch(_via_region_edge[1]) {
3641
case 5:
3642
new_ry = Math.sqrt(dx*dx + dy*dy);
3643
new_theta = Math.atan2(- (_via_current_x - attr['cx']), (_via_current_y - attr['cy']));
3644
break;
3645
3646
case 6:
3647
new_rx = Math.sqrt(dx*dx + dy*dy);
3648
new_theta = Math.atan2((_via_current_y - attr['cy']), (_via_current_x - attr['cx']));
3649
break;
3650
3651
default:
3652
new_rx = dx;
3653
new_ry = dy;
3654
new_theta = 0;
3655
break;
3656
}
3657
3658
_via_draw_ellipse_region(attr['cx'],
3659
attr['cy'],
3660
new_rx,
3661
new_ry,
3662
new_theta,
3663
true);
3664
if ( rf != null && _via_is_region_info_visible ) {
3665
var curr_texts = rf.innerHTML.split(",");
3666
rf.innerHTML = "";
3667
rf.innerHTML = curr_texts[0] + ',' + curr_texts[1] + ',' + ' X-radius:' + fixfloat(new_rx) + ',' + ' Y-radius:' + fixfloat(new_ry);
3668
}
3669
break;
3670
3671
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
3672
case VIA_REGION_SHAPE.POLYGON:
3673
var moved_all_points_x = attr['all_points_x'].slice(0);
3674
var moved_all_points_y = attr['all_points_y'].slice(0);
3675
var moved_vertex_id = _via_region_edge[1] - VIA_POLYGON_RESIZE_VERTEX_OFFSET;
3676
3677
moved_all_points_x[moved_vertex_id] = _via_current_x;
3678
moved_all_points_y[moved_vertex_id] = _via_current_y;
3679
3680
_via_draw_polygon_region(moved_all_points_x,
3681
moved_all_points_y,
3682
true,
3683
attr['name']);
3684
if ( rf != null && _via_is_region_info_visible ) {
3685
rf.innerHTML += ',' + ' Vertices:' + attr['all_points_x'].length;
3686
}
3687
break;
3688
}
3689
_via_reg_canvas.focus();
3690
}
3691
3692
if ( _via_is_user_moving_region ) {
3693
// draw region as the user drags the mouse coursor
3694
if (_via_canvas_regions.length) {
3695
_via_redraw_reg_canvas(); // clear old intermediate rectangle
3696
} else {
3697
// first region being drawn, just clear the full region canvas
3698
_via_reg_ctx.clearRect(0, 0, _via_reg_canvas.width, _via_reg_canvas.height);
3699
}
3700
3701
var move_x = (_via_current_x - _via_region_click_x);
3702
var move_y = (_via_current_y - _via_region_click_y);
3703
var attr = _via_canvas_regions[_via_user_sel_region_id].shape_attributes;
3704
3705
switch (attr['name']) {
3706
case VIA_REGION_SHAPE.RECT:
3707
_via_draw_rect_region(attr['x'] + move_x,
3708
attr['y'] + move_y,
3709
attr['width'],
3710
attr['height'],
3711
true);
3712
// display the current region info
3713
if ( rf != null && _via_is_region_info_visible ) {
3714
rf.innerHTML += ',' + ' W:' + attr['width'] + ',' + ' H:' + attr['height'];
3715
}
3716
break;
3717
3718
case VIA_REGION_SHAPE.CIRCLE:
3719
_via_draw_circle_region(attr['cx'] + move_x,
3720
attr['cy'] + move_y,
3721
attr['r'],
3722
true);
3723
break;
3724
3725
case VIA_REGION_SHAPE.ELLIPSE:
3726
if (typeof(attr['theta']) === 'undefined') { attr['theta'] = 0; }
3727
_via_draw_ellipse_region(attr['cx'] + move_x,
3728
attr['cy'] + move_y,
3729
attr['rx'],
3730
attr['ry'],
3731
attr['theta'],
3732
true);
3733
break;
3734
3735
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
3736
case VIA_REGION_SHAPE.POLYGON:
3737
var moved_all_points_x = attr['all_points_x'].slice(0);
3738
var moved_all_points_y = attr['all_points_y'].slice(0);
3739
for (var i=0; i<moved_all_points_x.length; ++i) {
3740
moved_all_points_x[i] += move_x;
3741
moved_all_points_y[i] += move_y;
3742
}
3743
_via_draw_polygon_region(moved_all_points_x,
3744
moved_all_points_y,
3745
true,
3746
attr['name']);
3747
if ( rf != null && _via_is_region_info_visible ) {
3748
rf.innerHTML += ',' + ' Vertices:' + attr['all_points_x'].length;
3749
}
3750
break;
3751
3752
case VIA_REGION_SHAPE.POINT:
3753
_via_draw_point_region(attr['cx'] + move_x,
3754
attr['cy'] + move_y,
3755
true);
3756
break;
3757
}
3758
_via_reg_canvas.focus();
3759
annotation_editor_hide() // moving
3760
return;
3761
}
3762
3763
if ( _via_is_user_drawing_polygon ) {
3764
_via_redraw_reg_canvas();
3765
var attr = _via_canvas_regions[_via_current_polygon_region_id].shape_attributes;
3766
var all_points_x = attr['all_points_x'];
3767
var all_points_y = attr['all_points_y'];
3768
var npts = all_points_x.length;
3769
3770
if ( npts > 0 ) {
3771
var line_x = [all_points_x.slice(npts-1), _via_current_x];
3772
var line_y = [all_points_y.slice(npts-1), _via_current_y];
3773
_via_draw_polygon_region(line_x, line_y, false, attr['name']);
3774
}
3775
3776
if ( rf != null && _via_is_region_info_visible ) {
3777
rf.innerHTML += ',' + ' Vertices:' + npts;
3778
}
3779
}
3780
}
3781
3782
function _via_move_selected_regions(move_x, move_y) {
3783
var i, n;
3784
n = _via_region_selected_flag.length;
3785
for ( i = 0; i < n; ++i ) {
3786
if ( _via_region_selected_flag[i] ) {
3787
_via_move_region(i, move_x, move_y);
3788
}
3789
}
3790
}
3791
3792
function _via_validate_move_region(x, y, canvas_attr) {
3793
switch( canvas_attr['name'] ) {
3794
case VIA_REGION_SHAPE.RECT:
3795
// left and top boundary check
3796
if (x < 0 || y < 0) {
3797
show_message('Region moved beyond image boundary. Resetting.');
3798
return false;
3799
}
3800
// right and bottom boundary check
3801
if ((y + canvas_attr['height']) > _via_current_image_height ||
3802
(x + canvas_attr['width']) > _via_current_image_width) {
3803
show_message('Region moved beyond image boundary. Resetting.');
3804
return false;
3805
}
3806
3807
// same validation for all
3808
case VIA_REGION_SHAPE.CIRCLE:
3809
case VIA_REGION_SHAPE.ELLIPSE:
3810
case VIA_REGION_SHAPE.POINT:
3811
case VIA_REGION_SHAPE.POLYLINE:
3812
case VIA_REGION_SHAPE.POLYGON:
3813
if (x < 0 || y < 0 ||
3814
x > _via_current_image_width || y > _via_current_image_height) {
3815
show_message('Region moved beyond image boundary. Resetting.');
3816
return false;
3817
}
3818
}
3819
return true;
3820
}
3821
3822
function _via_move_region(region_id, move_x, move_y) {
3823
var image_attr = _via_img_metadata[_via_image_id].regions[region_id].shape_attributes;
3824
var canvas_attr = _via_canvas_regions[region_id].shape_attributes;
3825
3826
switch( canvas_attr['name'] ) {
3827
case VIA_REGION_SHAPE.RECT:
3828
var xnew = image_attr['x'] + Math.round(move_x * _via_canvas_scale);
3829
var ynew = image_attr['y'] + Math.round(move_y * _via_canvas_scale);
3830
3831
var is_valid = _via_validate_move_region(xnew, ynew, image_attr);
3832
if (! is_valid ) { break; }
3833
3834
image_attr['x'] = xnew;
3835
image_attr['y'] = ynew;
3836
3837
canvas_attr['x'] = Math.round( image_attr['x'] / _via_canvas_scale);
3838
canvas_attr['y'] = Math.round( image_attr['y'] / _via_canvas_scale);
3839
break;
3840
3841
case VIA_REGION_SHAPE.CIRCLE: // Fall-through
3842
case VIA_REGION_SHAPE.ELLIPSE: // Fall-through
3843
case VIA_REGION_SHAPE.POINT:
3844
var cxnew = image_attr['cx'] + Math.round(move_x * _via_canvas_scale);
3845
var cynew = image_attr['cy'] + Math.round(move_y * _via_canvas_scale);
3846
3847
var is_valid = _via_validate_move_region(cxnew, cynew, image_attr);
3848
if (! is_valid ) { break; }
3849
3850
image_attr['cx'] = cxnew;
3851
image_attr['cy'] = cynew;
3852
3853
canvas_attr['cx'] = Math.round( image_attr['cx'] / _via_canvas_scale);
3854
canvas_attr['cy'] = Math.round( image_attr['cy'] / _via_canvas_scale);
3855
break;
3856
3857
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
3858
case VIA_REGION_SHAPE.POLYGON:
3859
var img_px = image_attr['all_points_x'];
3860
var img_py = image_attr['all_points_y'];
3861
var canvas_px = canvas_attr['all_points_x'];
3862
var canvas_py = canvas_attr['all_points_y'];
3863
// clone for reverting if valiation fails
3864
var img_px_old = Object.assign({}, img_px);
3865
var img_py_old = Object.assign({}, img_py);
3866
3867
// validate move
3868
for (var i=0; i<img_px.length; ++i) {
3869
var pxnew = img_px[i] + Math.round(move_x * _via_canvas_scale);
3870
var pynew = img_py[i] + Math.round(move_y * _via_canvas_scale);
3871
if (! _via_validate_move_region(pxnew, pynew, image_attr) ) {
3872
img_px = img_px_old;
3873
img_py = img_py_old;
3874
break;
3875
}
3876
}
3877
// move points
3878
for (var i=0; i<img_px.length; ++i) {
3879
img_px[i] = img_px[i] + Math.round(move_x * _via_canvas_scale);
3880
img_py[i] = img_py[i] + Math.round(move_y * _via_canvas_scale);
3881
}
3882
3883
for (var i=0; i<canvas_px.length; ++i) {
3884
canvas_px[i] = Math.round( img_px[i] / _via_canvas_scale );
3885
canvas_py[i] = Math.round( img_py[i] / _via_canvas_scale );
3886
}
3887
break;
3888
}
3889
}
3890
3891
function _via_polygon_del_vertex(region_id, vertex_id) {
3892
var rs = _via_canvas_regions[region_id].shape_attributes;
3893
var npts = rs['all_points_x'].length;
3894
var shape = rs['name'];
3895
if ( shape !== VIA_REGION_SHAPE.POLYGON && shape !== VIA_REGION_SHAPE.POLYLINE ) {
3896
show_message('Vertices can only be deleted from polygon/polyline.');
3897
return false;
3898
}
3899
if ( npts <=3 && shape === VIA_REGION_SHAPE.POLYGON ) {
3900
show_message('Failed to delete vertex because a polygon must have at least 3 vertices.');
3901
return false;
3902
}
3903
if ( npts <=2 && shape === VIA_REGION_SHAPE.POLYLINE ) {
3904
show_message('Failed to delete vertex because a polyline must have at least 2 vertices.');
3905
return false;
3906
}
3907
// delete vertex from canvas
3908
_via_canvas_regions[region_id].shape_attributes['all_points_x'].splice(vertex_id, 1);
3909
_via_canvas_regions[region_id].shape_attributes['all_points_y'].splice(vertex_id, 1);
3910
3911
// delete vertex from image metadata
3912
_via_img_metadata[_via_image_id].regions[region_id].shape_attributes['all_points_x'].splice(vertex_id, 1);
3913
_via_img_metadata[_via_image_id].regions[region_id].shape_attributes['all_points_y'].splice(vertex_id, 1);
3914
return true;
3915
}
3916
3917
//
3918
// Canvas update routines
3919
//
3920
function _via_redraw_reg_canvas() {
3921
if (_via_current_image_loaded) {
3922
_via_reg_ctx.clearRect(0, 0, _via_reg_canvas.width, _via_reg_canvas.height);
3923
if ( _via_canvas_regions.length > 0 ) {
3924
if (_via_is_region_boundary_visible) {
3925
draw_all_regions();
3926
}
3927
if (_via_is_region_id_visible) {
3928
draw_all_region_id();
3929
}
3930
}
3931
}
3932
}
3933
3934
function _via_clear_reg_canvas() {
3935
_via_reg_ctx.clearRect(0, 0, _via_reg_canvas.width, _via_reg_canvas.height);
3936
}
3937
3938
function draw_all_regions() {
3939
var aid = _via_settings.ui.image.region_color;
3940
var attr, is_selected, aid, avalue;
3941
for (var i=0; i < _via_canvas_regions.length; ++i) {
3942
attr = _via_canvas_regions[i].shape_attributes;
3943
is_selected = _via_region_selected_flag[i];
3944
3945
// region stroke style may depend on attribute value
3946
_via_reg_ctx.strokeStyle = VIA_THEME_BOUNDARY_FILL_COLOR;
3947
if ( ! _via_is_user_drawing_polygon &&
3948
aid !== '__via_default_region_color__' ) {
3949
avalue = _via_img_metadata[_via_image_id].regions[i].region_attributes[aid];
3950
if ( _via_canvas_regions_group_color.hasOwnProperty(avalue) ) {
3951
_via_reg_ctx.strokeStyle = _via_canvas_regions_group_color[avalue];
3952
}
3953
}
3954
3955
switch( attr['name'] ) {
3956
case VIA_REGION_SHAPE.RECT:
3957
_via_draw_rect_region(attr['x'],
3958
attr['y'],
3959
attr['width'],
3960
attr['height'],
3961
is_selected);
3962
break;
3963
3964
case VIA_REGION_SHAPE.CIRCLE:
3965
_via_draw_circle_region(attr['cx'],
3966
attr['cy'],
3967
attr['r'],
3968
is_selected);
3969
break;
3970
3971
case VIA_REGION_SHAPE.ELLIPSE:
3972
if (typeof(attr['theta']) === 'undefined') { attr['theta'] = 0; }
3973
_via_draw_ellipse_region(attr['cx'],
3974
attr['cy'],
3975
attr['rx'],
3976
attr['ry'],
3977
attr['theta'],
3978
is_selected);
3979
break;
3980
3981
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
3982
case VIA_REGION_SHAPE.POLYGON:
3983
_via_draw_polygon_region(attr['all_points_x'],
3984
attr['all_points_y'],
3985
is_selected,
3986
attr['name']);
3987
break;
3988
3989
case VIA_REGION_SHAPE.POINT:
3990
_via_draw_point_region(attr['cx'],
3991
attr['cy'],
3992
is_selected);
3993
break;
3994
}
3995
}
3996
}
3997
3998
// control point for resize of region boundaries
3999
function _via_draw_control_point(cx, cy) {
4000
_via_reg_ctx.beginPath();
4001
_via_reg_ctx.arc(cx, cy, VIA_REGION_SHAPES_POINTS_RADIUS, 0, 2*Math.PI, false);
4002
_via_reg_ctx.closePath();
4003
4004
_via_reg_ctx.fillStyle = VIA_THEME_CONTROL_POINT_COLOR;
4005
_via_reg_ctx.globalAlpha = 1.0;
4006
_via_reg_ctx.fill();
4007
}
4008
4009
function _via_draw_rect_region(x, y, w, h, is_selected) {
4010
if (is_selected) {
4011
_via_draw_rect(x, y, w, h);
4012
4013
_via_reg_ctx.strokeStyle = VIA_THEME_SEL_REGION_FILL_BOUNDARY_COLOR;
4014
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4015
_via_reg_ctx.stroke();
4016
4017
_via_reg_ctx.fillStyle = VIA_THEME_SEL_REGION_FILL_COLOR;
4018
_via_reg_ctx.globalAlpha = VIA_THEME_SEL_REGION_OPACITY;
4019
_via_reg_ctx.fill();
4020
_via_reg_ctx.globalAlpha = 1.0;
4021
4022
_via_draw_control_point(x , y);
4023
_via_draw_control_point(x+w, y+h);
4024
_via_draw_control_point(x , y+h);
4025
_via_draw_control_point(x+w, y);
4026
_via_draw_control_point(x+w/2, y);
4027
_via_draw_control_point(x+w/2, y+h);
4028
_via_draw_control_point(x , y+h/2);
4029
_via_draw_control_point(x+w , y+h/2);
4030
} else {
4031
// draw a fill line
4032
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4033
_via_draw_rect(x, y, w, h);
4034
_via_reg_ctx.stroke();
4035
4036
if ( w > VIA_THEME_REGION_BOUNDARY_WIDTH &&
4037
h > VIA_THEME_REGION_BOUNDARY_WIDTH ) {
4038
// draw a boundary line on both sides of the fill line
4039
_via_reg_ctx.strokeStyle = VIA_THEME_BOUNDARY_LINE_COLOR;
4040
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/4;
4041
_via_draw_rect(x - VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4042
y - VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4043
w + VIA_THEME_REGION_BOUNDARY_WIDTH,
4044
h + VIA_THEME_REGION_BOUNDARY_WIDTH);
4045
_via_reg_ctx.stroke();
4046
4047
_via_draw_rect(x + VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4048
y + VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4049
w - VIA_THEME_REGION_BOUNDARY_WIDTH,
4050
h - VIA_THEME_REGION_BOUNDARY_WIDTH);
4051
_via_reg_ctx.stroke();
4052
}
4053
}
4054
}
4055
4056
function _via_draw_rect(x, y, w, h) {
4057
_via_reg_ctx.beginPath();
4058
_via_reg_ctx.moveTo(x , y);
4059
_via_reg_ctx.lineTo(x+w, y);
4060
_via_reg_ctx.lineTo(x+w, y+h);
4061
_via_reg_ctx.lineTo(x , y+h);
4062
_via_reg_ctx.closePath();
4063
}
4064
4065
function _via_draw_circle_region(cx, cy, r, is_selected) {
4066
if (is_selected) {
4067
_via_draw_circle(cx, cy, r);
4068
4069
_via_reg_ctx.strokeStyle = VIA_THEME_SEL_REGION_FILL_BOUNDARY_COLOR;
4070
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4071
_via_reg_ctx.stroke();
4072
4073
_via_reg_ctx.fillStyle = VIA_THEME_SEL_REGION_FILL_COLOR;
4074
_via_reg_ctx.globalAlpha = VIA_THEME_SEL_REGION_OPACITY;
4075
_via_reg_ctx.fill();
4076
_via_reg_ctx.globalAlpha = 1.0;
4077
4078
_via_draw_control_point(cx + r, cy);
4079
} else {
4080
// draw a fill line
4081
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4082
_via_draw_circle(cx, cy, r);
4083
_via_reg_ctx.stroke();
4084
4085
if ( r > VIA_THEME_REGION_BOUNDARY_WIDTH ) {
4086
// draw a boundary line on both sides of the fill line
4087
_via_reg_ctx.strokeStyle = VIA_THEME_BOUNDARY_LINE_COLOR;
4088
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/4;
4089
_via_draw_circle(cx, cy,
4090
r - VIA_THEME_REGION_BOUNDARY_WIDTH/2);
4091
_via_reg_ctx.stroke();
4092
_via_draw_circle(cx, cy,
4093
r + VIA_THEME_REGION_BOUNDARY_WIDTH/2);
4094
_via_reg_ctx.stroke();
4095
}
4096
}
4097
}
4098
4099
function _via_draw_circle(cx, cy, r) {
4100
_via_reg_ctx.beginPath();
4101
_via_reg_ctx.arc(cx, cy, r, 0, 2*Math.PI, false);
4102
_via_reg_ctx.closePath();
4103
}
4104
4105
function _via_draw_ellipse_region(cx, cy, rx, ry, rr, is_selected) {
4106
if (is_selected) {
4107
_via_draw_ellipse(cx, cy, rx, ry, rr);
4108
4109
_via_reg_ctx.strokeStyle = VIA_THEME_SEL_REGION_FILL_BOUNDARY_COLOR;
4110
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4111
_via_reg_ctx.stroke();
4112
4113
_via_reg_ctx.fillStyle = VIA_THEME_SEL_REGION_FILL_COLOR;
4114
_via_reg_ctx.globalAlpha = VIA_THEME_SEL_REGION_OPACITY;
4115
_via_reg_ctx.fill();
4116
_via_reg_ctx.globalAlpha = 1.0;
4117
4118
_via_draw_control_point(cx + rx * Math.cos(rr), cy + rx * Math.sin(rr));
4119
_via_draw_control_point(cx - rx * Math.cos(rr), cy - rx * Math.sin(rr));
4120
_via_draw_control_point(cx + ry * Math.sin(rr), cy - ry * Math.cos(rr));
4121
_via_draw_control_point(cx - ry * Math.sin(rr), cy + ry * Math.cos(rr));
4122
4123
} else {
4124
// draw a fill line
4125
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4126
_via_draw_ellipse(cx, cy, rx, ry, rr);
4127
_via_reg_ctx.stroke();
4128
4129
if ( rx > VIA_THEME_REGION_BOUNDARY_WIDTH &&
4130
ry > VIA_THEME_REGION_BOUNDARY_WIDTH ) {
4131
// draw a boundary line on both sides of the fill line
4132
_via_reg_ctx.strokeStyle = VIA_THEME_BOUNDARY_LINE_COLOR;
4133
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/4;
4134
_via_draw_ellipse(cx, cy,
4135
rx + VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4136
ry + VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4137
rr);
4138
_via_reg_ctx.stroke();
4139
_via_draw_ellipse(cx, cy,
4140
rx - VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4141
ry - VIA_THEME_REGION_BOUNDARY_WIDTH/2,
4142
rr);
4143
_via_reg_ctx.stroke();
4144
}
4145
}
4146
}
4147
4148
function _via_draw_ellipse(cx, cy, rx, ry, rr) {
4149
_via_reg_ctx.save();
4150
4151
_via_reg_ctx.beginPath();
4152
_via_reg_ctx.ellipse(cx, cy, rx, ry, rr, 0, 2 * Math.PI);
4153
4154
_via_reg_ctx.restore(); // restore to original state
4155
_via_reg_ctx.closePath();
4156
}
4157
4158
function _via_draw_polygon_region(all_points_x, all_points_y, is_selected, shape) {
4159
if ( is_selected ) {
4160
_via_reg_ctx.strokeStyle = VIA_THEME_SEL_REGION_FILL_BOUNDARY_COLOR;
4161
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4162
_via_reg_ctx.beginPath();
4163
_via_reg_ctx.moveTo(all_points_x[0], all_points_y[0]);
4164
for ( var i=1; i < all_points_x.length; ++i ) {
4165
_via_reg_ctx.lineTo(all_points_x[i], all_points_y[i]);
4166
}
4167
if ( shape === VIA_REGION_SHAPE.POLYGON ) {
4168
_via_reg_ctx.lineTo(all_points_x[0], all_points_y[0]); // close loop
4169
}
4170
_via_reg_ctx.stroke();
4171
4172
_via_reg_ctx.fillStyle = VIA_THEME_SEL_REGION_FILL_COLOR;
4173
_via_reg_ctx.globalAlpha = VIA_THEME_SEL_REGION_OPACITY;
4174
_via_reg_ctx.fill();
4175
_via_reg_ctx.globalAlpha = 1.0;
4176
for ( var i=0; i < all_points_x.length; ++i ) {
4177
_via_draw_control_point(all_points_x[i], all_points_y[i]);
4178
}
4179
} else {
4180
// draw a fill line
4181
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4182
_via_reg_ctx.beginPath();
4183
_via_reg_ctx.moveTo(all_points_x[0], all_points_y[0]);
4184
for ( var i=0; i < all_points_x.length; ++i ) {
4185
_via_reg_ctx.lineTo(all_points_x[i], all_points_y[i]);
4186
}
4187
if ( shape === VIA_REGION_SHAPE.POLYGON ) {
4188
_via_reg_ctx.lineTo(all_points_x[0], all_points_y[0]); // close loop
4189
}
4190
_via_reg_ctx.stroke();
4191
}
4192
}
4193
4194
function _via_draw_point_region(cx, cy, is_selected) {
4195
if (is_selected) {
4196
_via_draw_point(cx, cy, VIA_REGION_POINT_RADIUS);
4197
4198
_via_reg_ctx.strokeStyle = VIA_THEME_SEL_REGION_FILL_BOUNDARY_COLOR;
4199
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4200
_via_reg_ctx.stroke();
4201
4202
_via_reg_ctx.fillStyle = VIA_THEME_SEL_REGION_FILL_COLOR;
4203
_via_reg_ctx.globalAlpha = VIA_THEME_SEL_REGION_OPACITY;
4204
_via_reg_ctx.fill();
4205
_via_reg_ctx.globalAlpha = 1.0;
4206
} else {
4207
// draw a fill line
4208
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/2;
4209
_via_draw_point(cx, cy, VIA_REGION_POINT_RADIUS);
4210
_via_reg_ctx.stroke();
4211
4212
// draw a boundary line on both sides of the fill line
4213
_via_reg_ctx.strokeStyle = VIA_THEME_BOUNDARY_LINE_COLOR;
4214
_via_reg_ctx.lineWidth = VIA_THEME_REGION_BOUNDARY_WIDTH/4;
4215
_via_draw_point(cx, cy,
4216
VIA_REGION_POINT_RADIUS - VIA_THEME_REGION_BOUNDARY_WIDTH/2);
4217
_via_reg_ctx.stroke();
4218
_via_draw_point(cx, cy,
4219
VIA_REGION_POINT_RADIUS + VIA_THEME_REGION_BOUNDARY_WIDTH/2);
4220
_via_reg_ctx.stroke();
4221
}
4222
}
4223
4224
function _via_draw_point(cx, cy, r) {
4225
_via_reg_ctx.beginPath();
4226
_via_reg_ctx.arc(cx, cy, r, 0, 2*Math.PI, false);
4227
_via_reg_ctx.closePath();
4228
}
4229
4230
function draw_all_region_id() {
4231
_via_reg_ctx.shadowColor = "transparent";
4232
_via_reg_ctx.font = _via_settings.ui.image.region_label_font;
4233
for ( var i = 0; i < _via_img_metadata[_via_image_id].regions.length; ++i ) {
4234
var canvas_reg = _via_canvas_regions[i];
4235
4236
var bbox = get_region_bounding_box(canvas_reg);
4237
var x = bbox[0];
4238
var y = bbox[1];
4239
var w = Math.abs(bbox[2] - bbox[0]);
4240
4241
var char_width = _via_reg_ctx.measureText('M').width;
4242
var char_height = 1.8 * char_width;
4243
4244
var annotation_str = (i+1).toString();
4245
var rattr = _via_img_metadata[_via_image_id].regions[i].region_attributes[_via_settings.ui.image.region_label];
4246
var rshape = _via_img_metadata[_via_image_id].regions[i].shape_attributes['name'];
4247
if ( _via_settings.ui.image.region_label !== '__via_region_id__' ) {
4248
if ( typeof(rattr) !== 'undefined' ) {
4249
switch( typeof(rattr) ) {
4250
default:
4251
case 'string':
4252
annotation_str = rattr;
4253
break;
4254
case 'object':
4255
annotation_str = Object.keys(rattr).join(',');
4256
break;
4257
}
4258
} else {
4259
annotation_str = 'undefined';
4260
}
4261
}
4262
4263
var bgnd_rect_width;
4264
var strw = _via_reg_ctx.measureText(annotation_str).width;
4265
if ( strw > w ) {
4266
if ( _via_settings.ui.image.region_label === '__via_region_id__' ) {
4267
// region-id is always visible in full
4268
bgnd_rect_width = strw + char_width;
4269
} else {
4270
4271
// if text overflows, crop it
4272
var str_max = Math.floor((w * annotation_str.length) / strw);
4273
if ( str_max > 1 ) {
4274
annotation_str = annotation_str.substr(0, str_max-1) + '.';
4275
bgnd_rect_width = w;
4276
} else {
4277
annotation_str = annotation_str.substr(0, 1) + '.';
4278
bgnd_rect_width = 2 * char_width;
4279
}
4280
}
4281
} else {
4282
bgnd_rect_width = strw + char_width;
4283
}
4284
4285
if (canvas_reg.shape_attributes['name'] === VIA_REGION_SHAPE.POLYGON ||
4286
canvas_reg.shape_attributes['name'] === VIA_REGION_SHAPE.POLYLINE) {
4287
// put label near the first vertex
4288
x = canvas_reg.shape_attributes['all_points_x'][0];
4289
y = canvas_reg.shape_attributes['all_points_y'][0];
4290
} else {
4291
// center the label
4292
x = x - (bgnd_rect_width/2 - w/2);
4293
}
4294
4295
// ensure that the text is within the image boundaries
4296
if ( y < char_height ) {
4297
y = char_height;
4298
}
4299
4300
// first, draw a background rectangle first
4301
_via_reg_ctx.fillStyle = 'black';
4302
_via_reg_ctx.globalAlpha = 0.8;
4303
_via_reg_ctx.fillRect(Math.floor(x),
4304
Math.floor(y - 1.1*char_height),
4305
Math.floor(bgnd_rect_width),
4306
Math.floor(char_height));
4307
4308
// then, draw text over this background rectangle
4309
_via_reg_ctx.globalAlpha = 1.0;
4310
_via_reg_ctx.fillStyle = 'yellow';
4311
_via_reg_ctx.fillText(annotation_str,
4312
Math.floor(x + 0.4*char_width),
4313
Math.floor(y - 0.35*char_height));
4314
4315
}
4316
}
4317
4318
function get_region_bounding_box(region) {
4319
var d = region.shape_attributes;
4320
var bbox = new Array(4);
4321
4322
switch( d['name'] ) {
4323
case 'rect':
4324
bbox[0] = d['x'];
4325
bbox[1] = d['y'];
4326
bbox[2] = d['x'] + d['width'];
4327
bbox[3] = d['y'] + d['height'];
4328
break;
4329
4330
case 'circle':
4331
bbox[0] = d['cx'] - d['r'];
4332
bbox[1] = d['cy'] - d['r'];
4333
bbox[2] = d['cx'] + d['r'];
4334
bbox[3] = d['cy'] + d['r'];
4335
break;
4336
4337
case 'ellipse':
4338
let radians = d['theta'];
4339
let radians90 = radians + Math.PI / 2;
4340
let ux = d['rx'] * Math.cos(radians);
4341
let uy = d['rx'] * Math.sin(radians);
4342
let vx = d['ry'] * Math.cos(radians90);
4343
let vy = d['ry'] * Math.sin(radians90);
4344
4345
let width = Math.sqrt(ux * ux + vx * vx) * 2;
4346
let height = Math.sqrt(uy * uy + vy * vy) * 2;
4347
4348
bbox[0] = d['cx'] - (width / 2);
4349
bbox[1] = d['cy'] - (height / 2);
4350
bbox[2] = d['cx'] + (width / 2);
4351
bbox[3] = d['cy'] + (height / 2);
4352
break;
4353
4354
case 'polyline': // handled by polygon
4355
case 'polygon':
4356
var all_points_x = d['all_points_x'];
4357
var all_points_y = d['all_points_y'];
4358
4359
var minx = Number.MAX_SAFE_INTEGER;
4360
var miny = Number.MAX_SAFE_INTEGER;
4361
var maxx = 0;
4362
var maxy = 0;
4363
for ( var i=0; i < all_points_x.length; ++i ) {
4364
if ( all_points_x[i] < minx ) {
4365
minx = all_points_x[i];
4366
}
4367
if ( all_points_x[i] > maxx ) {
4368
maxx = all_points_x[i];
4369
}
4370
if ( all_points_y[i] < miny ) {
4371
miny = all_points_y[i];
4372
}
4373
if ( all_points_y[i] > maxy ) {
4374
maxy = all_points_y[i];
4375
}
4376
}
4377
bbox[0] = minx;
4378
bbox[1] = miny;
4379
bbox[2] = maxx;
4380
bbox[3] = maxy;
4381
break;
4382
4383
case 'point':
4384
bbox[0] = d['cx'] - VIA_REGION_POINT_RADIUS;
4385
bbox[1] = d['cy'] - VIA_REGION_POINT_RADIUS;
4386
bbox[2] = d['cx'] + VIA_REGION_POINT_RADIUS;
4387
bbox[3] = d['cy'] + VIA_REGION_POINT_RADIUS;
4388
break;
4389
}
4390
return bbox;
4391
}
4392
4393
//
4394
// Region collision routines
4395
//
4396
function is_inside_region(px, py, descending_order) {
4397
var N = _via_canvas_regions.length;
4398
if ( N === 0 ) {
4399
return -1;
4400
}
4401
var start, end, del;
4402
// traverse the canvas regions in alternating ascending
4403
// and descending order to solve the issue of nested regions
4404
if ( descending_order ) {
4405
start = N - 1;
4406
end = -1;
4407
del = -1;
4408
} else {
4409
start = 0;
4410
end = N;
4411
del = 1;
4412
}
4413
4414
var i = start;
4415
while ( i !== end ) {
4416
var yes = is_inside_this_region(px, py, i);
4417
if (yes) {
4418
return i;
4419
}
4420
i = i + del;
4421
}
4422
return -1;
4423
}
4424
4425
function is_inside_this_region(px, py, region_id) {
4426
var attr = _via_canvas_regions[region_id].shape_attributes;
4427
var result = false;
4428
switch ( attr['name'] ) {
4429
case VIA_REGION_SHAPE.RECT:
4430
result = is_inside_rect(attr['x'],
4431
attr['y'],
4432
attr['width'],
4433
attr['height'],
4434
px, py);
4435
break;
4436
4437
case VIA_REGION_SHAPE.CIRCLE:
4438
result = is_inside_circle(attr['cx'],
4439
attr['cy'],
4440
attr['r'],
4441
px, py);
4442
break;
4443
4444
case VIA_REGION_SHAPE.ELLIPSE:
4445
result = is_inside_ellipse(attr['cx'],
4446
attr['cy'],
4447
attr['rx'],
4448
attr['ry'],
4449
attr['theta'],
4450
px, py);
4451
break;
4452
4453
case VIA_REGION_SHAPE.POLYLINE: // handled by POLYGON
4454
case VIA_REGION_SHAPE.POLYGON:
4455
result = is_inside_polygon(attr['all_points_x'],
4456
attr['all_points_y'],
4457
px, py);
4458
break;
4459
4460
case VIA_REGION_SHAPE.POINT:
4461
result = is_inside_point(attr['cx'],
4462
attr['cy'],
4463
px, py);
4464
break;
4465
}
4466
return result;
4467
}
4468
4469
function is_inside_circle(cx, cy, r, px, py) {
4470
var dx = px - cx;
4471
var dy = py - cy;
4472
return (dx * dx + dy * dy) < r * r;
4473
}
4474
4475
function is_inside_rect(x, y, w, h, px, py) {
4476
return px > x &&
4477
px < (x + w) &&
4478
py > y &&
4479
py < (y + h);
4480
}
4481
4482
function is_inside_ellipse(cx, cy, rx, ry, rr, px, py) {
4483
// Inverse rotation of pixel coordinates
4484
var dx = Math.cos(-rr) * (cx - px) - Math.sin(-rr) * (cy - py)
4485
var dy = Math.sin(-rr) * (cx - px) + Math.cos(-rr) * (cy - py)
4486
4487
return ((dx * dx) / (rx * rx)) + ((dy * dy) / (ry * ry)) < 1;
4488
}
4489
4490
// returns 0 when (px,py) is outside the polygon
4491
// source: http://geomalgorithms.com/a03-_inclusion.html
4492
function is_inside_polygon(all_points_x, all_points_y, px, py) {
4493
if ( all_points_x.length === 0 || all_points_y.length === 0 ) {
4494
return 0;
4495
}
4496
4497
var wn = 0; // the winding number counter
4498
var n = all_points_x.length;
4499
var i;
4500
// loop through all edges of the polygon
4501
for ( i = 0; i < n-1; ++i ) { // edge from V[i] to V[i+1]
4502
var is_left_value = is_left( all_points_x[i], all_points_y[i],
4503
all_points_x[i+1], all_points_y[i+1],
4504
px, py);
4505
4506
if (all_points_y[i] <= py) {
4507
if (all_points_y[i+1] > py && is_left_value > 0) {
4508
++wn;
4509
}
4510
}
4511
else {
4512
if (all_points_y[i+1] <= py && is_left_value < 0) {
4513
--wn;
4514
}
4515
}
4516
}
4517
4518
// also take into account the loop closing edge that connects last point with first point
4519
var is_left_value = is_left( all_points_x[n-1], all_points_y[n-1],
4520
all_points_x[0], all_points_y[0],
4521
px, py);
4522
4523
if (all_points_y[n-1] <= py) {
4524
if (all_points_y[0] > py && is_left_value > 0) {
4525
++wn;
4526
}
4527
}
4528
else {
4529
if (all_points_y[0] <= py && is_left_value < 0) {
4530
--wn;
4531
}
4532
}
4533
4534
if ( wn === 0 ) {
4535
return 0;
4536
}
4537
else {
4538
return 1;
4539
}
4540
}
4541
4542
function is_inside_point(cx, cy, px, py) {
4543
var dx = px - cx;
4544
var dy = py - cy;
4545
var r2 = VIA_POLYGON_VERTEX_MATCH_TOL * VIA_POLYGON_VERTEX_MATCH_TOL;
4546
return (dx * dx + dy * dy) < r2;
4547
}
4548
4549
// returns
4550
// >0 if (x2,y2) lies on the left side of line joining (x0,y0) and (x1,y1)
4551
// =0 if (x2,y2) lies on the line joining (x0,y0) and (x1,y1)
4552
// >0 if (x2,y2) lies on the right side of line joining (x0,y0) and (x1,y1)
4553
// source: http://geomalgorithms.com/a03-_inclusion.html
4554
function is_left(x0, y0, x1, y1, x2, y2) {
4555
return ( ((x1 - x0) * (y2 - y0)) - ((x2 - x0) * (y1 - y0)) );
4556
}
4557
4558
function is_on_region_corner(px, py) {
4559
var _via_region_edge = [-1, -1]; // region_id, corner_id [top-left=1,top-right=2,bottom-right=3,bottom-left=4]
4560
4561
for ( var i = 0; i < _via_canvas_regions.length; ++i ) {
4562
var attr = _via_canvas_regions[i].shape_attributes;
4563
var result = false;
4564
_via_region_edge[0] = i;
4565
4566
switch ( attr['name'] ) {
4567
case VIA_REGION_SHAPE.RECT:
4568
result = is_on_rect_edge(attr['x'],
4569
attr['y'],
4570
attr['width'],
4571
attr['height'],
4572
px, py);
4573
break;
4574
4575
case VIA_REGION_SHAPE.CIRCLE:
4576
result = is_on_circle_edge(attr['cx'],
4577
attr['cy'],
4578
attr['r'],
4579
px, py);
4580
break;
4581
4582
case VIA_REGION_SHAPE.ELLIPSE:
4583
result = is_on_ellipse_edge(attr['cx'],
4584
attr['cy'],
4585
attr['rx'],
4586
attr['ry'],
4587
attr['theta'],
4588
px, py);
4589
break;
4590
4591
case VIA_REGION_SHAPE.POLYLINE: // handled by polygon
4592
case VIA_REGION_SHAPE.POLYGON:
4593
result = is_on_polygon_vertex(attr['all_points_x'],
4594
attr['all_points_y'],
4595
px, py);
4596
if ( result === 0 ) {
4597
result = is_on_polygon_edge(attr['all_points_x'],
4598
attr['all_points_y'],
4599
px, py);
4600
}
4601
break;
4602
4603
case VIA_REGION_SHAPE.POINT:
4604
// since there are no edges of a point
4605
result = 0;
4606
break;
4607
}
4608
4609
if (result > 0) {
4610
_via_region_edge[1] = result;
4611
return _via_region_edge;
4612
}
4613
}
4614
_via_region_edge[0] = -1;
4615
return _via_region_edge;
4616
}
4617
4618
function is_on_rect_edge(x, y, w, h, px, py) {
4619
var dx0 = Math.abs(x - px);
4620
var dy0 = Math.abs(y - py);
4621
var dx1 = Math.abs(x + w - px);
4622
var dy1 = Math.abs(y + h - py);
4623
//[top-left=1,top-right=2,bottom-right=3,bottom-left=4]
4624
if ( dx0 < VIA_REGION_EDGE_TOL &&
4625
dy0 < VIA_REGION_EDGE_TOL ) {
4626
return 1;
4627
}
4628
if ( dx1 < VIA_REGION_EDGE_TOL &&
4629
dy0 < VIA_REGION_EDGE_TOL ) {
4630
return 2;
4631
}
4632
if ( dx1 < VIA_REGION_EDGE_TOL &&
4633
dy1 < VIA_REGION_EDGE_TOL ) {
4634
return 3;
4635
}
4636
4637
if ( dx0 < VIA_REGION_EDGE_TOL &&
4638
dy1 < VIA_REGION_EDGE_TOL ) {
4639
return 4;
4640
}
4641
4642
var mx0 = Math.abs(x + w/2 - px);
4643
var my0 = Math.abs(y + h/2 - py);
4644
//[top-middle=5,right-middle=6,bottom-middle=7,left-middle=8]
4645
if ( mx0 < VIA_REGION_EDGE_TOL &&
4646
dy0 < VIA_REGION_EDGE_TOL ) {
4647
return 5;
4648
}
4649
if ( dx1 < VIA_REGION_EDGE_TOL &&
4650
my0 < VIA_REGION_EDGE_TOL ) {
4651
return 6;
4652
}
4653
if ( mx0 < VIA_REGION_EDGE_TOL &&
4654
dy1 < VIA_REGION_EDGE_TOL ) {
4655
return 7;
4656
}
4657
if ( dx0 < VIA_REGION_EDGE_TOL &&
4658
my0 < VIA_REGION_EDGE_TOL ) {
4659
return 8;
4660
}
4661
4662
return 0;
4663
}
4664
4665
function is_on_circle_edge(cx, cy, r, px, py) {
4666
var dx = cx - px;
4667
var dy = cy - py;
4668
if ( Math.abs(Math.sqrt( dx*dx + dy*dy ) - r) < VIA_REGION_EDGE_TOL ) {
4669
var theta = Math.atan2( py - cy, px - cx );
4670
if ( Math.abs(theta - (Math.PI/2)) < VIA_THETA_TOL ||
4671
Math.abs(theta + (Math.PI/2)) < VIA_THETA_TOL) {
4672
return 5;
4673
}
4674
if ( Math.abs(theta) < VIA_THETA_TOL ||
4675
Math.abs(Math.abs(theta) - Math.PI) < VIA_THETA_TOL) {
4676
return 6;
4677
}
4678
4679
if ( theta > 0 && theta < (Math.PI/2) ) {
4680
return 1;
4681
}
4682
if ( theta > (Math.PI/2) && theta < (Math.PI) ) {
4683
return 4;
4684
}
4685
if ( theta < 0 && theta > -(Math.PI/2) ) {
4686
return 2;
4687
}
4688
if ( theta < -(Math.PI/2) && theta > -Math.PI ) {
4689
return 3;
4690
}
4691
} else {
4692
return 0;
4693
}
4694
}
4695
4696
function is_on_ellipse_edge(cx, cy, rx, ry, rr, px, py) {
4697
// Inverse rotation of pixel coordinates
4698
px = px - cx;
4699
py = py - cy;
4700
var px_ = Math.cos(-rr) * px - Math.sin(-rr) * py;
4701
var py_ = Math.sin(-rr) * px + Math.cos(-rr) * py;
4702
px = px_ + cx;
4703
py = py_ + cy;
4704
4705
var dx = (cx - px)/rx;
4706
var dy = (cy - py)/ry;
4707
4708
if ( Math.abs(Math.sqrt( dx*dx + dy*dy ) - 1) < VIA_ELLIPSE_EDGE_TOL ) {
4709
var theta = Math.atan2( py - cy, px - cx );
4710
if ( Math.abs(theta - (Math.PI/2)) < VIA_THETA_TOL ||
4711
Math.abs(theta + (Math.PI/2)) < VIA_THETA_TOL) {
4712
return 5;
4713
}
4714
if ( Math.abs(theta) < VIA_THETA_TOL ||
4715
Math.abs(Math.abs(theta) - Math.PI) < VIA_THETA_TOL) {
4716
return 6;
4717
}
4718
} else {
4719
return 0;
4720
}
4721
}
4722
4723
function is_on_polygon_vertex(all_points_x, all_points_y, px, py) {
4724
var i, n;
4725
n = all_points_x.length;
4726
4727
for ( i = 0; i < n; ++i ) {
4728
if ( Math.abs(all_points_x[i] - px) < VIA_POLYGON_VERTEX_MATCH_TOL &&
4729
Math.abs(all_points_y[i] - py) < VIA_POLYGON_VERTEX_MATCH_TOL ) {
4730
return (VIA_POLYGON_RESIZE_VERTEX_OFFSET+i);
4731
}
4732
}
4733
return 0;
4734
}
4735
4736
function is_on_polygon_edge(all_points_x, all_points_y, px, py) {
4737
var i, n, di, d;
4738
n = all_points_x.length;
4739
d = [];
4740
for ( i = 0; i < n - 1; ++i ) {
4741
di = dist_to_line(px, py, all_points_x[i], all_points_y[i], all_points_x[i+1], all_points_y[i+1]);
4742
d.push(di);
4743
}
4744
// closing edge
4745
di = dist_to_line(px, py, all_points_x[n-1], all_points_y[n-1], all_points_x[0], all_points_y[0]);
4746
d.push(di);
4747
4748
var smallest_value = d[0];
4749
var smallest_index = 0;
4750
n = d.length;
4751
for ( i = 1; i < n; ++i ) {
4752
if ( d[i] < smallest_value ) {
4753
smallest_value = d[i];
4754
smallest_index = i;
4755
}
4756
}
4757
if ( smallest_value < VIA_POLYGON_VERTEX_MATCH_TOL ) {
4758
return (VIA_POLYGON_RESIZE_VERTEX_OFFSET + smallest_index);
4759
} else {
4760
return 0;
4761
}
4762
}
4763
4764
function is_point_inside_bounding_box(x, y, x1, y1, x2, y2) {
4765
// ensure that (x1,y1) is top left and (x2,y2) is bottom right corner of rectangle
4766
var rect = {};
4767
if( x1 < x2 ) {
4768
rect.x1 = x1;
4769
rect.x2 = x2;
4770
} else {
4771
rect.x1 = x2;
4772
rect.x2 = x1;
4773
}
4774
if ( y1 < y2 ) {
4775
rect.y1 = y1;
4776
rect.y2 = y2;
4777
} else {
4778
rect.y1 = y2;
4779
rect.y2 = y1;
4780
}
4781
4782
if ( x >= rect.x1 && x <= rect.x2 && y >= rect.y1 && y <= rect.y2 ) {
4783
return true;
4784
} else {
4785
return false;
4786
}
4787
}
4788
4789
function dist_to_line(x, y, x1, y1, x2, y2) {
4790
if ( is_point_inside_bounding_box(x, y, x1, y1, x2, y2) ) {
4791
var dy = y2 - y1;
4792
var dx = x2 - x1;
4793
var nr = Math.abs( dy*x - dx*y + x2*y1 - y2*x1 );
4794
var dr = Math.sqrt( dx*dx + dy*dy );
4795
var dist = nr / dr;
4796
return Math.round(dist);
4797
} else {
4798
return Number.MAX_SAFE_INTEGER;
4799
}
4800
}
4801
4802
function rect_standardize_coordinates(d) {
4803
// d[x0,y0,x1,y1]
4804
// ensures that (d[0],d[1]) is top-left corner while
4805
// (d[2],d[3]) is bottom-right corner
4806
if ( d[0] > d[2] ) {
4807
// swap
4808
var t = d[0];
4809
d[0] = d[2];
4810
d[2] = t;
4811
}
4812
4813
if ( d[1] > d[3] ) {
4814
// swap
4815
var t = d[1];
4816
d[1] = d[3];
4817
d[3] = t;
4818
}
4819
}
4820
4821
function rect_update_corner(corner_id, d, x, y, preserve_aspect_ratio) {
4822
// pre-condition : d[x0,y0,x1,y1] is standardized
4823
// post-condition : corner is moved ( d may not stay standardized )
4824
if (preserve_aspect_ratio) {
4825
switch(corner_id) {
4826
case 1: // Fall-through // top-left
4827
case 3: // bottom-right
4828
var dx = d[2] - d[0];
4829
var dy = d[3] - d[1];
4830
var norm = Math.sqrt( dx*dx + dy*dy );
4831
var nx = dx / norm; // x component of unit vector along the diagonal of rect
4832
var ny = dy / norm; // y component
4833
var proj = (x - d[0]) * nx + (y - d[1]) * ny;
4834
var proj_x = nx * proj;
4835
var proj_y = ny * proj;
4836
// constrain (mx,my) to lie on a line connecting (x0,y0) and (x1,y1)
4837
x = Math.round( d[0] + proj_x );
4838
y = Math.round( d[1] + proj_y );
4839
break;
4840
4841
case 2: // Fall-through // top-right
4842
case 4: // bottom-left
4843
var dx = d[2] - d[0];
4844
var dy = d[1] - d[3];
4845
var norm = Math.sqrt( dx*dx + dy*dy );
4846
var nx = dx / norm; // x component of unit vector along the diagonal of rect
4847
var ny = dy / norm; // y component
4848
var proj = (x - d[0]) * nx + (y - d[3]) * ny;
4849
var proj_x = nx * proj;
4850
var proj_y = ny * proj;
4851
// constrain (mx,my) to lie on a line connecting (x0,y0) and (x1,y1)
4852
x = Math.round( d[0] + proj_x );
4853
y = Math.round( d[3] + proj_y );
4854
break;
4855
}
4856
}
4857
4858
switch(corner_id) {
4859
case 1: // top-left
4860
d[0] = x;
4861
d[1] = y;
4862
break;
4863
4864
case 3: // bottom-right
4865
d[2] = x;
4866
d[3] = y;
4867
break;
4868
4869
case 2: // top-right
4870
d[2] = x;
4871
d[1] = y;
4872
break;
4873
4874
case 4: // bottom-left
4875
d[0] = x;
4876
d[3] = y;
4877
break;
4878
4879
case 5: // top-middle
4880
d[1] = y;
4881
break;
4882
4883
case 6: // right-middle
4884
d[2] = x;
4885
break;
4886
4887
case 7: // bottom-middle
4888
d[3] = y;
4889
break;
4890
4891
case 8: // left-middle
4892
d[0] = x;
4893
break;
4894
}
4895
}
4896
4897
function _via_update_ui_components() {
4898
if ( ! _via_current_image_loaded ) {
4899
return;
4900
}
4901
4902
show_message('Updating user interface components.');
4903
switch(_via_display_area_content_name) {
4904
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID:
4905
image_grid_set_content_panel_height_fixed();
4906
image_grid_set_content_to_current_group();
4907
break;
4908
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE:
4909
if ( !_via_is_window_resized && _via_current_image_loaded ) {
4910
_via_is_window_resized = true;
4911
_via_show_img(_via_image_index);
4912
4913
if (_via_is_canvas_zoomed) {
4914
reset_zoom_level();
4915
}
4916
}
4917
break;
4918
}
4919
}
4920
4921
//
4922
// Shortcut key handlers
4923
//
4924
function _via_window_keydown_handler(e) {
4925
if ( e.target === document.body ) {
4926
// process the keyboard event
4927
_via_handle_global_keydown_event(e);
4928
}
4929
}
4930
4931
// global keys are active irrespective of element focus
4932
// arrow keys, n, p, s, o, space, d, Home, End, PageUp, PageDown
4933
function _via_handle_global_keydown_event(e) {
4934
// zoom
4935
if (_via_current_image_loaded) {
4936
if ( e.key === "+") {
4937
zoom_in();
4938
return;
4939
}
4940
4941
if ( e.key === "=") {
4942
reset_zoom_level();
4943
return;
4944
}
4945
4946
if ( e.key === "-") {
4947
zoom_out();
4948
return;
4949
}
4950
}
4951
4952
if ( e.key === 'ArrowRight' || e.key === 'n') {
4953
move_to_next_image();
4954
e.preventDefault();
4955
return;
4956
}
4957
if ( e.key === 'ArrowLeft' || e.key === 'p') {
4958
move_to_prev_image();
4959
e.preventDefault();
4960
return;
4961
}
4962
4963
if ( e.key === 'ArrowUp' ) {
4964
region_visualisation_update('region_label', '__via_region_id__', 1);
4965
e.preventDefault();
4966
return;
4967
}
4968
4969
if ( e.key === 'ArrowDown' ) {
4970
region_visualisation_update('region_color', '__via_default_region_color__', -1);
4971
e.preventDefault();
4972
return;
4973
}
4974
4975
4976
if ( e.key === 'Home') {
4977
show_first_image();
4978
e.preventDefault();
4979
return;
4980
}
4981
if ( e.key === 'End') {
4982
show_last_image();
4983
e.preventDefault();
4984
return;
4985
}
4986
if ( e.key === 'PageDown') {
4987
jump_to_next_image_block();
4988
e.preventDefault();
4989
return;
4990
}
4991
if ( e.key === 'PageUp') {
4992
jump_to_prev_image_block();
4993
e.preventDefault();
4994
return;
4995
}
4996
4997
if ( e.key === 'a' ) {
4998
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
4999
// select all in image grid
5000
image_grid_group_toggle_select_all();
5001
}
5002
}
5003
5004
if ( e.key === 'Escape' ) {
5005
e.preventDefault();
5006
if ( _via_is_loading_current_image ) {
5007
_via_cancel_current_image_loading();
5008
}
5009
5010
if ( _via_is_user_resizing_region ) {
5011
// cancel region resizing action
5012
_via_is_user_resizing_region = false;
5013
}
5014
5015
if ( _via_is_region_selected ) {
5016
// clear all region selections
5017
_via_is_region_selected = false;
5018
_via_user_sel_region_id = -1;
5019
toggle_all_regions_selection(false);
5020
}
5021
5022
if ( _via_is_user_drawing_polygon ) {
5023
_via_is_user_drawing_polygon = false;
5024
_via_canvas_regions.splice(_via_current_polygon_region_id, 1);
5025
}
5026
5027
if ( _via_is_user_drawing_region ) {
5028
_via_is_user_drawing_region = false;
5029
}
5030
5031
if ( _via_is_user_resizing_region ) {
5032
_via_is_user_resizing_region = false
5033
}
5034
5035
if ( _via_is_user_moving_region ) {
5036
_via_is_user_moving_region = false
5037
}
5038
5039
_via_redraw_reg_canvas();
5040
return;
5041
}
5042
5043
if ( e.key === ' ' ) { // Space key
5044
if ( e.ctrlKey ) {
5045
annotation_editor_toggle_on_image_editor();
5046
} else {
5047
annotation_editor_toggle_all_regions_editor();
5048
}
5049
e.preventDefault();
5050
return;
5051
}
5052
5053
if ( e.key === 'F1' ) { // F1 for help
5054
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_GETTING_STARTED);
5055
e.preventDefault();
5056
return;
5057
}
5058
if ( e.key === 'F2' ) { // F2 for about
5059
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_ABOUT);
5060
e.preventDefault();
5061
return;
5062
}
5063
}
5064
5065
function _via_reg_canvas_keyup_handler(e) {
5066
if ( e.key === 'Control' ) {
5067
_via_is_ctrl_pressed = false;
5068
}
5069
}
5070
5071
function _via_reg_canvas_keydown_handler(e) {
5072
if ( e.key === 'Control' ) {
5073
_via_is_ctrl_pressed = true;
5074
}
5075
5076
if (_via_current_image_loaded) {
5077
if ( e.key === 'Enter' ) {
5078
if ( _via_current_shape === VIA_REGION_SHAPE.POLYLINE ||
5079
_via_current_shape === VIA_REGION_SHAPE.POLYGON) {
5080
_via_polyshape_finish_drawing();
5081
}
5082
}
5083
if ( e.key === 'Backspace' ) {
5084
if ( _via_current_shape === VIA_REGION_SHAPE.POLYLINE ||
5085
_via_current_shape === VIA_REGION_SHAPE.POLYGON) {
5086
_via_polyshape_delete_last_vertex();
5087
}
5088
}
5089
5090
if ( e.key === 'a' ) {
5091
sel_all_regions();
5092
e.preventDefault();
5093
return;
5094
}
5095
5096
if ( e.key === 'c' ) {
5097
if (_via_is_region_selected ||
5098
_via_is_all_region_selected) {
5099
copy_sel_regions();
5100
}
5101
e.preventDefault();
5102
return;
5103
}
5104
5105
if ( e.key === 'v' ) {
5106
paste_sel_regions_in_current_image();
5107
e.preventDefault();
5108
return;
5109
}
5110
5111
if ( e.key === 'b' ) {
5112
toggle_region_boundary_visibility();
5113
e.preventDefault();
5114
return;
5115
}
5116
5117
if ( e.key === 'l' ) {
5118
toggle_region_id_visibility();
5119
e.preventDefault();
5120
return;
5121
}
5122
5123
if ( e.key === 'd' ) {
5124
if ( _via_is_region_selected ||
5125
_via_is_all_region_selected ) {
5126
del_sel_regions();
5127
}
5128
e.preventDefault();
5129
return;
5130
}
5131
5132
if ( _via_is_region_selected ) {
5133
if ( e.key === 'ArrowRight' ||
5134
e.key === 'ArrowLeft' ||
5135
e.key === 'ArrowDown' ||
5136
e.key === 'ArrowUp' ) {
5137
var del = 1;
5138
if ( e.shiftKey ) {
5139
del = 10;
5140
}
5141
var move_x = 0;
5142
var move_y = 0;
5143
switch( e.key ) {
5144
case 'ArrowLeft':
5145
move_x = -del;
5146
break;
5147
case 'ArrowUp':
5148
move_y = -del;
5149
break;
5150
case 'ArrowRight':
5151
move_x = del;
5152
break;
5153
case 'ArrowDown':
5154
move_y = del;
5155
break;
5156
}
5157
_via_move_selected_regions(move_x, move_y);
5158
_via_redraw_reg_canvas();
5159
e.preventDefault();
5160
return;
5161
}
5162
}
5163
}
5164
_via_handle_global_keydown_event(e);
5165
}
5166
5167
function _via_polyshape_finish_drawing() {
5168
if ( _via_is_user_drawing_polygon ) {
5169
// double click is used to indicate completion of
5170
// polygon or polyline drawing action
5171
var new_region_id = _via_current_polygon_region_id;
5172
var new_region_shape = _via_current_shape;
5173
5174
var npts = _via_canvas_regions[new_region_id].shape_attributes['all_points_x'].length;
5175
if ( npts <=2 && new_region_shape === VIA_REGION_SHAPE.POLYGON ) {
5176
show_message('For a polygon, you must define at least 3 points. ' +
5177
'Press [Esc] to cancel drawing operation.!');
5178
return;
5179
}
5180
if ( npts <=1 && new_region_shape === VIA_REGION_SHAPE.POLYLINE ) {
5181
show_message('A polyline must have at least 2 points. ' +
5182
'Press [Esc] to cancel drawing operation.!');
5183
return;
5184
}
5185
5186
var img_id = _via_image_id;
5187
_via_current_polygon_region_id = -1;
5188
_via_is_user_drawing_polygon = false;
5189
_via_is_user_drawing_region = false;
5190
5191
_via_img_metadata[img_id].regions[new_region_id] = {}; // create placeholder
5192
_via_polyshape_add_new_polyshape(img_id, new_region_shape, new_region_id);
5193
select_only_region(new_region_id); // select new region
5194
set_region_annotations_to_default_value( new_region_id );
5195
annotation_editor_add_row( new_region_id );
5196
annotation_editor_scroll_to_row( new_region_id );
5197
5198
_via_redraw_reg_canvas();
5199
_via_reg_canvas.focus();
5200
}
5201
return;
5202
}
5203
5204
function _via_polyshape_delete_last_vertex() {
5205
if ( _via_is_user_drawing_polygon ) {
5206
var npts = _via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_x'].length;
5207
if ( npts > 0 ) {
5208
_via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_x'].splice(npts - 1, 1);
5209
_via_canvas_regions[_via_current_polygon_region_id].shape_attributes['all_points_y'].splice(npts - 1, 1);
5210
5211
_via_redraw_reg_canvas();
5212
_via_reg_canvas.focus();
5213
}
5214
}
5215
}
5216
5217
function _via_polyshape_add_new_polyshape(img_id, region_shape, region_id) {
5218
// add all polygon points stored in _via_canvas_regions[]
5219
var all_points_x = _via_canvas_regions[region_id].shape_attributes['all_points_x'].slice(0);
5220
var all_points_y = _via_canvas_regions[region_id].shape_attributes['all_points_y'].slice(0);
5221
5222
var canvas_all_points_x = [];
5223
var canvas_all_points_y = [];
5224
var n = all_points_x.length;
5225
var i;
5226
for ( i = 0; i < n; ++i ) {
5227
all_points_x[i] = Math.round( all_points_x[i] * _via_canvas_scale );
5228
all_points_y[i] = Math.round( all_points_y[i] * _via_canvas_scale );
5229
5230
canvas_all_points_x[i] = Math.round( all_points_x[i] / _via_canvas_scale );
5231
canvas_all_points_y[i] = Math.round( all_points_y[i] / _via_canvas_scale );
5232
}
5233
5234
var polygon_region = new file_region();
5235
polygon_region.shape_attributes['name'] = region_shape;
5236
polygon_region.shape_attributes['all_points_x'] = all_points_x;
5237
polygon_region.shape_attributes['all_points_y'] = all_points_y;
5238
_via_img_metadata[img_id].regions[region_id] = polygon_region;
5239
5240
// update canvas
5241
if ( img_id === _via_image_id ) {
5242
_via_canvas_regions[region_id].shape_attributes['name'] = region_shape;
5243
_via_canvas_regions[region_id].shape_attributes['all_points_x'] = canvas_all_points_x;
5244
_via_canvas_regions[region_id].shape_attributes['all_points_y'] = canvas_all_points_y;
5245
}
5246
}
5247
5248
function del_sel_regions() {
5249
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5250
return;
5251
}
5252
5253
if ( !_via_current_image_loaded ) {
5254
show_message('First load some images!');
5255
return;
5256
}
5257
5258
var del_region_count = 0;
5259
if ( _via_is_all_region_selected ) {
5260
del_region_count = _via_canvas_regions.length;
5261
_via_canvas_regions.splice(0);
5262
_via_img_metadata[_via_image_id].regions.splice(0);
5263
} else {
5264
var sorted_sel_reg_id = [];
5265
for ( var i = 0; i < _via_canvas_regions.length; ++i ) {
5266
if ( _via_region_selected_flag[i] ) {
5267
sorted_sel_reg_id.push(i);
5268
_via_region_selected_flag[i] = false;
5269
}
5270
}
5271
sorted_sel_reg_id.sort( function(a,b) {
5272
return (b-a);
5273
});
5274
for ( var i = 0; i < sorted_sel_reg_id.length; ++i ) {
5275
_via_canvas_regions.splice( sorted_sel_reg_id[i], 1);
5276
_via_img_metadata[_via_image_id].regions.splice( sorted_sel_reg_id[i], 1);
5277
del_region_count += 1;
5278
}
5279
5280
if ( sorted_sel_reg_id.length ) {
5281
_via_reg_canvas.style.cursor = "default";
5282
}
5283
}
5284
5285
_via_is_all_region_selected = false;
5286
_via_is_region_selected = false;
5287
_via_user_sel_region_id = -1;
5288
5289
if ( _via_canvas_regions.length === 0 ) {
5290
// all regions were deleted, hence clear region canvas
5291
_via_clear_reg_canvas();
5292
} else {
5293
_via_redraw_reg_canvas();
5294
}
5295
_via_reg_canvas.focus();
5296
annotation_editor_show();
5297
5298
show_message('Deleted ' + del_region_count + ' selected regions');
5299
}
5300
5301
function sel_all_regions() {
5302
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5303
image_grid_group_toggle_select_all();
5304
return;
5305
}
5306
5307
if (!_via_current_image_loaded) {
5308
show_message('First load some images!');
5309
return;
5310
}
5311
5312
toggle_all_regions_selection(true);
5313
_via_is_all_region_selected = true;
5314
_via_redraw_reg_canvas();
5315
}
5316
5317
function copy_sel_regions() {
5318
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5319
return;
5320
}
5321
5322
if (!_via_current_image_loaded) {
5323
show_message('First load some images!');
5324
return;
5325
}
5326
5327
if (_via_is_region_selected ||
5328
_via_is_all_region_selected) {
5329
_via_copied_image_regions.splice(0);
5330
for ( var i = 0; i < _via_img_metadata[_via_image_id].regions.length; ++i ) {
5331
var img_region = _via_img_metadata[_via_image_id].regions[i];
5332
var canvas_region = _via_canvas_regions[i];
5333
if ( _via_region_selected_flag[i] ) {
5334
_via_copied_image_regions.push( clone_image_region(img_region) );
5335
}
5336
}
5337
show_message('Copied ' + _via_copied_image_regions.length +
5338
' selected regions. Press Ctrl + v to paste');
5339
} else {
5340
show_message('Select a region first!');
5341
}
5342
}
5343
5344
function paste_sel_regions_in_current_image() {
5345
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5346
return;
5347
}
5348
5349
if ( !_via_current_image_loaded ) {
5350
show_message('First load some images!');
5351
return;
5352
}
5353
5354
if ( _via_copied_image_regions.length ) {
5355
var pasted_reg_count = 0;
5356
for ( var i = 0; i < _via_copied_image_regions.length; ++i ) {
5357
// ensure copied the regions are within this image's boundaries
5358
var bbox = get_region_bounding_box( _via_copied_image_regions[i] );
5359
if (bbox[2] < _via_current_image_width &&
5360
bbox[3] < _via_current_image_height) {
5361
var r = clone_image_region(_via_copied_image_regions[i]);
5362
_via_img_metadata[_via_image_id].regions.push(r);
5363
5364
pasted_reg_count += 1;
5365
}
5366
}
5367
_via_load_canvas_regions();
5368
var discarded_reg_count = _via_copied_image_regions.length - pasted_reg_count;
5369
show_message('Pasted ' + pasted_reg_count + ' regions. ' +
5370
'Discarded ' + discarded_reg_count + ' regions exceeding image boundary.');
5371
_via_redraw_reg_canvas();
5372
_via_reg_canvas.focus();
5373
} else {
5374
show_message('To paste a region, you first need to select a region and copy it!');
5375
}
5376
}
5377
5378
function paste_to_multiple_images_with_confirm() {
5379
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5380
return;
5381
}
5382
5383
if ( _via_copied_image_regions.length === 0 ) {
5384
show_message('First copy some regions!');
5385
return;
5386
}
5387
5388
var config = {'title':'Paste Regions to Multiple Images' };
5389
var input = { 'region_count': { type:'text', name:'Number of copied regions', value:_via_copied_image_regions.length, disabled:true },
5390
'prev_next_count':{ type:'text', name:'Copy to (count format)<br><span style="font-size:0.8rem">For example: to paste copied regions to the <i>previous 2 images</i> and <i>next 3 images</i>, type <strong>2,3</strong> in the textbox and to paste only in <i>next 5 images</i>, type <strong>0,5</strong></span>', placeholder:'2,3', disabled:false, size:30},
5391
'img_index_list':{ type:'text', name:'Copy to (image index list)<br><span style="font-size:0.8rem">For example: <strong>2-5,7,9</strong> pastes the copied regions to the images with the following id <i>2,3,4,5,7,9</i> and <strong>3,8,141</strong> pastes to the images with id <i>3,8 and 141</i></span>', placeholder:'2-5,7,9', disabled:false, size:30},
5392
'regex':{ type:'text', name:'Copy to filenames matching a regular expression<br><span style="font-size:0.8rem">For example: <strong>_large</strong> pastes the copied regions to all images whose filename contain the keyword <i>_large</i></span>', placeholder:'regular expression', disabled:false, size:30},
5393
'include_region_attributes':{ type:'checkbox', name:'Paste also the region annotations', checked:true},
5394
};
5395
5396
invoke_with_user_inputs(paste_to_multiple_images_confirmed, input, config);
5397
}
5398
5399
function paste_to_multiple_images_confirmed(input) {
5400
// keep a copy of user inputs for the undo operation
5401
_via_paste_to_multiple_images_input = input;
5402
var intersect = generate_img_index_list(input);
5403
var i;
5404
var total_pasted_region_count = 0;
5405
for ( i = 0; i < intersect.length; i++ ) {
5406
total_pasted_region_count += paste_regions( intersect[i] );
5407
}
5408
5409
show_message('Pasted [' + total_pasted_region_count + '] regions ' +
5410
'in ' + intersect.length + ' images');
5411
5412
if ( intersect.includes(_via_image_index) ) {
5413
_via_load_canvas_regions();
5414
_via_redraw_reg_canvas();
5415
_via_reg_canvas.focus();
5416
}
5417
user_input_default_cancel_handler();
5418
}
5419
5420
function paste_regions(img_index) {
5421
var pasted_reg_count = 0;
5422
if ( _via_copied_image_regions.length ) {
5423
var img_id = _via_image_id_list[img_index];
5424
var i;
5425
for ( i = 0; i < _via_copied_image_regions.length; ++i ) {
5426
var r = clone_image_region(_via_copied_image_regions[i]);
5427
_via_img_metadata[img_id].regions.push(r);
5428
5429
pasted_reg_count += 1;
5430
}
5431
}
5432
return pasted_reg_count;
5433
}
5434
5435
5436
function del_sel_regions_with_confirm() {
5437
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5438
return;
5439
}
5440
5441
if ( _via_copied_image_regions.length === 0 ) {
5442
show_message('First copy some regions!');
5443
return;
5444
}
5445
5446
var prev_next_count, img_index_list, regex;
5447
if ( _via_paste_to_multiple_images_input ) {
5448
prev_next_count = _via_paste_to_multiple_images_input.prev_next_count.value;
5449
img_index_list = _via_paste_to_multiple_images_input.img_index_list.value;
5450
regex = _via_paste_to_multiple_images_input.regex.value;
5451
}
5452
5453
var config = {'title':'Undo Regions Pasted to Multiple Images' };
5454
var input = { 'region_count': { type:'text', name:'Number of regions selected', value:_via_copied_image_regions.length, disabled:true },
5455
'prev_next_count':{ type:'text', name:'Delete from (count format)<br><span style="font-size:0.8rem">For example: to delete copied regions from the <i>previous 2 images</i> and <i>next 3 images</i>, type <strong>2,3</strong> in the textbox and to delete regions only in <i>next 5 images</i>, type <strong>0,5</strong></span>', placeholder:'2,3', disabled:false, size:30, value:prev_next_count},
5456
'img_index_list':{ type:'text', name:'Delete from (image index list)<br><span style="font-size:0.8rem">For example: <strong>2-5,7,9</strong> deletes the copied regions to the images with the following id <i>2,3,4,5,7,9</i> and <strong>3,8,141</strong> deletes regions from the images with id <i>3,8 and 141</i></span>', placeholder:'2-5,7,9', disabled:false, size:30, value:img_index_list},
5457
'regex':{ type:'text', name:'Delete from filenames matching a regular expression<br><span style="font-size:0.8rem">For example: <strong>_large</strong> deletes the copied regions from all images whose filename contain the keyword <i>_large</i></span>', placeholder:'regular expression', disabled:false, size:30, value:regex},
5458
};
5459
5460
invoke_with_user_inputs(del_sel_regions_confirmed, input, config);
5461
}
5462
5463
function del_sel_regions_confirmed(input) {
5464
user_input_default_cancel_handler();
5465
var intersect = generate_img_index_list(input);
5466
var i;
5467
var total_deleted_region_count = 0;
5468
for ( i = 0; i < intersect.length; i++ ) {
5469
total_deleted_region_count += delete_regions( intersect[i] );
5470
}
5471
5472
show_message('Deleted [' + total_deleted_region_count + '] regions ' +
5473
'in ' + intersect.length + ' images');
5474
5475
if ( intersect.includes(_via_image_index) ) {
5476
_via_load_canvas_regions();
5477
_via_redraw_reg_canvas();
5478
_via_reg_canvas.focus();
5479
}
5480
}
5481
5482
function delete_regions(img_index) {
5483
var del_region_count = 0;
5484
if ( _via_copied_image_regions.length ) {
5485
var img_id = _via_image_id_list[img_index];
5486
var i;
5487
for ( i = 0; i < _via_copied_image_regions.length; ++i ) {
5488
var copied_region_shape_str = JSON.stringify(_via_copied_image_regions[i].shape_attributes);
5489
var j;
5490
// start from last region in order to delete the last pasted region
5491
for ( j = _via_img_metadata[img_id].regions.length-1; j >= 0; --j ) {
5492
if ( JSON.stringify(_via_img_metadata[img_id].regions[j].shape_attributes) === copied_region_shape_str ) {
5493
_via_img_metadata[img_id].regions.splice( j, 1);
5494
del_region_count += 1;
5495
break; // delete only one matching region
5496
}
5497
}
5498
}
5499
}
5500
return del_region_count;
5501
}
5502
5503
function show_first_image() {
5504
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5505
if ( _via_image_grid_group_var.length ) {
5506
image_grid_group_prev( { 'value':0 } ); // simulate button click
5507
} else {
5508
show_message('First, create groups by selecting items from "Group by" dropdown list');
5509
}
5510
return;
5511
}
5512
5513
if (_via_img_count > 0) {
5514
_via_show_img( _via_img_fn_list_img_index_list[0] );
5515
}
5516
}
5517
5518
function show_last_image() {
5519
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5520
if ( _via_image_grid_group_var.length ) {
5521
image_grid_group_prev( { 'value':_via_image_grid_group_var.length-1 } ); // simulate button click
5522
} else {
5523
show_message('First, create groups by selecting items from "Group by" dropdown list');
5524
}
5525
return;
5526
}
5527
5528
if (_via_img_count > 0) {
5529
var last_img_index = _via_img_fn_list_img_index_list.length - 1;
5530
_via_show_img( _via_img_fn_list_img_index_list[ last_img_index ] );
5531
}
5532
}
5533
5534
function jump_image_block_get_count() {
5535
var n = _via_img_fn_list_img_index_list.length;
5536
if ( n < 20 ) {
5537
return 2;
5538
}
5539
if ( n < 100 ) {
5540
return 10;
5541
}
5542
if ( n < 1000 ) {
5543
return 25;
5544
}
5545
if ( n < 5000 ) {
5546
return 50;
5547
}
5548
if ( n < 10000 ) {
5549
return 100;
5550
}
5551
if ( n < 50000 ) {
5552
return 500;
5553
}
5554
5555
return Math.round( n / 50 );
5556
}
5557
5558
function jump_to_next_image_block() {
5559
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5560
return;
5561
}
5562
5563
var jump_count = jump_image_block_get_count();
5564
if ( jump_count > 1 ) {
5565
var current_img_index = _via_image_index;
5566
if ( _via_img_fn_list_img_index_list.includes( current_img_index ) ) {
5567
var list_index = _via_img_fn_list_img_index_list.indexOf( current_img_index );
5568
var next_list_index = list_index + jump_count;
5569
if ( (next_list_index + 1) > _via_img_fn_list_img_index_list.length ) {
5570
next_list_index = 0;
5571
}
5572
var next_img_index = _via_img_fn_list_img_index_list[next_list_index];
5573
_via_show_img(next_img_index);
5574
}
5575
} else {
5576
move_to_next_image();
5577
}
5578
}
5579
5580
function jump_to_prev_image_block() {
5581
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5582
return;
5583
}
5584
5585
var jump_count = jump_image_block_get_count();
5586
if ( jump_count > 1 ) {
5587
var current_img_index = _via_image_index;
5588
if ( _via_img_fn_list_img_index_list.includes( current_img_index ) ) {
5589
var list_index = _via_img_fn_list_img_index_list.indexOf( current_img_index );
5590
var prev_list_index = list_index - jump_count;
5591
if ( prev_list_index < 0 ) {
5592
prev_list_index = _via_img_fn_list_img_index_list.length - 1;
5593
}
5594
var prev_img_index = _via_img_fn_list_img_index_list[prev_list_index];
5595
_via_show_img(prev_img_index);
5596
}
5597
} else {
5598
move_to_prev_image();
5599
}
5600
}
5601
5602
function move_to_prev_image() {
5603
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5604
if ( _via_image_grid_group_var.length ) {
5605
var last_group_index = _via_image_grid_group_var.length - 1;
5606
image_grid_group_prev( { 'value':last_group_index } ); // simulate button click
5607
} else {
5608
show_message('First, create groups by selecting items from "Group by" dropdown list');
5609
}
5610
return;
5611
}
5612
5613
if (_via_img_count > 0) {
5614
var current_img_index = _via_image_index;
5615
if ( _via_img_fn_list_img_index_list.includes( current_img_index ) ) {
5616
var list_index = _via_img_fn_list_img_index_list.indexOf( current_img_index );
5617
var next_list_index = list_index - 1;
5618
if ( next_list_index === -1 ) {
5619
next_list_index = _via_img_fn_list_img_index_list.length - 1;
5620
}
5621
var next_img_index = _via_img_fn_list_img_index_list[next_list_index];
5622
_via_show_img(next_img_index);
5623
} else {
5624
if ( _via_img_fn_list_img_index_list.length === 0 ) {
5625
show_message('Filtered file list does not any files!');
5626
} else {
5627
_via_show_img( _via_img_fn_list_img_index_list[0] );
5628
}
5629
}
5630
5631
if (typeof _via_hook_prev_image === 'function') {
5632
_via_hook_prev_image(current_img_index);
5633
}
5634
}
5635
}
5636
5637
function move_to_next_image() {
5638
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5639
if ( _via_image_grid_group_var.length ) {
5640
var last_group_index = _via_image_grid_group_var.length - 1;
5641
image_grid_group_next( { 'value':last_group_index } ); // simulate button click
5642
} else {
5643
show_message('First, create groups by selecting items from "Group by" dropdown list');
5644
}
5645
return;
5646
}
5647
5648
if (_via_img_count > 0) {
5649
var current_img_index = _via_image_index;
5650
if ( _via_img_fn_list_img_index_list.includes( current_img_index ) ) {
5651
var list_index = _via_img_fn_list_img_index_list.indexOf( current_img_index );
5652
var next_list_index = list_index + 1;
5653
if ( next_list_index === _via_img_fn_list_img_index_list.length ) {
5654
next_list_index = 0;
5655
}
5656
var next_img_index = _via_img_fn_list_img_index_list[next_list_index];
5657
_via_show_img(next_img_index);
5658
} else {
5659
if ( _via_img_fn_list_img_index_list.length === 0 ) {
5660
show_message('Filtered file list does not contain any files!');
5661
} else {
5662
_via_show_img( _via_img_fn_list_img_index_list[0] );
5663
}
5664
}
5665
5666
if (typeof _via_hook_next_image === 'function') {
5667
_via_hook_next_image(current_img_index);
5668
}
5669
}
5670
}
5671
5672
function set_zoom(zoom_level_index) {
5673
if ( zoom_level_index === VIA_CANVAS_DEFAULT_ZOOM_LEVEL_INDEX ) {
5674
_via_is_canvas_zoomed = false;
5675
_via_canvas_zoom_level_index = VIA_CANVAS_DEFAULT_ZOOM_LEVEL_INDEX;
5676
} else {
5677
_via_is_canvas_zoomed = true;
5678
_via_canvas_zoom_level_index = zoom_level_index;
5679
}
5680
5681
var zoom_scale = VIA_CANVAS_ZOOM_LEVELS[_via_canvas_zoom_level_index];
5682
set_all_canvas_scale(zoom_scale);
5683
var canvas_w = ( _via_current_image.naturalWidth * zoom_scale ) / _via_canvas_scale_without_zoom;
5684
var canvas_h = ( _via_current_image.naturalHeight * zoom_scale ) / _via_canvas_scale_without_zoom;
5685
set_all_canvas_size(canvas_w, canvas_h);
5686
_via_canvas_scale = _via_canvas_scale_without_zoom / zoom_scale;
5687
_via_canvas_scale = _via_canvas_scale_without_zoom / zoom_scale;
5688
5689
if ( zoom_scale === 1 ) {
5690
VIA_REGION_POINT_RADIUS = VIA_REGION_POINT_RADIUS_DEFAULT;
5691
} else {
5692
if ( zoom_scale > 1 ) {
5693
VIA_REGION_POINT_RADIUS = VIA_REGION_POINT_RADIUS_DEFAULT * zoom_scale;
5694
}
5695
}
5696
5697
_via_load_canvas_regions(); // image to canvas space transform
5698
_via_redraw_reg_canvas();
5699
_via_reg_canvas.focus();
5700
update_vertical_space();
5701
}
5702
5703
function reset_zoom_level() {
5704
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5705
image_grid_image_size_reset();
5706
show_message('Zoom reset');
5707
return;
5708
}
5709
5710
if (!_via_current_image_loaded) {
5711
show_message('First load some images!');
5712
return;
5713
}
5714
5715
if (_via_is_canvas_zoomed) {
5716
set_zoom(VIA_CANVAS_DEFAULT_ZOOM_LEVEL_INDEX);
5717
show_message('Zoom reset');
5718
} else {
5719
show_message('Cannot reset zoom because image zoom has not been applied!');
5720
}
5721
update_vertical_space();
5722
}
5723
5724
function zoom_in() {
5725
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5726
image_grid_image_size_increase();
5727
show_message('Increased size of images shown in image grid');
5728
return;
5729
}
5730
5731
if (!_via_current_image_loaded) {
5732
show_message('First load some images!');
5733
return;
5734
}
5735
5736
if ( _via_is_user_drawing_polygon || _via_is_user_drawing_region ) {
5737
return;
5738
}
5739
5740
if (_via_canvas_zoom_level_index === (VIA_CANVAS_ZOOM_LEVELS.length-1)) {
5741
show_message('Further zoom-in not possible');
5742
} else {
5743
var new_zoom_level_index = _via_canvas_zoom_level_index + 1;
5744
set_zoom( new_zoom_level_index );
5745
show_message('Zoomed in to level ' + VIA_CANVAS_ZOOM_LEVELS[_via_canvas_zoom_level_index] + 'X');
5746
}
5747
}
5748
5749
function zoom_out() {
5750
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5751
image_grid_image_size_decrease();
5752
show_message('Reduced size of images shown in image grid');
5753
return;
5754
}
5755
5756
if (!_via_current_image_loaded) {
5757
show_message('First load some images!');
5758
return;
5759
}
5760
5761
if ( _via_is_user_drawing_polygon || _via_is_user_drawing_region ) {
5762
return;
5763
}
5764
5765
if (_via_canvas_zoom_level_index === 0) {
5766
show_message('Further zoom-out not possible');
5767
} else {
5768
var new_zoom_level_index = _via_canvas_zoom_level_index - 1;
5769
set_zoom( new_zoom_level_index );
5770
show_message('Zoomed out to level ' + VIA_CANVAS_ZOOM_LEVELS[_via_canvas_zoom_level_index] + 'X');
5771
}
5772
}
5773
5774
function toggle_region_boundary_visibility() {
5775
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE ) {
5776
_via_is_region_boundary_visible = !_via_is_region_boundary_visible;
5777
_via_redraw_reg_canvas();
5778
_via_reg_canvas.focus();
5779
}
5780
5781
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
5782
if ( _via_settings.ui.image_grid.show_region_shape ) {
5783
_via_settings.ui.image_grid.show_region_shape = false;
5784
document.getElementById('image_grid_content_rshape').innerHTML = '';
5785
} else {
5786
_via_settings.ui.image_grid.show_region_shape = true;
5787
image_grid_page_show_all_regions();
5788
}
5789
}
5790
}
5791
5792
function toggle_region_id_visibility() {
5793
_via_is_region_id_visible = !_via_is_region_id_visible;
5794
_via_redraw_reg_canvas();
5795
_via_reg_canvas.focus();
5796
}
5797
5798
function toggle_region_info_visibility() {
5799
var elem = document.getElementById('region_info');
5800
// toggle between displaying and not displaying
5801
if ( elem.classList.contains('display_none') ) {
5802
elem.classList.remove('display_none');
5803
_via_is_region_info_visible = true;
5804
} else {
5805
elem.classList.add('display_none');
5806
_via_is_region_info_visible = false;
5807
}
5808
}
5809
5810
//
5811
// Mouse wheel event listener
5812
//
5813
function _via_reg_canvas_mouse_wheel_listener(e) {
5814
if (!_via_current_image_loaded) {
5815
return;
5816
}
5817
5818
if ( e.ctrlKey ) {
5819
// perform zoom
5820
if (e.deltaY < 0) {
5821
zoom_in();
5822
} else {
5823
zoom_out();
5824
}
5825
e.preventDefault();
5826
}
5827
}
5828
5829
function region_visualisation_update(type, default_id, next_offset) {
5830
var attr_list = [ default_id ];
5831
attr_list = attr_list.concat(Object.keys(_via_attributes['region']));
5832
var n = attr_list.length;
5833
var current_index = attr_list.indexOf(_via_settings.ui.image[type]);
5834
var new_index;
5835
if ( current_index !== -1 ) {
5836
new_index = current_index + next_offset;
5837
5838
if ( new_index < 0 ) {
5839
new_index = n + new_index;
5840
}
5841
if ( new_index >= n ) {
5842
new_index = new_index - n;
5843
}
5844
switch(type) {
5845
case 'region_label':
5846
_via_settings.ui.image.region_label = attr_list[new_index];
5847
_via_redraw_reg_canvas();
5848
break;
5849
case 'region_color':
5850
_via_settings.ui.image.region_color = attr_list[new_index];
5851
_via_regions_group_color_init();
5852
_via_redraw_reg_canvas();
5853
}
5854
5855
var type_str = type.replace('_', ' ');
5856
if ( _via_settings.ui.image[type].startsWith('__via') ) {
5857
show_message(type_str + ' cleared');
5858
} else {
5859
show_message(type_str + ' set to region attribute [' + _via_settings.ui.image[type] + ']');
5860
}
5861
}
5862
}
5863
5864
//
5865
// left sidebar toolbox maintainer
5866
//
5867
function leftsidebar_toggle() {
5868
var leftsidebar = document.getElementById('leftsidebar');
5869
if ( leftsidebar.style.display === 'none' ) {
5870
leftsidebar.style.display = 'table-cell';
5871
document.getElementById('leftsidebar_collapse_panel').style.display = 'none';
5872
} else {
5873
leftsidebar.style.display = 'none';
5874
document.getElementById('leftsidebar_collapse_panel').style.display = 'table-cell';
5875
}
5876
_via_update_ui_components();
5877
}
5878
5879
function leftsidebar_increase_width() {
5880
var leftsidebar = document.getElementById('leftsidebar');
5881
var new_width = _via_settings.ui.leftsidebar_width + VIA_LEFTSIDEBAR_WIDTH_CHANGE;
5882
leftsidebar.style.width = new_width + 'rem';
5883
_via_settings.ui.leftsidebar_width = new_width;
5884
if ( _via_current_image_loaded ) {
5885
_via_show_img(_via_image_index);
5886
}
5887
}
5888
5889
function leftsidebar_decrease_width() {
5890
var leftsidebar = document.getElementById('leftsidebar');
5891
var new_width = _via_settings.ui.leftsidebar_width - VIA_LEFTSIDEBAR_WIDTH_CHANGE;
5892
if ( new_width >= 5 ) {
5893
leftsidebar.style.width = new_width + 'rem';
5894
_via_settings.ui.leftsidebar_width = new_width;
5895
if ( _via_current_image_loaded ) {
5896
_via_show_img(_via_image_index);
5897
}
5898
}
5899
}
5900
5901
function leftsidebar_show() {
5902
var leftsidebar = document.getElementById('leftsidebar');
5903
leftsidebar.style.display = 'table-cell';
5904
document.getElementById('leftsidebar_collapse_panel').style.display = 'none';
5905
}
5906
5907
// source: https://www.w3schools.com/howto/howto_js_accordion.asp
5908
function init_leftsidebar_accordion() {
5909
var leftsidebar = document.getElementById('leftsidebar');
5910
leftsidebar.style.width = _via_settings.ui.leftsidebar_width + 'rem';
5911
5912
var acc = document.getElementsByClassName('leftsidebar_accordion');
5913
var i;
5914
for ( i = 0; i < acc.length; ++i ) {
5915
acc[i].addEventListener('click', function() {
5916
update_vertical_space();
5917
this.classList.toggle('active');
5918
this.nextElementSibling.classList.toggle('show');
5919
5920
switch( this.innerHTML ) {
5921
case 'Attributes':
5922
update_attributes_update_panel();
5923
break;
5924
case 'Project':
5925
update_img_fn_list();
5926
break;
5927
}
5928
});
5929
}
5930
}
5931
5932
//
5933
// image filename list shown in leftsidebar panel
5934
//
5935
function is_img_fn_list_visible() {
5936
return img_fn_list_panel.classList.contains('show');
5937
}
5938
5939
function img_loading_spinbar(image_index, show) {
5940
var panel = document.getElementById('project_panel_title');
5941
if ( show ) {
5942
panel.innerHTML = 'Project <span style="margin-left:1rem;" class="loading_spinbox"></span>';
5943
} else {
5944
panel.innerHTML = 'Project';
5945
}
5946
}
5947
5948
function update_img_fn_list() {
5949
var regex = document.getElementById('img_fn_list_regex').value;
5950
var p = document.getElementById('filelist_preset_filters_list');
5951
if ( regex === '' || regex === null ) {
5952
if ( p.selectedIndex === 0 ) {
5953
// show all files
5954
_via_img_fn_list_html = [];
5955
_via_img_fn_list_img_index_list = [];
5956
_via_img_fn_list_html.push('<ul>');
5957
for ( var i=0; i < _via_image_filename_list.length; ++i ) {
5958
_via_img_fn_list_html.push( img_fn_list_ith_entry_html(i) );
5959
_via_img_fn_list_img_index_list.push(i);
5960
}
5961
_via_img_fn_list_html.push('</ul>');
5962
img_fn_list.innerHTML = _via_img_fn_list_html.join('');
5963
img_fn_list_scroll_to_current_file();
5964
} else {
5965
// filter according to preset filters
5966
img_fn_list_onpresetfilter_select();
5967
}
5968
} else {
5969
if ( p.selectedIndex === 6 ) {
5970
// Filter By Region Attribute
5971
filter_selected_region_attribute(regex);
5972
} else {
5973
// RegExp
5974
img_fn_list_generate_html(regex);
5975
img_fn_list.innerHTML = _via_img_fn_list_html.join('');
5976
img_fn_list_scroll_to_current_file();
5977
}
5978
}
5979
}
5980
5981
function img_fn_list_onregex() {
5982
var regex = document.getElementById('img_fn_list_regex').value;
5983
var p = document.getElementById('filelist_preset_filters_list');
5984
5985
if ( p.selectedIndex === 6 ) {
5986
filter_selected_region_attribute(regex);
5987
return;
5988
}
5989
5990
img_fn_list_generate_html( regex );
5991
img_fn_list.innerHTML = _via_img_fn_list_html.join('');
5992
img_fn_list_scroll_to_current_file();
5993
5994
// select 'regex' in the predefined filter list ( Unless Filter By Attribute is Selected)
5995
if ( regex === '' ) {
5996
p.selectedIndex = 0;
5997
} else {
5998
if ( p.options[p.selectedIndex].value != 'files_region_attribute' &&
5999
p.options[p.selectedIndex].value != 'regex' ) {
6000
var i;
6001
for ( i=0; i<p.options.length; ++i) {
6002
if ( p.options[i].value === 'regex' ) {
6003
p.selectedIndex = i;
6004
break;
6005
}
6006
}
6007
}
6008
}
6009
}
6010
6011
function img_fn_list_onpresetfilter_select() {
6012
var p = document.getElementById('filelist_preset_filters_list');
6013
var filter = p.options[p.selectedIndex].value;
6014
switch(filter) {
6015
case 'all':
6016
document.getElementById('img_fn_list_regex').value = '';
6017
img_fn_list_generate_html();
6018
img_fn_list.innerHTML = _via_img_fn_list_html.join('');
6019
img_fn_list_scroll_to_current_file();
6020
break;
6021
case 'regex':
6022
img_fn_list_onregex();
6023
document.getElementById('img_fn_list_regex').focus();
6024
break;
6025
case 'files_region_attribute':
6026
img_fn_list_onregex();
6027
document.getElementById('img_fn_list_regex').focus();
6028
break;
6029
default:
6030
_via_img_fn_list_html = [];
6031
_via_img_fn_list_img_index_list = [];
6032
_via_img_fn_list_html.push('<ul>');
6033
var i;
6034
for ( i=0; i < _via_image_filename_list.length; ++i ) {
6035
var img_id = _via_image_id_list[i];
6036
var add_to_list = false;
6037
switch(filter) {
6038
case 'files_without_region':
6039
if ( _via_img_metadata[img_id].regions.length === 0 ) {
6040
add_to_list = true;
6041
}
6042
break;
6043
case 'files_missing_region_annotations':
6044
if ( is_region_annotation_missing(img_id) ) {
6045
add_to_list = true;
6046
}
6047
break;
6048
case 'files_missing_file_annotations':
6049
if ( is_file_annotation_missing(img_id) ) {
6050
add_to_list = true;
6051
}
6052
break;
6053
case 'files_error_loading':
6054
if ( _via_image_load_error[i] === true ) {
6055
add_to_list = true;
6056
}
6057
}
6058
if ( add_to_list ) {
6059
_via_img_fn_list_html.push( img_fn_list_ith_entry_html(i) );
6060
_via_img_fn_list_img_index_list.push(i);
6061
}
6062
}
6063
_via_img_fn_list_html.push('</ul>');
6064
img_fn_list.innerHTML = _via_img_fn_list_html.join('');
6065
img_fn_list_scroll_to_current_file();
6066
break;
6067
}
6068
}
6069
6070
function filter_selected_region_attribute(property_entry) {
6071
const propArray = property_entry.split("=");
6072
if ( propArray.length != 2 || propArray[0] === "" || propArray[1] === "") return;
6073
var selected_attribute = propArray[0];
6074
var selected_value = propArray[1];
6075
if ( selected_value === "\"\"") selected_value = "";
6076
6077
_via_img_fn_list_html = [];
6078
_via_img_fn_list_img_index_list = [];
6079
_via_img_fn_list_html.push('<ul>');
6080
6081
if ( _via_attributes.region.hasOwnProperty(selected_attribute) ) {
6082
var i;
6083
6084
for ( i=0; i < _via_image_filename_list.length; ++i ) {
6085
var img_id = _via_image_id_list[i];
6086
var add_to_list = false;
6087
var idx;
6088
6089
for ( idx = 0; idx < _via_img_metadata[img_id].regions.length; ++idx ) {
6090
if ( _via_img_metadata[img_id].regions[idx].region_attributes.hasOwnProperty(selected_attribute) ) {
6091
if ( JSON.stringify(_via_img_metadata[img_id].regions[idx].region_attributes[selected_attribute]) === JSON.stringify(selected_value) ) {
6092
add_to_list = true;
6093
_via_img_fn_list_html.push( img_fn_list_ith_entry_html(i) );
6094
_via_img_fn_list_img_index_list.push(i);
6095
break;
6096
}
6097
}
6098
}
6099
}
6100
}
6101
_via_img_fn_list_html.push('</ul>');
6102
img_fn_list.innerHTML = _via_img_fn_list_html.join('');
6103
img_fn_list_scroll_to_current_file();
6104
}
6105
6106
function is_region_annotation_missing(img_id) {
6107
var region_attribute;
6108
var i;
6109
for ( i = 0; i < _via_img_metadata[img_id].regions.length; ++i ) {
6110
for ( region_attribute in _via_attributes['region'] ) {
6111
if ( _via_img_metadata[img_id].regions[i].region_attributes.hasOwnProperty(region_attribute) ) {
6112
if ( _via_img_metadata[img_id].regions[i].region_attributes[region_attribute] === '' ) {
6113
return true;
6114
}
6115
} else {
6116
return true;
6117
}
6118
}
6119
}
6120
return false;
6121
}
6122
6123
function is_file_annotation_missing(img_id) {
6124
var file_attribute;
6125
for ( file_attribute in _via_attributes['file'] ) {
6126
if ( _via_img_metadata[img_id].file_attributes.hasOwnProperty(file_attribute) ) {
6127
if ( _via_img_metadata[img_id].file_attributes[file_attribute] === '' ) {
6128
return true;
6129
}
6130
} else {
6131
return true;
6132
}
6133
}
6134
return false;
6135
}
6136
6137
function img_fn_list_ith_entry_selected(img_index, is_selected) {
6138
if ( is_selected ) {
6139
img_fn_list_ith_entry_add_css_class(img_index, 'sel');
6140
} else {
6141
img_fn_list_ith_entry_remove_css_class(img_index, 'sel');
6142
}
6143
}
6144
6145
function img_fn_list_ith_entry_error(img_index, is_error) {
6146
if ( is_error ) {
6147
img_fn_list_ith_entry_add_css_class(img_index, 'error');
6148
} else {
6149
img_fn_list_ith_entry_remove_css_class(img_index, 'error');
6150
}
6151
}
6152
6153
function img_fn_list_ith_entry_add_css_class(img_index, classname) {
6154
var li = document.getElementById('fl' + img_index);
6155
if ( li && ! li.classList.contains(classname) ) {
6156
li.classList.add(classname);
6157
}
6158
}
6159
6160
function img_fn_list_ith_entry_remove_css_class(img_index, classname) {
6161
var li = document.getElementById('fl' + img_index);
6162
if ( li && li.classList.contains(classname) ) {
6163
li.classList.remove(classname);
6164
}
6165
}
6166
6167
function img_fn_list_clear_all_style() {
6168
var cn = document.getElementById('img_fn_list').childNodes[0].childNodes;
6169
var i, j;
6170
var n = cn.length;
6171
var nclass;
6172
for ( i = 0; i < n; ++i ) {
6173
//cn[i].classList = []; // throws error in Edge browser
6174
nclass = cn[i].classList.length;
6175
if ( nclass ) {
6176
for ( j = 0; j < nclass; ++j ) {
6177
cn[i].classList.remove( cn[i].classList.item(j) );
6178
}
6179
}
6180
}
6181
}
6182
6183
function img_fn_list_clear_css_classname(classname) {
6184
var cn = document.getElementById('img_fn_list').childNodes[0].childNodes;
6185
var i;
6186
var n = cn.length;
6187
for ( i = 0; i < n; ++i ) {
6188
if ( cn[i].classList.contains(classname) ) {
6189
cn[i].classList.remove(classname);
6190
}
6191
}
6192
}
6193
6194
function img_fn_list_ith_entry_html(i) {
6195
var htmli = '';
6196
var filename = _via_image_filename_list[i];
6197
if ( is_url(filename) ) {
6198
filename = filename.substr(0,4) + '...' + get_filename_from_url(filename);
6199
}
6200
6201
htmli += '<li id="fl' + i + '"';
6202
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
6203
if ( _via_image_grid_page_img_index_list.includes(i) ) {
6204
// highlight images being shown in image grid
6205
htmli += ' class="sel"';
6206
}
6207
6208
} else {
6209
if ( i === _via_image_index ) {
6210
// highlight the current entry
6211
htmli += ' class="sel"';
6212
}
6213
}
6214
htmli += ' onclick="jump_to_image(' + (i) + ')" title="' + _via_image_filename_list[i] + '">[' + (i+1) + '] ' + decodeURIComponent(filename) + '</li>';
6215
return htmli;
6216
}
6217
6218
function img_fn_list_generate_html(regex) {
6219
_via_img_fn_list_html = [];
6220
_via_img_fn_list_img_index_list = [];
6221
_via_img_fn_list_html.push('<ul>');
6222
for ( var i=0; i < _via_image_filename_list.length; ++i ) {
6223
var filename = _via_image_filename_list[i];
6224
if ( filename.match(regex) !== null ) {
6225
_via_img_fn_list_html.push( img_fn_list_ith_entry_html(i) );
6226
_via_img_fn_list_img_index_list.push(i);
6227
}
6228
}
6229
_via_img_fn_list_html.push('</ul>');
6230
}
6231
6232
function img_fn_list_scroll_to_current_file() {
6233
img_fn_list_scroll_to_file( _via_image_index );
6234
}
6235
6236
function img_fn_list_scroll_to_file(file_index) {
6237
if( _via_img_fn_list_img_index_list.includes(file_index) ) {
6238
var sel_file = document.getElementById( 'fl' + file_index );
6239
var panel_height = img_fn_list.clientHeight - 20;
6240
var window_top = img_fn_list.scrollTop;
6241
var window_bottom = img_fn_list.scrollTop + panel_height
6242
if ( sel_file.offsetTop > window_top ) {
6243
if ( sel_file.offsetTop > window_bottom ) {
6244
img_fn_list.scrollTop = sel_file.offsetTop;
6245
}
6246
} else {
6247
img_fn_list.scrollTop = sel_file.offsetTop - panel_height;
6248
}
6249
}
6250
}
6251
6252
function toggle_img_fn_list_visibility() {
6253
leftsidebar_show();
6254
document.getElementById('img_fn_list_panel').classList.toggle('show');
6255
document.getElementById('project_panel_title').classList.toggle('active');
6256
}
6257
6258
function toggle_attributes_editor() {
6259
leftsidebar_show();
6260
document.getElementById('attributes_editor_panel').classList.toggle('show');
6261
document.getElementById('attributes_editor_panel_title').classList.toggle('active');
6262
}
6263
6264
// this vertical spacer is needed to allow scrollbar to show
6265
// items like Keyboard Shortcut hidden under the attributes panel
6266
function update_vertical_space() {
6267
var panel = document.getElementById('vertical_space');
6268
var aepanel = document.getElementById('annotation_editor_panel');
6269
panel.style.height = (aepanel.offsetHeight + 40) + 'px';
6270
}
6271
6272
//
6273
// region and file attributes update panel
6274
//
6275
function attribute_update_panel_set_active_button() {
6276
var attribute_type;
6277
for ( attribute_type in _via_attributes ) {
6278
var bid = 'button_show_' + attribute_type + '_attributes';
6279
document.getElementById(bid).classList.remove('active');
6280
}
6281
var bid = 'button_show_' + _via_attribute_being_updated + '_attributes';
6282
document.getElementById(bid).classList.add('active');
6283
}
6284
6285
function show_region_attributes_update_panel() {
6286
_via_attribute_being_updated = 'region';
6287
var rattr_list = Object.keys(_via_attributes['region']);
6288
if ( rattr_list.length ) {
6289
_via_current_attribute_id = rattr_list[0];
6290
} else {
6291
_via_current_attribute_id = '';
6292
}
6293
update_attributes_update_panel();
6294
attribute_update_panel_set_active_button();
6295
6296
}
6297
6298
function show_file_attributes_update_panel() {
6299
_via_attribute_being_updated = 'file';
6300
var fattr_list = Object.keys(_via_attributes['file']);
6301
if ( fattr_list.length ) {
6302
_via_current_attribute_id = fattr_list[0];
6303
} else {
6304
_via_current_attribute_id = '';
6305
}
6306
update_attributes_update_panel();
6307
attribute_update_panel_set_active_button();
6308
}
6309
6310
function update_attributes_name_list() {
6311
var p = document.getElementById('attributes_name_list');
6312
p.innerHTML = '';
6313
6314
var attr;
6315
for ( attr in _via_attributes[_via_attribute_being_updated] ) {
6316
var option = document.createElement('option');
6317
option.setAttribute('value', attr)
6318
option.innerHTML = attr;
6319
if ( attr === _via_current_attribute_id ) {
6320
option.setAttribute('selected', 'selected');
6321
}
6322
p.appendChild(option);
6323
}
6324
}
6325
6326
function update_attributes_update_panel() {
6327
if ( document.getElementById('attributes_editor_panel').classList.contains('show') ) {
6328
update_attributes_name_list();
6329
show_attribute_properties();
6330
show_attribute_options();
6331
}
6332
}
6333
6334
function update_attribute_properties_panel() {
6335
if ( document.getElementById('attributes_editor_panel').classList.contains('show') ) {
6336
show_attribute_properties();
6337
show_attribute_options();
6338
}
6339
}
6340
6341
function show_attribute_properties() {
6342
var attr_list = document.getElementById('attributes_name_list');
6343
document.getElementById('attribute_properties').innerHTML = '';
6344
6345
if ( attr_list.options.length === 0 ) {
6346
return;
6347
}
6348
6349
if ( typeof(_via_current_attribute_id) === 'undefined' || _via_current_attribute_id === '' ) {
6350
_via_current_attribute_id = attr_list.options[0].value;
6351
}
6352
6353
var attr_id = _via_current_attribute_id;
6354
var attr_type = _via_attribute_being_updated;
6355
var attr_input_type = _via_attributes[attr_type][attr_id].type;
6356
var attr_desc = _via_attributes[attr_type][attr_id].description;
6357
6358
attribute_property_add_input_property('Name of attribute (appears in exported annotations)',
6359
'Name',
6360
attr_id,
6361
'attribute_name');
6362
attribute_property_add_input_property('Description of attribute (shown to user during annotation session)',
6363
'Desc.',
6364
attr_desc,
6365
'attribute_description');
6366
6367
if ( attr_input_type === 'text' ) {
6368
var attr_default_value = _via_attributes[attr_type][attr_id].default_value;
6369
attribute_property_add_input_property('Default value of this attribute',
6370
'Def.',
6371
attr_default_value,
6372
'attribute_default_value');
6373
}
6374
6375
// add dropdown for type of attribute
6376
var p = document.createElement('div');
6377
p.setAttribute('class', 'property');
6378
var c0 = document.createElement('span');
6379
c0.setAttribute('title', 'Attribute type (e.g. text, checkbox, radio, etc)');
6380
c0.innerHTML = 'Type';
6381
var c1 = document.createElement('span');
6382
var c1b = document.createElement('select');
6383
c1b.setAttribute('onchange', 'attribute_property_on_update(this)');
6384
c1b.setAttribute('id', 'attribute_type');
6385
var type_id;
6386
for ( type_id in VIA_ATTRIBUTE_TYPE ) {
6387
var type = VIA_ATTRIBUTE_TYPE[type_id];
6388
var option = document.createElement('option');
6389
option.setAttribute('value', type);
6390
option.innerHTML = type;
6391
if ( attr_input_type == type ) {
6392
option.setAttribute('selected', 'selected');
6393
}
6394
c1b.appendChild(option);
6395
}
6396
c1.appendChild(c1b);
6397
p.appendChild(c0);
6398
p.appendChild(c1);
6399
document.getElementById('attribute_properties').appendChild(p);
6400
}
6401
6402
function show_attribute_options() {
6403
var attr_list = document.getElementById('attributes_name_list');
6404
document.getElementById('attribute_options').innerHTML = '';
6405
if ( attr_list.options.length === 0 ) {
6406
return;
6407
}
6408
6409
var attr_id = attr_list.value;
6410
var attr_type = _via_attributes[_via_attribute_being_updated][attr_id].type;
6411
6412
// populate additional options based on attribute type
6413
switch( attr_type ) {
6414
case VIA_ATTRIBUTE_TYPE.TEXT:
6415
// text does not have any additional properties
6416
break;
6417
case VIA_ATTRIBUTE_TYPE.IMAGE:
6418
var p = document.createElement('div');
6419
p.setAttribute('class', 'property');
6420
p.setAttribute('style', 'text-align:center');
6421
var c0 = document.createElement('span');
6422
c0.setAttribute('style', 'width:25%');
6423
c0.setAttribute('title', 'When selected, this is the value that appears in exported annotations');
6424
c0.innerHTML = 'id';
6425
var c1 = document.createElement('span');
6426
c1.setAttribute('style', 'width:60%');
6427
c1.setAttribute('title', 'URL or base64 (see https://www.base64-image.de/) encoded image data that corresponds to the image shown as an option to the annotator');
6428
c1.innerHTML = 'image url or b64';
6429
var c2 = document.createElement('span');
6430
c2.setAttribute('title', 'The default value of this attribute');
6431
c2.innerHTML = 'def.';
6432
p.appendChild(c0);
6433
p.appendChild(c1);
6434
p.appendChild(c2);
6435
document.getElementById('attribute_options').appendChild(p);
6436
6437
var options = _via_attributes[_via_attribute_being_updated][attr_id].options;
6438
var option_id;
6439
for ( option_id in options ) {
6440
var option_desc = options[option_id];
6441
6442
var option_default = _via_attributes[_via_attribute_being_updated][attr_id].default_options[option_id];
6443
attribute_property_add_option(attr_id, option_id, option_desc, option_default, attr_type);
6444
}
6445
attribute_property_add_new_entry_option(attr_id, attr_type);
6446
break;
6447
case VIA_ATTRIBUTE_TYPE.CHECKBOX: // handled by next case
6448
case VIA_ATTRIBUTE_TYPE.DROPDOWN: // handled by next case
6449
case VIA_ATTRIBUTE_TYPE.RADIO:
6450
var p = document.createElement('div');
6451
p.setAttribute('class', 'property');
6452
p.setAttribute('style', 'text-align:center');
6453
var c0 = document.createElement('span');
6454
c0.setAttribute('style', 'width:25%');
6455
c0.setAttribute('title', 'When selected, this is the value that appears in exported annotations');
6456
c0.innerHTML = 'id';
6457
var c1 = document.createElement('span');
6458
c1.setAttribute('style', 'width:60%');
6459
c1.setAttribute('title', 'This is the text shown as an option to the annotator');
6460
c1.innerHTML = 'description';
6461
var c2 = document.createElement('span');
6462
c2.setAttribute('title', 'The default value of this attribute');
6463
c2.innerHTML = 'def.';
6464
p.appendChild(c0);
6465
p.appendChild(c1);
6466
p.appendChild(c2);
6467
document.getElementById('attribute_options').appendChild(p);
6468
6469
var options = _via_attributes[_via_attribute_being_updated][attr_id].options;
6470
var option_id;
6471
for ( option_id in options ) {
6472
var option_desc = options[option_id];
6473
6474
var option_default = _via_attributes[_via_attribute_being_updated][attr_id].default_options[option_id];
6475
attribute_property_add_option(attr_id, option_id, option_desc, option_default, attr_type);
6476
}
6477
attribute_property_add_new_entry_option(attr_id, attr_type);
6478
break;
6479
default:
6480
console.log('Attribute type ' + attr_type + ' is unavailable');
6481
}
6482
}
6483
6484
function attribute_property_add_input_property(title, name, value, id) {
6485
var p = document.createElement('div');
6486
p.setAttribute('class', 'property');
6487
var c0 = document.createElement('span');
6488
c0.setAttribute('title', title);
6489
c0.innerHTML = name;
6490
var c1 = document.createElement('span');
6491
var c1b = document.createElement('input');
6492
c1b.setAttribute('onchange', 'attribute_property_on_update(this)');
6493
if ( typeof(value) !== 'undefined' ) {
6494
c1b.setAttribute('value', value);
6495
}
6496
c1b.setAttribute('id', id);
6497
c1.appendChild(c1b);
6498
p.appendChild(c0);
6499
p.appendChild(c1);
6500
6501
document.getElementById('attribute_properties').appendChild(p);
6502
}
6503
6504
function attribute_property_add_option(attr_id, option_id, option_desc, option_default, attribute_type) {
6505
var p = document.createElement('div');
6506
p.setAttribute('class', 'property');
6507
var c0 = document.createElement('span');
6508
var c0b = document.createElement('input');
6509
c0b.setAttribute('type', 'text');
6510
c0b.setAttribute('value', option_id);
6511
c0b.setAttribute('title', option_id);
6512
c0b.setAttribute('onchange', 'attribute_property_on_option_update(this)');
6513
c0b.setAttribute('id', '_via_attribute_option_id_' + option_id);
6514
6515
var c1 = document.createElement('span');
6516
var c1b = document.createElement('input');
6517
c1b.setAttribute('type', 'text');
6518
6519
if ( attribute_type === VIA_ATTRIBUTE_TYPE.IMAGE ) {
6520
var option_desc_info = option_desc.length + ' bytes of base64 image data';
6521
c1b.setAttribute('value', option_desc_info);
6522
c1b.setAttribute('title', 'To update, copy and paste base64 image data in this text box');
6523
} else {
6524
c1b.setAttribute('value', option_desc);
6525
c1b.setAttribute('title', option_desc);
6526
}
6527
c1b.setAttribute('onchange', 'attribute_property_on_option_update(this)');
6528
c1b.setAttribute('id', '_via_attribute_option_description_' + option_id);
6529
6530
var c2 = document.createElement('span');
6531
var c2b = document.createElement('input');
6532
c2b.setAttribute('type', attribute_type);
6533
if ( typeof option_default !== 'undefined' ) {
6534
c2b.checked = option_default;
6535
}
6536
if ( attribute_type === 'radio' || attribute_type === 'image' || attribute_type === 'dropdown' ) {
6537
// ensured that user can activate only one radio button
6538
c2b.setAttribute('type', 'radio');
6539
c2b.setAttribute('name', attr_id);
6540
}
6541
6542
c2b.setAttribute('onchange', 'attribute_property_on_option_update(this)');
6543
c2b.setAttribute('id', '_via_attribute_option_default_' + option_id);
6544
6545
c0.appendChild(c0b);
6546
c1.appendChild(c1b);
6547
c2.appendChild(c2b);
6548
p.appendChild(c0);
6549
p.appendChild(c1);
6550
p.appendChild(c2);
6551
6552
document.getElementById('attribute_options').appendChild(p);
6553
}
6554
6555
function attribute_property_add_new_entry_option(attr_id, attribute_type) {
6556
var p = document.createElement('div');
6557
p.setAttribute('class', 'new_option_id_entry');
6558
var c0b = document.createElement('input');
6559
c0b.setAttribute('type', 'text');
6560
c0b.setAttribute('onchange', 'attribute_property_on_option_add(this)');
6561
c0b.setAttribute('id', '_via_attribute_new_option_id');
6562
c0b.setAttribute('placeholder', 'Add new option id');
6563
p.appendChild(c0b);
6564
document.getElementById('attribute_options').appendChild(p);
6565
}
6566
6567
function attribute_property_on_update(p) {
6568
var attr_id = get_current_attribute_id();
6569
var attr_type = _via_attribute_being_updated;
6570
var new_attr_type = p.value;
6571
6572
switch(p.id) {
6573
case 'attribute_name':
6574
if ( new_attr_type !== attr_id ) {
6575
Object.defineProperty(_via_attributes[attr_type],
6576
new_attr_type,
6577
Object.getOwnPropertyDescriptor(_via_attributes[attr_type], attr_id));
6578
6579
delete _via_attributes[attr_type][attr_id];
6580
update_attributes_update_panel();
6581
annotation_editor_update_content();
6582
}
6583
break;
6584
case 'attribute_description':
6585
_via_attributes[attr_type][attr_id].description = new_attr_type;
6586
update_attributes_update_panel();
6587
annotation_editor_update_content();
6588
break;
6589
case 'attribute_default_value':
6590
_via_attributes[attr_type][attr_id].default_value = new_attr_type;
6591
update_attributes_update_panel();
6592
annotation_editor_update_content();
6593
break;
6594
case 'attribute_type':
6595
var old_attr_type = _via_attributes[attr_type][attr_id].type;
6596
_via_attributes[attr_type][attr_id].type = new_attr_type;
6597
if( new_attr_type === VIA_ATTRIBUTE_TYPE.TEXT ) {
6598
_via_attributes[attr_type][attr_id].default_value = '';
6599
delete _via_attributes[attr_type][attr_id].options;
6600
delete _via_attributes[attr_type][attr_id].default_options;
6601
} else {
6602
// add options entry (if missing)
6603
if ( ! _via_attributes[attr_type][attr_id].hasOwnProperty('options') ) {
6604
_via_attributes[attr_type][attr_id].options = {};
6605
_via_attributes[attr_type][attr_id].default_options = {};
6606
}
6607
if ( _via_attributes[attr_type][attr_id].hasOwnProperty('default_value') ) {
6608
delete _via_attributes[attr_type][attr_id].default_value;
6609
}
6610
6611
// 1. gather all the attribute values in existing metadata
6612
var existing_attr_values = attribute_get_unique_values(attr_type, attr_id);
6613
6614
// 2. for checkbox, radio, dropdown: create options based on existing options and existing values
6615
for(var option_id in _via_attributes[attr_type][attr_id]['options']) {
6616
if( !existing_attr_values.includes(option_id) ) {
6617
_via_attributes[attr_type][attr_id]['options'][option_id] = option_id;
6618
}
6619
}
6620
6621
// update existing metadata to reflect changes in attribute type
6622
// ensure that attribute has only one value
6623
for(var img_id in _via_img_metadata ) {
6624
for(var rindex in _via_img_metadata[img_id]['regions']) {
6625
if(_via_img_metadata[img_id]['regions'][rindex]['region_attributes'].hasOwnProperty(attr_id)) {
6626
if(old_attr_type === VIA_ATTRIBUTE_TYPE.CHECKBOX &&
6627
(new_attr_type === VIA_ATTRIBUTE_TYPE.RADIO ||
6628
new_attr_type === VIA_ATTRIBUTE_TYPE.DROPDOWN) ) {
6629
// add only if checkbox has only single option selected
6630
var sel_option_count = 0;
6631
var sel_option_id;
6632
for(var option_id in _via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id]) {
6633
if(_via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id][option_id]) {
6634
sel_option_count = sel_option_count + 1;
6635
sel_option_id = option_id;
6636
}
6637
}
6638
if(sel_option_count === 1) {
6639
_via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id] = sel_option_id;
6640
} else {
6641
// delete as multiple options cannot be represented as radio or dropdown
6642
delete _via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id];
6643
}
6644
}
6645
if( (old_attr_type === VIA_ATTRIBUTE_TYPE.RADIO ||
6646
old_attr_type === VIA_ATTRIBUTE_TYPE.DROPDOWN) &&
6647
new_attr_type === VIA_ATTRIBUTE_TYPE.CHECKBOX) {
6648
var old_option_id = _via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id];
6649
_via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id] = {};
6650
_via_img_metadata[img_id]['regions'][rindex]['region_attributes'][attr_id][old_option_id] = true;
6651
}
6652
}
6653
}
6654
}
6655
}
6656
show_attribute_properties();
6657
show_attribute_options();
6658
annotation_editor_update_content();
6659
break;
6660
}
6661
}
6662
6663
function attribute_get_unique_values(attr_type, attr_id) {
6664
var values = [];
6665
switch ( attr_type ) {
6666
case 'file':
6667
var img_id, attr_val;
6668
for ( img_id in _via_img_metadata ) {
6669
if ( _via_img_metadata[img_id].file_attributes.hasOwnProperty(attr_id) ) {
6670
attr_val = _via_img_metadata[img_id].file_attributes[attr_id];
6671
if ( ! values.includes(attr_val) ) {
6672
values.push(attr_val);
6673
}
6674
}
6675
}
6676
break;
6677
case 'region':
6678
var img_id, attr_val, i;
6679
for ( img_id in _via_img_metadata ) {
6680
for ( i = 0; i < _via_img_metadata[img_id].regions.length; ++i ) {
6681
if ( _via_img_metadata[img_id].regions[i].region_attributes.hasOwnProperty(attr_id) ) {
6682
attr_val = _via_img_metadata[img_id].regions[i].region_attributes[attr_id];
6683
if( typeof(attr_val) === 'object' ) {
6684
for(var option_id in _via_img_metadata[img_id].regions[i].region_attributes[attr_id]) {
6685
if ( ! values.includes(option_id) ) {
6686
values.push(option_id);
6687
}
6688
}
6689
} else {
6690
if ( ! values.includes(attr_val) ) {
6691
values.push(attr_val);
6692
}
6693
}
6694
}
6695
}
6696
}
6697
break;
6698
default:
6699
break;
6700
}
6701
return values;
6702
}
6703
6704
function attribute_property_on_option_update(p) {
6705
var attr_id = get_current_attribute_id();
6706
if ( p.id.startsWith('_via_attribute_option_id_') ) {
6707
var old_key = p.id.substr( '_via_attribute_option_id_'.length );
6708
var new_key = p.value;
6709
if ( old_key !== new_key ) {
6710
var option_id_test = attribute_property_option_id_is_valid(attr_id, new_key);
6711
if ( option_id_test.is_valid ) {
6712
update_attribute_option_id_with_confirm(_via_attribute_being_updated,
6713
attr_id,
6714
old_key,
6715
new_key);
6716
} else {
6717
p.value = old_key; // restore old value
6718
show_message( option_id_test.message );
6719
show_attribute_properties();
6720
}
6721
return;
6722
}
6723
}
6724
6725
if ( p.id.startsWith('_via_attribute_option_description_') ) {
6726
var key = p.id.substr( '_via_attribute_option_description_'.length );
6727
var old_value = _via_attributes[_via_attribute_being_updated][attr_id].options[key];
6728
var new_value = p.value;
6729
if ( new_value !== old_value ) {
6730
_via_attributes[_via_attribute_being_updated][attr_id].options[key] = new_value;
6731
show_attribute_properties();
6732
annotation_editor_update_content();
6733
}
6734
}
6735
6736
if ( p.id.startsWith('_via_attribute_option_default_') ) {
6737
var new_default_option_id = p.id.substr( '_via_attribute_option_default_'.length );
6738
var old_default_option_id_list = Object.keys(_via_attributes[_via_attribute_being_updated][attr_id].default_options);
6739
6740
if ( old_default_option_id_list.length === 0 ) {
6741
// default set for the first time
6742
_via_attributes[_via_attribute_being_updated][attr_id].default_options[new_default_option_id] = p.checked;
6743
} else {
6744
switch ( _via_attributes[_via_attribute_being_updated][attr_id].type ) {
6745
case 'image': // fallback
6746
case 'dropdown': // fallback
6747
case 'radio': // fallback
6748
// to ensure that only one radio button is selected at a time
6749
_via_attributes[_via_attribute_being_updated][attr_id].default_options = {};
6750
_via_attributes[_via_attribute_being_updated][attr_id].default_options[new_default_option_id] = p.checked;
6751
break;
6752
case 'checkbox':
6753
_via_attributes[_via_attribute_being_updated][attr_id].default_options[new_default_option_id] = p.checked;
6754
break;
6755
}
6756
}
6757
// default option updated
6758
attribute_property_on_option_default_update(_via_attribute_being_updated,
6759
attr_id,
6760
new_default_option_id).then( function() {
6761
show_attribute_properties();
6762
annotation_editor_update_content();
6763
});
6764
}
6765
}
6766
6767
function attribute_property_on_option_default_update(attribute_being_updated, attr_id, new_default_option_id) {
6768
return new Promise( function(ok_callback, err_callback) {
6769
// set all metadata to new_value if:
6770
// - metadata[attr_id] is missing
6771
// - metadata[attr_id] is set to option_old_value
6772
var img_id, attr_value, n, i;
6773
var attr_type = _via_attributes[attribute_being_updated][attr_id].type;
6774
switch( attribute_being_updated ) {
6775
case 'file':
6776
for ( img_id in _via_img_metadata ) {
6777
if ( ! _via_img_metadata[img_id].file_attributes.hasOwnProperty(attr_id) ) {
6778
_via_img_metadata[img_id].file_attributes[attr_id] = new_default_option_id;
6779
}
6780
}
6781
break;
6782
case 'region':
6783
for ( img_id in _via_img_metadata ) {
6784
n = _via_img_metadata[img_id].regions.length;
6785
for ( i = 0; i < n; ++i ) {
6786
if ( ! _via_img_metadata[img_id].regions[i].region_attributes.hasOwnProperty(attr_id) ) {
6787
_via_img_metadata[img_id].regions[i].region_attributes[attr_id] = new_default_option_id;
6788
}
6789
}
6790
}
6791
break;
6792
}
6793
ok_callback();
6794
});
6795
}
6796
6797
function attribute_property_on_option_add(p) {
6798
if ( p.value === '' || p.value === null ) {
6799
return;
6800
}
6801
6802
if ( p.id === '_via_attribute_new_option_id' ) {
6803
var attr_id = get_current_attribute_id();
6804
var option_id = p.value;
6805
var option_id_test = attribute_property_option_id_is_valid(attr_id, option_id);
6806
if ( option_id_test.is_valid ) {
6807
_via_attributes[_via_attribute_being_updated][attr_id].options[option_id] = '';
6808
show_attribute_options();
6809
annotation_editor_update_content();
6810
} else {
6811
show_message( option_id_test.message );
6812
attribute_property_reset_new_entry_inputs();
6813
}
6814
}
6815
}
6816
6817
function attribute_property_reset_new_entry_inputs() {
6818
var container = document.getElementById('attribute_options');
6819
var p = container.lastChild;
6820
if ( p.childNodes[0] ) {
6821
p.childNodes[0].value = '';
6822
}
6823
if ( p.childNodes[1] ) {
6824
p.childNodes[1].value = '';
6825
}
6826
}
6827
6828
function attribute_property_show_new_entry_inputs(attr_id, attribute_type) {
6829
var n0 = document.createElement('div');
6830
n0.classList.add('property');
6831
var n1a = document.createElement('span');
6832
var n1b = document.createElement('input');
6833
n1b.setAttribute('onchange', 'attribute_property_on_option_add(this)');
6834
n1b.setAttribute('placeholder', 'Add new id');
6835
n1b.setAttribute('value', '');
6836
n1b.setAttribute('id', '_via_attribute_new_option_id');
6837
n1a.appendChild(n1b);
6838
6839
var n2a = document.createElement('span');
6840
var n2b = document.createElement('input');
6841
n2b.setAttribute('onchange', 'attribute_property_on_option_add(this)');
6842
n2b.setAttribute('placeholder', 'Optional description');
6843
n2b.setAttribute('value', '');
6844
n2b.setAttribute('id', '_via_attribute_new_option_description');
6845
n2a.appendChild(n2b);
6846
6847
var n3a = document.createElement('span');
6848
var n3b = document.createElement('input');
6849
n3b.setAttribute('type', attribute_type);
6850
if ( attribute_type === 'radio' ) {
6851
n3b.setAttribute('name', attr_id);
6852
}
6853
n3b.setAttribute('onchange', 'attribute_property_on_option_add(this)');
6854
n3b.setAttribute('id', '_via_attribute_new_option_default');
6855
n3a.appendChild(n3b);
6856
6857
n0.appendChild(n1a);
6858
n0.appendChild(n2a);
6859
n0.appendChild(n3a);
6860
6861
var container = document.getElementById('attribute_options');
6862
container.appendChild(n0);
6863
}
6864
6865
function attribute_property_option_id_is_valid(attr_id, new_option_id) {
6866
var option_id;
6867
for ( option_id in _via_attributes[_via_attribute_being_updated][attr_id].options ) {
6868
if ( option_id === new_option_id ) {
6869
return { 'is_valid':false, 'message':'Option id [' + attr_id + '] already exists' };
6870
}
6871
}
6872
6873
if ( new_option_id.includes('__') ) { // reserved separator for attribute-id, row-id, option-id
6874
return {'is_valid':false, 'message':'Option id cannot contain two consecutive underscores'};
6875
}
6876
6877
return {'is_valid':true};
6878
}
6879
6880
function attribute_property_id_exists(name) {
6881
var attr_name;
6882
for ( attr_name in _via_attributes[_via_attribute_being_updated] ) {
6883
if ( attr_name === name ) {
6884
return true;
6885
}
6886
}
6887
return false;
6888
}
6889
6890
function delete_existing_attribute_with_confirm() {
6891
var attr_id = document.getElementById('user_input_attribute_id').value;
6892
if ( attr_id === '' ) {
6893
show_message('Enter the name of attribute that you wish to delete');
6894
return;
6895
}
6896
if ( attribute_property_id_exists(attr_id) ) {
6897
var config = {'title':'Delete ' + _via_attribute_being_updated + ' attribute [' + attr_id + ']',
6898
'warning': 'Warning: Deleting an attribute will lead to the attribute being deleted in all the annotations. Please click OK only if you are sure.'};
6899
var input = { 'attr_type':{'type':'text', 'name':'Attribute Type', 'value':_via_attribute_being_updated, 'disabled':true},
6900
'attr_id':{'type':'text', 'name':'Attribute Id', 'value':attr_id, 'disabled':true}
6901
};
6902
invoke_with_user_inputs(delete_existing_attribute_confirmed, input, config);
6903
} else {
6904
show_message('Attribute [' + attr_id + '] does not exist!');
6905
return;
6906
}
6907
}
6908
6909
function delete_existing_attribute_confirmed(input) {
6910
var attr_type = input.attr_type.value;
6911
var attr_id = input.attr_id.value;
6912
delete_existing_attribute(attr_type, attr_id);
6913
document.getElementById('user_input_attribute_id').value = '';
6914
show_message('Deleted ' + attr_type + ' attribute [' + attr_id + ']');
6915
user_input_default_cancel_handler();
6916
}
6917
6918
function delete_existing_attribute(attribute_type, attr_id) {
6919
if ( _via_attributes[attribute_type].hasOwnProperty( attr_id ) ) {
6920
var attr_id_list = Object.keys(_via_attributes[attribute_type]);
6921
if ( attr_id_list.length === 1 ) {
6922
_via_current_attribute_id = '';
6923
} else {
6924
var current_index = attr_id_list.indexOf(attr_id);
6925
var next_index = current_index + 1;
6926
if ( next_index === attr_id_list.length ) {
6927
next_index = current_index - 1;
6928
}
6929
_via_current_attribute_id = attr_id_list[next_index];
6930
}
6931
delete _via_attributes[attribute_type][attr_id];
6932
delete_region_attribute_in_all_metadata(attr_id);
6933
update_attributes_update_panel();
6934
annotation_editor_update_content();
6935
}
6936
}
6937
6938
function add_new_attribute_from_user_input() {
6939
var attr_id = document.getElementById('user_input_attribute_id').value;
6940
if ( attr_id === '' ) {
6941
show_message('Enter the name of attribute that you wish to delete');
6942
return;
6943
}
6944
6945
if ( attribute_property_id_exists(attr_id) ) {
6946
show_message('The ' + _via_attribute_being_updated + ' attribute [' + attr_id + '] already exists.');
6947
} else {
6948
_via_current_attribute_id = attr_id;
6949
add_new_attribute(attr_id);
6950
update_attributes_update_panel();
6951
annotation_editor_update_content();
6952
show_message('Added ' + _via_attribute_being_updated + ' attribute [' + attr_id + '].');
6953
}
6954
}
6955
6956
function add_new_attribute(attribute_id) {
6957
_via_attributes[_via_attribute_being_updated][attribute_id] = {};
6958
_via_attributes[_via_attribute_being_updated][attribute_id].type = 'text';
6959
_via_attributes[_via_attribute_being_updated][attribute_id].description = '';
6960
_via_attributes[_via_attribute_being_updated][attribute_id].default_value = '';
6961
}
6962
6963
function update_current_attribute_id(p) {
6964
_via_current_attribute_id = p.options[p.selectedIndex].value;
6965
update_attribute_properties_panel();
6966
}
6967
6968
function get_current_attribute_id() {
6969
return document.getElementById('attributes_name_list').value;
6970
}
6971
6972
function update_attribute_option_id_with_confirm(attr_type, attr_id, option_id, new_option_id) {
6973
var is_delete = false;
6974
var config;
6975
if ( new_option_id === '' || typeof(new_option_id) === 'undefined' ) {
6976
// an empty new_option_id indicates deletion of option_id
6977
config = {'title':'Delete an option for ' + attr_type + ' attribute'};
6978
is_delete = true;
6979
} else {
6980
config = {'title':'Rename an option for ' + attr_type + ' attribute'};
6981
}
6982
6983
var input = { 'attr_type':{'type':'text', 'name':'Attribute Type', 'value':attr_type, 'disabled':true},
6984
'attr_id':{'type':'text', 'name':'Attribute Id', 'value':attr_id, 'disabled':true}
6985
};
6986
6987
if ( is_delete ) {
6988
input['option_id'] = {'type':'text', 'name':'Attribute Option', 'value':option_id, 'disabled':true};
6989
} else {
6990
input['option_id'] = {'type':'text', 'name':'Attribute Option (old)', 'value':option_id, 'disabled':true},
6991
input['new_option_id'] = {'type':'text', 'name':'Attribute Option (new)', 'value':new_option_id, 'disabled':true};
6992
}
6993
6994
invoke_with_user_inputs(update_attribute_option_id_confirmed, input, config, update_attribute_option_id_cancel);
6995
}
6996
6997
function update_attribute_option_id_cancel(input) {
6998
update_attribute_properties_panel();
6999
}
7000
7001
function update_attribute_option_id_confirmed(input) {
7002
var attr_type = input.attr_type.value;
7003
var attr_id = input.attr_id.value;
7004
var option_id = input.option_id.value;
7005
var is_delete;
7006
var new_option_id;
7007
if ( typeof(input.new_option_id) === 'undefined' || input.new_option_id === '' ) {
7008
is_delete = true;
7009
new_option_id = '';
7010
} else {
7011
is_delete = false;
7012
new_option_id = input.new_option_id.value;
7013
}
7014
7015
update_attribute_option(is_delete, attr_type, attr_id, option_id, new_option_id);
7016
7017
if ( is_delete ) {
7018
show_message('Deleted option [' + option_id + '] for ' + attr_type + ' attribute [' + attr_id + '].');
7019
} else {
7020
show_message('Renamed option [' + option_id + '] to [' + new_option_id + '] for ' + attr_type + ' attribute [' + attr_id + '].');
7021
}
7022
update_attribute_properties_panel();
7023
annotation_editor_update_content();
7024
user_input_default_cancel_handler();
7025
}
7026
7027
function update_attribute_option(is_delete, attr_type, attr_id, option_id, new_option_id) {
7028
switch ( attr_type ) {
7029
case 'region':
7030
update_region_attribute_option_in_all_metadata(is_delete, attr_id, option_id, new_option_id);
7031
if ( ! is_delete ) {
7032
Object.defineProperty(_via_attributes[attr_type][attr_id].options,
7033
new_option_id,
7034
Object.getOwnPropertyDescriptor(_via_attributes[_via_attribute_being_updated][attr_id].options, option_id));
7035
}
7036
delete _via_attributes['region'][attr_id].options[option_id];
7037
7038
break;
7039
case 'file':
7040
update_file_attribute_option_in_all_metadata(attr_id, option_id);
7041
if ( ! is_delete ) {
7042
Object.defineProperty(_via_attributes[attr_type][attr_id].options,
7043
new_option_id,
7044
Object.getOwnPropertyDescriptor(_via_attributes[_via_attribute_being_updated][attr_id].options, option_id));
7045
}
7046
7047
delete _via_attributes['file'][attr_id].options[option_id];
7048
break;
7049
}
7050
}
7051
7052
function update_file_attribute_option_in_all_metadata(is_delete, attr_id, option_id, new_option_id) {
7053
var image_id;
7054
for ( image_id in _via_img_metadata ) {
7055
if ( _via_img_metadata[image_id].file_attributes.hasOwnProperty(attr_id) ) {
7056
if ( _via_img_metadata[image_id].file_attributes[attr_id].hasOwnProperty(option_id) ) {
7057
Object.defineProperty(_via_img_metadata[image_id].file_attributes[attr_id],
7058
new_option_id,
7059
Object.getOwnPropertyDescriptor(_via_img_metadata[image_id].file_attributes[attr_id], option_id));
7060
delete _via_img_metadata[image_id].file_attributes[attr_id][option_id];
7061
}
7062
}
7063
}
7064
}
7065
7066
function update_region_attribute_option_in_all_metadata(is_delete, attr_id, option_id, new_option_id) {
7067
var image_id;
7068
for ( image_id in _via_img_metadata ) {
7069
for (var i = 0; i < _via_img_metadata[image_id].regions.length; ++i ) {
7070
if ( _via_img_metadata[image_id].regions[i].region_attributes.hasOwnProperty(attr_id) ) {
7071
if ( _via_img_metadata[image_id].regions[i].region_attributes[attr_id].hasOwnProperty(option_id) ) {
7072
Object.defineProperty(_via_img_metadata[image_id].regions[i].region_attributes[attr_id],
7073
new_option_id,
7074
Object.getOwnPropertyDescriptor(_via_img_metadata[image_id].regions[i].region_attributes[attr_id], option_id));
7075
delete _via_img_metadata[image_id].regions[i].region_attributes[attr_id][option_id];
7076
}
7077
}
7078
}
7079
}
7080
}
7081
7082
function delete_region_attribute_in_all_metadata(attr_id) {
7083
var image_id;
7084
for ( image_id in _via_img_metadata ) {
7085
for (var i = 0; i < _via_img_metadata[image_id].regions.length; ++i ) {
7086
if ( _via_img_metadata[image_id].regions[i].region_attributes.hasOwnProperty(attr_id)) {
7087
delete _via_img_metadata[image_id].regions[i].region_attributes[attr_id];
7088
}
7089
}
7090
}
7091
}
7092
7093
function delete_file_attribute_option_from_all_metadata(attr_id, option_id) {
7094
var image_id;
7095
for ( image_id in _via_img_metadata ) {
7096
if ( _via_img_metadata.hasOwnProperty(image_id) ) {
7097
delete_file_attribute_option_from_metadata(image_id, attr_id, option_id);
7098
}
7099
}
7100
}
7101
7102
function delete_file_attribute_option_from_metadata(image_id, attr_id, option_id) {
7103
var i;
7104
if ( _via_img_metadata[image_id].file_attributes.hasOwnProperty(attr_id) ) {
7105
if ( _via_img_metadata[image_id].file_attributes[attr_id].hasOwnProperty(option_id) ) {
7106
delete _via_img_metadata[image_id].file_attributes[attr_id][option_id];
7107
}
7108
}
7109
}
7110
7111
function delete_file_attribute_from_all_metadata(image_id, attr_id) {
7112
var image_id;
7113
for ( image_id in _via_img_metadata ) {
7114
if ( _via_img_metadata.hasOwnProperty(image_id) ) {
7115
if ( _via_img_metadata[image_id].file_attributes.hasOwnProperty(attr_id) ) {
7116
delete _via_img_metadata[image_id].file_attributes[attr_id];
7117
}
7118
}
7119
}
7120
}
7121
7122
//
7123
// invoke a method after receiving inputs from user
7124
//
7125
function invoke_with_user_inputs(ok_handler, input, config, cancel_handler) {
7126
setup_user_input_panel(ok_handler, input, config, cancel_handler);
7127
show_user_input_panel();
7128
}
7129
7130
function setup_user_input_panel(ok_handler, input, config, cancel_handler) {
7131
// create html page with OK and CANCEL button
7132
// when OK is clicked
7133
// - setup input with all the user entered values
7134
// - invoke handler with input
7135
// when CANCEL is clicked
7136
// - invoke user_input_cancel()
7137
_via_user_input_ok_handler = ok_handler;
7138
_via_user_input_cancel_handler = cancel_handler;
7139
_via_user_input_data = input;
7140
7141
var p = document.getElementById('user_input_panel');
7142
var c = document.createElement('div');
7143
c.setAttribute('class', 'content');
7144
var html = [];
7145
html.push('<p class="title">' + config.title + '</p>');
7146
7147
html.push('<div class="user_inputs">');
7148
var key;
7149
for ( key in _via_user_input_data ) {
7150
html.push('<div class="row">');
7151
html.push('<span class="cell">' + _via_user_input_data[key].name + '</span>');
7152
var disabled_html = '';
7153
if ( _via_user_input_data[key].disabled ) {
7154
disabled_html = 'disabled="disabled"';
7155
}
7156
var value_html = '';
7157
if ( _via_user_input_data[key].value ) {
7158
value_html = 'value="' + _via_user_input_data[key].value + '"';
7159
}
7160
7161
switch(_via_user_input_data[key].type) {
7162
case 'checkbox':
7163
if ( _via_user_input_data[key].checked ) {
7164
value_html = 'checked="checked"';
7165
} else {
7166
value_html = '';
7167
}
7168
html.push('<span class="cell">' +
7169
'<input class="_via_user_input_variable" ' +
7170
value_html + ' ' +
7171
disabled_html + ' ' +
7172
'type="checkbox" id="' + key + '"></span>');
7173
break;
7174
case 'text':
7175
var size = '50';
7176
if ( _via_user_input_data[key].size ) {
7177
size = _via_user_input_data[key].size;
7178
}
7179
var placeholder = '';
7180
if ( _via_user_input_data[key].placeholder ) {
7181
placeholder = _via_user_input_data[key].placeholder;
7182
}
7183
html.push('<span class="cell">' +
7184
'<input class="_via_user_input_variable" ' +
7185
value_html + ' ' +
7186
disabled_html + ' ' +
7187
'size="' + size + '" ' +
7188
'placeholder="' + placeholder + '" ' +
7189
'type="text" id="' + key + '"></span>');
7190
7191
break;
7192
case 'textarea':
7193
var rows = '5';
7194
var cols = '50'
7195
if ( _via_user_input_data[key].rows ) {
7196
rows = _via_user_input_data[key].rows;
7197
}
7198
if ( _via_user_input_data[key].cols ) {
7199
cols = _via_user_input_data[key].cols;
7200
}
7201
var placeholder = '';
7202
if ( _via_user_input_data[key].placeholder ) {
7203
placeholder = _via_user_input_data[key].placeholder;
7204
}
7205
html.push('<span class="cell">' +
7206
'<textarea class="_via_user_input_variable" ' +
7207
disabled_html + ' ' +
7208
'rows="' + rows + '" ' +
7209
'cols="' + cols + '" ' +
7210
'placeholder="' + placeholder + '" ' +
7211
'id="' + key + '">' + value_html + '</textarea></span>');
7212
7213
break;
7214
7215
}
7216
html.push('</div>'); // end of row
7217
}
7218
html.push('</div>'); // end of user_input div
7219
// optional warning before confirmation
7220
if (config.hasOwnProperty("warning") ) {
7221
html.push('<div class="warning">' + config.warning + '</div>');
7222
}
7223
html.push('<div class="user_confirm">' +
7224
'<span class="ok">' +
7225
'<button id="user_input_ok_button" onclick="user_input_parse_and_invoke_handler()">&nbsp;OK&nbsp;</button></span>' +
7226
'<span class="cancel">' +
7227
'<button id="user_input_cancel_button" onclick="user_input_cancel_handler()">CANCEL</button></span></div>');
7228
c.innerHTML = html.join('');
7229
p.innerHTML = '';
7230
p.appendChild(c);
7231
7232
}
7233
7234
function user_input_default_cancel_handler() {
7235
hide_user_input_panel();
7236
_via_user_input_data = {};
7237
_via_user_input_ok_handler = null;
7238
_via_user_input_cancel_handler = null;
7239
}
7240
7241
function user_input_cancel_handler() {
7242
if ( _via_user_input_cancel_handler ) {
7243
_via_user_input_cancel_handler();
7244
}
7245
user_input_default_cancel_handler();
7246
}
7247
7248
function user_input_parse_and_invoke_handler() {
7249
var elist = document.getElementsByClassName('_via_user_input_variable');
7250
var i;
7251
for ( i=0; i < elist.length; ++i ) {
7252
var eid = elist[i].id;
7253
if ( _via_user_input_data.hasOwnProperty(eid) ) {
7254
switch(_via_user_input_data[eid].type) {
7255
case 'checkbox':
7256
_via_user_input_data[eid].value = elist[i].checked;
7257
break;
7258
default:
7259
_via_user_input_data[eid].value = elist[i].value;
7260
break;
7261
}
7262
}
7263
}
7264
if ( typeof(_via_user_input_data.confirm) !== 'undefined' ) {
7265
if ( _via_user_input_data.confirm.value ) {
7266
_via_user_input_ok_handler(_via_user_input_data);
7267
} else {
7268
if ( _via_user_input_cancel_handler ) {
7269
_via_user_input_cancel_handler();
7270
}
7271
}
7272
} else {
7273
_via_user_input_ok_handler(_via_user_input_data);
7274
}
7275
user_input_default_cancel_handler();
7276
}
7277
7278
function show_user_input_panel() {
7279
document.getElementById('user_input_panel').style.display = 'block';
7280
}
7281
7282
function hide_user_input_panel() {
7283
document.getElementById('user_input_panel').style.display = 'none';
7284
}
7285
7286
//
7287
// annotations editor panel
7288
//
7289
function annotation_editor_show() {
7290
// remove existing annotation editor (if any)
7291
annotation_editor_remove();
7292
7293
// create new container of annotation editor
7294
var ae = document.createElement('div');
7295
ae.setAttribute('id', 'annotation_editor');
7296
7297
if ( _via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.SINGLE_REGION ) {
7298
if ( _via_settings.ui.image.on_image_annotation_editor_placement === VIA_ANNOTATION_EDITOR_PLACEMENT.DISABLE ) {
7299
return;
7300
}
7301
7302
// only display on-image annotation editor if
7303
// - region attribute are defined
7304
// - region is selected
7305
if ( _via_is_region_selected &&
7306
Object.keys(_via_attributes['region']).length &&
7307
_via_attributes['region'].constructor === Object ) {
7308
ae.classList.add('force_small_font');
7309
ae.classList.add('display_area_content'); // to enable automatic hiding of this content
7310
// add annotation editor to image_panel
7311
if ( _via_settings.ui.image.on_image_annotation_editor_placement === VIA_ANNOTATION_EDITOR_PLACEMENT.NEAR_REGION ) {
7312
var html_position = annotation_editor_get_placement(_via_user_sel_region_id);
7313
ae.style.top = html_position.top;
7314
ae.style.left = html_position.left;
7315
}
7316
_via_display_area.appendChild(ae);
7317
annotation_editor_update_content();
7318
update_vertical_space();
7319
}
7320
} else {
7321
// show annotation editor in a separate panel at the bottom
7322
_via_annotaion_editor_panel.appendChild(ae);
7323
annotation_editor_update_content();
7324
update_vertical_space();
7325
7326
if ( _via_is_region_selected ) {
7327
// highlight entry for region_id in annotation editor panel
7328
annotation_editor_scroll_to_row(_via_user_sel_region_id);
7329
annotation_editor_highlight_row(_via_user_sel_region_id);
7330
}
7331
}
7332
}
7333
7334
function annotation_editor_hide() {
7335
if ( _via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.SINGLE_REGION ) {
7336
// remove existing annotation editor (if any)
7337
annotation_editor_remove();
7338
} else {
7339
annotation_editor_clear_row_highlight();
7340
}
7341
}
7342
7343
function annotation_editor_toggle_on_image_editor() {
7344
if ( _via_settings.ui.image.on_image_annotation_editor_placement === VIA_ANNOTATION_EDITOR_PLACEMENT.DISABLE ) {
7345
_via_annotation_editor_mode = VIA_ANNOTATION_EDITOR_MODE.SINGLE_REGION;
7346
_via_settings.ui.image.on_image_annotation_editor_placement = VIA_ANNOTATION_EDITOR_PLACEMENT.NEAR_REGION;
7347
annotation_editor_show();
7348
show_message('Enabled on image annotation editor');
7349
} else {
7350
_via_settings.ui.image.on_image_annotation_editor_placement = VIA_ANNOTATION_EDITOR_PLACEMENT.DISABLE;
7351
_via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.ALL_REGIONS;
7352
annotation_editor_hide();
7353
show_message('Disabled on image annotation editor');
7354
}
7355
}
7356
7357
function annotation_editor_update_content() {
7358
return new Promise( function(ok_callback, err_callback) {
7359
var ae = document.getElementById('annotation_editor');
7360
if (ae ) {
7361
ae.innerHTML = '';
7362
annotation_editor_update_header_html();
7363
annotation_editor_update_metadata_html();
7364
}
7365
ok_callback();
7366
});
7367
}
7368
7369
function annotation_editor_get_placement(region_id) {
7370
var html_position = {};
7371
var r = _via_canvas_regions[region_id]['shape_attributes'];
7372
var shape = r['name'];
7373
switch( shape ) {
7374
case 'rect':
7375
html_position.top = r['y'] + r['height'];
7376
html_position.left = r['x'] + r['width'];
7377
break;
7378
case 'circle':
7379
html_position.top = r['cy'] + r['r'];
7380
html_position.left = r['cx'];
7381
break;
7382
case 'ellipse':
7383
html_position.top = r['cy'] + r['ry'] * Math.cos(r['theta']);
7384
html_position.left = r['cx'] - r['ry'] * Math.sin(r['theta']);
7385
break;
7386
case 'polygon':
7387
case 'polyline':
7388
var most_left =
7389
Object.keys(r['all_points_x']).reduce(function(a, b){
7390
return r['all_points_x'][a] > r['all_points_x'][b] ? a : b });
7391
html_position.top = Math.max( r['all_points_y'][most_left] );
7392
html_position.left = Math.max( r['all_points_x'][most_left] );
7393
break;
7394
case 'point':
7395
html_position.top = r['cy'];
7396
html_position.left = r['cx'];
7397
break;
7398
}
7399
html_position.top = html_position.top + _via_img_panel.offsetTop + VIA_REGION_EDGE_TOL + 'px';
7400
html_position.left = html_position.left + _via_img_panel.offsetLeft + VIA_REGION_EDGE_TOL + 'px';
7401
return html_position;
7402
}
7403
7404
function annotation_editor_remove() {
7405
var p = document.getElementById('annotation_editor');
7406
if ( p ) {
7407
p.remove();
7408
}
7409
}
7410
7411
function is_annotation_editor_visible() {
7412
return document.getElementById('annotation_editor_panel').classList.contains('display_block');
7413
}
7414
7415
function annotation_editor_toggle_all_regions_editor() {
7416
var p = document.getElementById('annotation_editor_panel');
7417
if ( p.classList.contains('display_block') ) {
7418
p.classList.remove('display_block');
7419
_via_annotation_editor_mode = VIA_ANNOTATION_EDITOR_MODE.SINGLE_REGION;
7420
} else {
7421
_via_annotation_editor_mode = VIA_ANNOTATION_EDITOR_MODE.ALL_REGIONS;
7422
p.classList.add('display_block');
7423
p.style.height = _via_settings.ui.annotation_editor_height + '%';
7424
p.style.fontSize = _via_settings.ui.annotation_editor_fontsize + 'rem';
7425
annotation_editor_show();
7426
}
7427
}
7428
7429
function annotation_editor_set_active_button() {
7430
var attribute_type;
7431
for ( attribute_type in _via_attributes ) {
7432
var bid = 'button_edit_' + attribute_type + '_metadata';
7433
document.getElementById(bid).classList.remove('active');
7434
}
7435
var bid = 'button_edit_' + _via_metadata_being_updated + '_metadata';
7436
document.getElementById(bid).classList.add('active');
7437
}
7438
7439
function edit_region_metadata_in_annotation_editor() {
7440
_via_metadata_being_updated = 'region';
7441
annotation_editor_set_active_button();
7442
annotation_editor_update_content();
7443
}
7444
function edit_file_metadata_in_annotation_editor() {
7445
_via_metadata_being_updated = 'file';
7446
annotation_editor_set_active_button();
7447
annotation_editor_update_content();
7448
}
7449
7450
function annotation_editor_update_header_html() {
7451
var head = document.createElement('div');
7452
head.setAttribute('class', 'row');
7453
head.setAttribute('id', 'annotation_editor_header');
7454
7455
if ( _via_metadata_being_updated === 'region' ) {
7456
var rid_col = document.createElement('span');
7457
rid_col.setAttribute('class', 'col');
7458
rid_col.innerHTML = '';
7459
head.appendChild(rid_col);
7460
}
7461
7462
if ( _via_metadata_being_updated === 'file' ) {
7463
var rid_col = document.createElement('span');
7464
rid_col.setAttribute('class', 'col header');
7465
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
7466
rid_col.innerHTML = 'group';
7467
} else {
7468
rid_col.innerHTML = 'filename';
7469
}
7470
head.appendChild(rid_col);
7471
}
7472
7473
var attr_id;
7474
for ( attr_id in _via_attributes[_via_metadata_being_updated] ) {
7475
var col = document.createElement('span');
7476
col.setAttribute('class', 'col header');
7477
col.innerHTML = attr_id;
7478
head.appendChild(col);
7479
}
7480
7481
var ae = document.getElementById('annotation_editor');
7482
if ( ae.childNodes.length === 0 ) {
7483
ae.appendChild(head);
7484
} else {
7485
if ( ae.firstChild.id === 'annotation_editor_header') {
7486
ae.replaceChild(head, ae.firstChild);
7487
} else {
7488
// header node is absent
7489
ae.insertBefore(head, ae.firstChild);
7490
}
7491
}
7492
}
7493
7494
function annotation_editor_update_metadata_html() {
7495
if ( ! _via_img_count ) {
7496
return;
7497
}
7498
7499
var ae = document.getElementById('annotation_editor');
7500
switch ( _via_metadata_being_updated ) {
7501
case 'region':
7502
var rindex;
7503
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
7504
ae.appendChild( annotation_editor_get_metadata_row_html(0) );
7505
} else {
7506
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE ) {
7507
if ( _via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.SINGLE_REGION ) {
7508
ae.appendChild( annotation_editor_get_metadata_row_html(_via_user_sel_region_id) );
7509
} else {
7510
for ( rindex = 0; rindex < _via_img_metadata[_via_image_id].regions.length; ++rindex ) {
7511
ae.appendChild( annotation_editor_get_metadata_row_html(rindex) );
7512
}
7513
}
7514
}
7515
}
7516
break;
7517
7518
case 'file':
7519
ae.appendChild( annotation_editor_get_metadata_row_html(0) );
7520
break;
7521
}
7522
}
7523
7524
function annotation_editor_update_row(row_id) {
7525
var ae = document.getElementById('annotation_editor');
7526
7527
var new_row = annotation_editor_get_metadata_row_html(row_id);
7528
var old_row = document.getElementById(new_row.getAttribute('id'));
7529
ae.replaceChild(new_row, old_row);
7530
}
7531
7532
function annotation_editor_add_row(row_id) {
7533
if ( is_annotation_editor_visible() ) {
7534
var ae = document.getElementById('annotation_editor');
7535
var new_row = annotation_editor_get_metadata_row_html(row_id);
7536
var penultimate_row_id = parseInt(row_id) - 1;
7537
if ( penultimate_row_id >= 0 ) {
7538
var penultimate_row_html_id = 'ae_' + _via_metadata_being_updated + '_' + penultimate_row_id;
7539
var penultimate_row = document.getElementById(penultimate_row_html_id);
7540
ae.insertBefore(new_row, penultimate_row.nextSibling);
7541
} else {
7542
ae.appendChild(new_row);
7543
}
7544
}
7545
}
7546
7547
function annotation_editor_get_metadata_row_html(row_id) {
7548
var row = document.createElement('div');
7549
row.setAttribute('class', 'row');
7550
row.setAttribute('id', 'ae_' + _via_metadata_being_updated + '_' + row_id);
7551
7552
if ( _via_metadata_being_updated === 'region' ) {
7553
var rid = document.createElement('span');
7554
7555
switch(_via_display_area_content_name) {
7556
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID:
7557
rid.setAttribute('class', 'col');
7558
rid.innerHTML = 'Grouped regions in ' + _via_image_grid_selected_img_index_list.length + ' files';
7559
break;
7560
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE:
7561
rid.setAttribute('class', 'col id');
7562
rid.innerHTML = (row_id + 1);
7563
break;
7564
}
7565
row.appendChild(rid);
7566
}
7567
7568
if ( _via_metadata_being_updated === 'file' ) {
7569
var rid = document.createElement('span');
7570
rid.setAttribute('class', 'col');
7571
switch(_via_display_area_content_name) {
7572
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID:
7573
rid.innerHTML = 'Group of ' + _via_image_grid_selected_img_index_list.length + ' files';
7574
break;
7575
case VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE:
7576
rid.innerHTML = _via_image_filename_list[_via_image_index];
7577
break;
7578
}
7579
7580
row.appendChild(rid);
7581
}
7582
7583
var attr_id;
7584
for ( attr_id in _via_attributes[_via_metadata_being_updated] ) {
7585
var col = document.createElement('span');
7586
col.setAttribute('class', 'col');
7587
7588
var attr_type = _via_attributes[_via_metadata_being_updated][attr_id].type;
7589
var attr_desc = _via_attributes[_via_metadata_being_updated][attr_id].desc;
7590
if ( typeof(attr_desc) === 'undefined' ) {
7591
attr_desc = '';
7592
}
7593
var attr_html_id = attr_id + '__' + row_id;
7594
7595
var attr_value = '';
7596
var attr_placeholder = '';
7597
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE ) {
7598
switch(_via_metadata_being_updated) {
7599
case 'region':
7600
if ( _via_img_metadata[_via_image_id].regions[row_id].region_attributes.hasOwnProperty(attr_id) ) {
7601
attr_value = _via_img_metadata[_via_image_id].regions[row_id].region_attributes[attr_id];
7602
} else {
7603
attr_placeholder = 'not defined yet!';
7604
}
7605
case 'file':
7606
if ( _via_img_metadata[_via_image_id].file_attributes.hasOwnProperty(attr_id) ) {
7607
attr_value = _via_img_metadata[_via_image_id].file_attributes[attr_id];
7608
} else {
7609
attr_placeholder = 'not defined yet!';
7610
}
7611
}
7612
}
7613
7614
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
7615
var attr_metadata_stat;
7616
switch(_via_metadata_being_updated) {
7617
case 'region':
7618
attr_metadata_stat = _via_get_region_metadata_stat(_via_image_grid_selected_img_index_list, attr_id);
7619
break;
7620
case 'file':
7621
attr_metadata_stat = _via_get_file_metadata_stat(_via_image_grid_selected_img_index_list, attr_id);
7622
break;
7623
}
7624
7625
switch ( attr_type ) {
7626
case 'text':
7627
if ( attr_metadata_stat.hasOwnProperty(attr_id) ) {
7628
var attr_value_set = Object.keys(attr_metadata_stat[attr_id]);
7629
if ( attr_value_set.includes('undefined') ) {
7630
attr_value = '';
7631
attr_placeholder = 'includes ' + attr_metadata_stat[attr_id]['undefined'] + ' undefined values';
7632
} else {
7633
switch( attr_value_set.length ) {
7634
case 0:
7635
attr_value = '';
7636
attr_placeholder = 'not applicable';
7637
break;
7638
case 1:
7639
attr_value = attr_value_set[0];
7640
attr_placeholder = '';
7641
break;
7642
default:
7643
attr_value = '';
7644
attr_placeholder = attr_value_set.length + ' different values: ' + JSON.stringify(attr_value_set).replace(/"/g,'\'');
7645
}
7646
}
7647
} else {
7648
attr_value = '';
7649
attr_placeholder = 'not defined yet!';
7650
}
7651
break;
7652
7653
case 'radio': // fallback
7654
case 'dropdown': // fallback
7655
case 'image': // fallback
7656
if ( attr_metadata_stat.hasOwnProperty(attr_id) ) {
7657
var attr_value_set = Object.keys(attr_metadata_stat[attr_id]);
7658
if ( attr_value_set.length === 1 ) {
7659
attr_value = attr_value_set[0];
7660
} else {
7661
attr_value = '';
7662
}
7663
} else {
7664
attr_value = '';
7665
}
7666
break;
7667
7668
case 'checkbox':
7669
attr_value = {};
7670
if ( attr_metadata_stat.hasOwnProperty(attr_id) ) {
7671
var attr_value_set = Object.keys(attr_metadata_stat[attr_id]);
7672
var same_count = true;
7673
var i, n;
7674
var attr_value_curr, attr_value_next;
7675
n = attr_value_set.length;
7676
for ( i = 0; i < n - 1; ++i ) {
7677
attr_value_curr = attr_value_set[i];
7678
attr_value_next = attr_value_set[i+1];
7679
7680
if ( attr_metadata_stat[attr_id][attr_value_curr] !== attr_metadata_stat[attr_id][attr_value_next] ) {
7681
same_count = false;
7682
break;
7683
}
7684
}
7685
if ( same_count ) {
7686
var attr_value_i;
7687
for ( attr_value_i in attr_metadata_stat[attr_id] ) {
7688
attr_value[attr_value_i] = true;
7689
}
7690
}
7691
}
7692
break;
7693
}
7694
}
7695
7696
switch(attr_type) {
7697
case 'text':
7698
col.innerHTML = '<textarea ' +
7699
'onchange="annotation_editor_on_metadata_update(this)" ' +
7700
'onfocus="annotation_editor_on_metadata_focus(this)" ' +
7701
'title="' + attr_desc + '" ' +
7702
'placeholder="' + attr_placeholder + '" ' +
7703
'id="' + attr_html_id + '">' + attr_value + '</textarea>';
7704
break;
7705
case 'checkbox':
7706
var options = _via_attributes[_via_metadata_being_updated][attr_id].options;
7707
var option_id;
7708
for ( option_id in options ) {
7709
var option_html_id = attr_html_id + '__' + option_id;
7710
var option = document.createElement('input');
7711
option.setAttribute('type', 'checkbox');
7712
option.setAttribute('value', option_id);
7713
option.setAttribute('id', option_html_id);
7714
option.setAttribute('onfocus', 'annotation_editor_on_metadata_focus(this)');
7715
option.setAttribute('onchange', 'annotation_editor_on_metadata_update(this)');
7716
7717
var option_desc = _via_attributes[_via_metadata_being_updated][attr_id].options[option_id];
7718
if ( option_desc === '' || typeof(option_desc) === 'undefined' ) {
7719
// option description is optional, use option_id when description is not present
7720
option_desc = option_id;
7721
}
7722
7723
// set the value of options based on the user annotations
7724
if ( typeof attr_value !== 'undefined') {
7725
if ( attr_value.hasOwnProperty(option_id) ) {
7726
option.checked = attr_value[option_id];
7727
}
7728
}
7729
7730
var label = document.createElement('label');
7731
label.setAttribute('for', option_html_id);
7732
label.innerHTML = option_desc;
7733
7734
var container = document.createElement('span');
7735
container.appendChild(option);
7736
container.appendChild(label);
7737
col.appendChild(container);
7738
}
7739
break;
7740
case 'radio':
7741
var option_id;
7742
for ( option_id in _via_attributes[_via_metadata_being_updated][attr_id].options ) {
7743
var option_html_id = attr_html_id + '__' + option_id;
7744
var option = document.createElement('input');
7745
option.setAttribute('type', 'radio');
7746
option.setAttribute('name', attr_html_id);
7747
option.setAttribute('value', option_id);
7748
option.setAttribute('id', option_html_id);
7749
option.setAttribute('onfocus', 'annotation_editor_on_metadata_focus(this)');
7750
option.setAttribute('onchange', 'annotation_editor_on_metadata_update(this)');
7751
7752
var option_desc = _via_attributes[_via_metadata_being_updated][attr_id].options[option_id];
7753
if ( option_desc === '' || typeof(option_desc) === 'undefined' ) {
7754
// option description is optional, use option_id when description is not present
7755
option_desc = option_id;
7756
}
7757
7758
if ( attr_value === option_id ) {
7759
option.checked = true;
7760
}
7761
7762
var label = document.createElement('label');
7763
label.setAttribute('for', option_html_id);
7764
label.innerHTML = option_desc;
7765
7766
var container = document.createElement('span');
7767
container.appendChild(option);
7768
container.appendChild(label);
7769
col.appendChild(container);
7770
}
7771
break;
7772
case 'image':
7773
var option_id;
7774
var option_count = 0;
7775
for ( option_id in _via_attributes[_via_metadata_being_updated][attr_id].options ) {
7776
option_count = option_count + 1;
7777
}
7778
var img_options = document.createElement('div');
7779
img_options.setAttribute('class', 'img_options');
7780
col.appendChild(img_options);
7781
7782
var option_index = 0;
7783
for ( option_id in _via_attributes[_via_metadata_being_updated][attr_id].options ) {
7784
var option_html_id = attr_html_id + '__' + option_id;
7785
var option = document.createElement('input');
7786
option.setAttribute('type', 'radio');
7787
option.setAttribute('name', attr_html_id);
7788
option.setAttribute('value', option_id);
7789
option.setAttribute('id', option_html_id);
7790
option.setAttribute('onfocus', 'annotation_editor_on_metadata_focus(this)');
7791
option.setAttribute('onchange', 'annotation_editor_on_metadata_update(this)');
7792
7793
var option_desc = _via_attributes[_via_metadata_being_updated][attr_id].options[option_id];
7794
if ( option_desc === '' || typeof(option_desc) === 'undefined' ) {
7795
// option description is optional, use option_id when description is not present
7796
option_desc = option_id;
7797
}
7798
7799
if ( attr_value === option_id ) {
7800
option.checked = true;
7801
}
7802
7803
var label = document.createElement('label');
7804
label.setAttribute('for', option_html_id);
7805
label.innerHTML = '<img src="' + option_desc + '"><p>' + option_id + '</p>';
7806
7807
var container = document.createElement('span');
7808
container.appendChild(option);
7809
container.appendChild(label);
7810
img_options.appendChild(container);
7811
}
7812
break;
7813
7814
case 'dropdown':
7815
var sel = document.createElement('select');
7816
sel.setAttribute('id', attr_html_id);
7817
sel.setAttribute('onfocus', 'annotation_editor_on_metadata_focus(this)');
7818
sel.setAttribute('onchange', 'annotation_editor_on_metadata_update(this)');
7819
var option_id;
7820
var option_selected = false;
7821
for ( option_id in _via_attributes[_via_metadata_being_updated][attr_id].options ) {
7822
var option_html_id = attr_html_id + '__' + option_id;
7823
var option = document.createElement('option');
7824
option.setAttribute('value', option_id);
7825
7826
var option_desc = _via_attributes[_via_metadata_being_updated][attr_id].options[option_id];
7827
if ( option_desc === '' || typeof(option_desc) === 'undefined' ) {
7828
// option description is optional, use option_id when description is not present
7829
option_desc = option_id;
7830
}
7831
7832
if ( option_id === attr_value ) {
7833
option.setAttribute('selected', 'selected');
7834
option_selected = true;
7835
}
7836
option.innerHTML = option_desc;
7837
sel.appendChild(option);
7838
}
7839
7840
if ( ! option_selected ) {
7841
sel.selectedIndex = '-1';
7842
}
7843
col.appendChild(sel);
7844
break;
7845
}
7846
7847
row.appendChild(col);
7848
}
7849
return row;
7850
}
7851
7852
function annotation_editor_scroll_to_row(row_id) {
7853
if ( is_annotation_editor_visible() ) {
7854
var row_html_id = 'ae_' + _via_metadata_being_updated + '_' + row_id;
7855
var row = document.getElementById(row_html_id);
7856
row.scrollIntoView(false);
7857
}
7858
}
7859
7860
function annotation_editor_highlight_row(row_id) {
7861
if ( is_annotation_editor_visible() ) {
7862
var row_html_id = 'ae_' + _via_metadata_being_updated + '_' + row_id;
7863
var row = document.getElementById(row_html_id);
7864
row.classList.add('highlight');
7865
}
7866
}
7867
7868
function annotation_editor_clear_row_highlight() {
7869
if ( is_annotation_editor_visible() ) {
7870
var ae = document.getElementById('annotation_editor');
7871
var i;
7872
for ( i=0; i<ae.childNodes.length; ++i ) {
7873
ae.childNodes[i].classList.remove('highlight');
7874
}
7875
}
7876
}
7877
7878
function annotation_editor_extract_html_id_components(html_id) {
7879
// html_id : attribute_name__row-id__option_id
7880
var parts = html_id.split('__');
7881
var parsed_id = {};
7882
switch( parts.length ) {
7883
case 3:
7884
// html_id : attribute-id__row-id__option_id
7885
parsed_id.attr_id = parts[0];
7886
parsed_id.row_id = parts[1];
7887
parsed_id.option_id = parts[2];
7888
break;
7889
case 2:
7890
// html_id : attribute-id__row-id
7891
parsed_id.attr_id = parts[0];
7892
parsed_id.row_id = parts[1];
7893
break;
7894
default:
7895
}
7896
return parsed_id;
7897
}
7898
7899
function _via_get_file_metadata_stat(img_index_list, attr_id) {
7900
var stat = {};
7901
stat[attr_id] = {};
7902
var i, n, img_id, img_index, value;
7903
n = img_index_list.length;
7904
for ( i = 0; i < n; ++i ) {
7905
img_index = img_index_list[i];
7906
img_id = _via_image_id_list[img_index];
7907
if ( _via_img_metadata[img_id].file_attributes.hasOwnProperty(attr_id) ) {
7908
value = _via_img_metadata[img_id].file_attributes[attr_id];
7909
if ( typeof(value) === 'object' ) {
7910
// checkbox has multiple values and hence is object
7911
var key;
7912
for ( key in value ) {
7913
if ( stat[attr_id].hasOwnProperty(key) ) {
7914
stat[attr_id][key] += 1;
7915
} else {
7916
stat[attr_id][key] = 1;
7917
}
7918
}
7919
} else {
7920
if ( stat[attr_id].hasOwnProperty(value) ) {
7921
stat[attr_id][value] += 1;
7922
} else {
7923
stat[attr_id][value] = 1;
7924
}
7925
}
7926
}
7927
7928
}
7929
return stat;
7930
}
7931
7932
function _via_get_region_metadata_stat(img_index_list, attr_id) {
7933
var stat = {};
7934
stat[attr_id] = {};
7935
var i, n, img_id, img_index, value;
7936
var j, m;
7937
n = img_index_list.length;
7938
for ( i = 0; i < n; ++i ) {
7939
img_index = img_index_list[i];
7940
img_id = _via_image_id_list[img_index];
7941
m = _via_img_metadata[img_id].regions.length;
7942
for ( j = 0; j < m; ++j ) {
7943
if ( ! image_grid_is_region_in_current_group( _via_img_metadata[img_id].regions[j].region_attributes ) ) {
7944
// skip region not in current group
7945
continue;
7946
}
7947
7948
value = _via_img_metadata[img_id].regions[j].region_attributes[attr_id];
7949
if ( typeof(value) === 'object' ) {
7950
// checkbox has multiple values and hence is object
7951
var key;
7952
for ( key in value ) {
7953
if ( stat[attr_id].hasOwnProperty(key) ) {
7954
stat[attr_id][key] += 1;
7955
} else {
7956
stat[attr_id][key] = 1;
7957
}
7958
}
7959
} else {
7960
if ( stat[attr_id].hasOwnProperty(value) ) {
7961
stat[attr_id][value] += 1;
7962
} else {
7963
stat[attr_id][value] = 1;
7964
}
7965
}
7966
}
7967
}
7968
return stat;
7969
}
7970
7971
// invoked when the input entry in annotation editor receives focus
7972
function annotation_editor_on_metadata_focus(p) {
7973
if ( _via_annotation_editor_mode === VIA_ANNOTATION_EDITOR_MODE.ALL_REGIONS ) {
7974
var pid = annotation_editor_extract_html_id_components(p.id);
7975
var region_id = pid.row_id;
7976
// clear existing highlights (if any)
7977
toggle_all_regions_selection(false);
7978
annotation_editor_clear_row_highlight();
7979
// set new selection highlights
7980
set_region_select_state(region_id, true);
7981
annotation_editor_scroll_to_row(region_id);
7982
annotation_editor_highlight_row(region_id);
7983
7984
_via_redraw_reg_canvas();
7985
}
7986
}
7987
7988
// invoked when the user updates annotations using the annotation editor
7989
function annotation_editor_on_metadata_update(p) {
7990
var pid = annotation_editor_extract_html_id_components(p.id);
7991
var img_id = _via_image_id;
7992
7993
var img_index_list = [ _via_image_index ];
7994
var region_id = pid.row_id;
7995
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
7996
img_index_list = _via_image_grid_selected_img_index_list.slice(0);
7997
region_id = -1; // this flag denotes that we want to update all regions
7998
}
7999
8000
if ( _via_metadata_being_updated === 'file' ) {
8001
annotation_editor_update_file_metadata(img_index_list, pid.attr_id, p.value, p.checked).then( function(update_count) {
8002
annotation_editor_on_metadata_update_done('file', pid.attr_id, update_count);
8003
}, function(err) {
8004
console.log(err)
8005
show_message('Failed to update file attributes! ' + err);
8006
});
8007
return;
8008
}
8009
8010
if ( _via_metadata_being_updated === 'region' ) {
8011
annotation_editor_update_region_metadata(img_index_list, region_id, pid.attr_id, p.value, p.checked).then( function(update_count) {
8012
annotation_editor_on_metadata_update_done('region', pid.attr_id, update_count);
8013
}, function(err) {
8014
show_message('Failed to update region attributes! ');
8015
});
8016
return;
8017
}
8018
}
8019
8020
function annotation_editor_on_metadata_update_done(type, attr_id, update_count) {
8021
show_message('Updated ' + type + ' attributes of ' + update_count + ' ' + type + 's');
8022
// check if the updated attribute is one of the group variables
8023
var i, n, type, attr_id;
8024
n = _via_image_grid_group_var.length;
8025
var clear_all_group = false;
8026
for ( i = 0; i < n; ++i ) {
8027
if ( _via_image_grid_group_var[i].type === type &&
8028
_via_image_grid_group_var[i].name === attr_id ) {
8029
clear_all_group = true;
8030
break;
8031
}
8032
}
8033
_via_regions_group_color_init();
8034
_via_redraw_reg_canvas();
8035
8036
// @todo: it is wasteful to cancel the full set of groups.
8037
// we should only cancel the groups that are affected by this update.
8038
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
8039
if ( clear_all_group ) {
8040
image_grid_show_all_project_images();
8041
}
8042
}
8043
}
8044
8045
function annotation_editor_update_file_metadata(img_index_list, attr_id, new_value, new_checked) {
8046
return new Promise( function(ok_callback, err_callback) {
8047
var i, n, img_id, img_index;
8048
n = img_index_list.length;
8049
var update_count = 0;
8050
for ( i = 0; i < n; ++i ) {
8051
img_index = img_index_list[i];
8052
img_id = _via_image_id_list[img_index];
8053
8054
switch( _via_attributes['file'][attr_id].type ) {
8055
case 'text': // fallback
8056
case 'radio': // fallback
8057
case 'dropdown': // fallback
8058
case 'image':
8059
_via_img_metadata[img_id].file_attributes[attr_id] = new_value;
8060
update_count += 1;
8061
break;
8062
8063
case 'checkbox':
8064
var option_id = new_value;
8065
if ( _via_img_metadata[img_id].file_attributes.hasOwnProperty(attr_id) ) {
8066
if ( typeof(_via_img_metadata[img_id].file_attributes[attr_id]) !== 'object' ) {
8067
var old_value = _via_img_metadata[img_id].file_attributes[attr_id];
8068
_via_img_metadata[img_id].file_attributes[attr_id] = {};
8069
if ( Object.keys(_via_attributes['file'][attr_id]['options']).includes(old_value) ) {
8070
// transform existing value as checkbox option
8071
_via_img_metadata[img_id].file_attributes[attr_id] = {};
8072
_via_img_metadata[img_id].file_attributes[attr_id][old_value] = true;
8073
}
8074
}
8075
} else {
8076
_via_img_metadata[img_id].file_attributes[attr_id] = {};
8077
}
8078
if ( new_checked ) {
8079
_via_img_metadata[img_id].file_attributes[attr_id][option_id] = true;
8080
} else {
8081
// false option values are not stored
8082
delete _via_img_metadata[img_id].file_attributes[attr_id][option_id];
8083
}
8084
update_count += 1;
8085
break;
8086
}
8087
}
8088
ok_callback(update_count);
8089
});
8090
}
8091
8092
function annotation_editor_update_region_metadata(img_index_list, region_id, attr_id, new_value, new_checked) {
8093
return new Promise( function(ok_callback, err_callback) {
8094
var i, n, img_id, img_index;
8095
n = img_index_list.length;
8096
var update_count = 0;
8097
var region_list = [];
8098
var j, m;
8099
8100
if ( region_id === -1 ) {
8101
// update all regions on a file (for image grid view)
8102
for ( i = 0; i < n; ++i ) {
8103
img_index = img_index_list[i];
8104
img_id = _via_image_id_list[img_index];
8105
8106
m = _via_img_metadata[img_id].regions.length;
8107
for ( j = 0; j < m; ++j ) {
8108
if ( ! image_grid_is_region_in_current_group( _via_img_metadata[img_id].regions[j].region_attributes ) ) {
8109
continue;
8110
}
8111
8112
switch( _via_attributes['region'][attr_id].type ) {
8113
case 'text': // fallback
8114
case 'dropdown': // fallback
8115
case 'radio': // fallback
8116
case 'image':
8117
_via_img_metadata[img_id].regions[j].region_attributes[attr_id] = new_value;
8118
update_count += 1;
8119
break;
8120
case 'checkbox':
8121
var option_id = new_value;
8122
if ( _via_img_metadata[img_id].regions[j].region_attributes.hasOwnProperty(attr_id) ) {
8123
if ( typeof(_via_img_metadata[img_id].regions[j].region_attributes[attr_id]) !== 'object' ) {
8124
var old_value = _via_img_metadata[img_id].regions[j].region_attributes[attr_id];
8125
_via_img_metadata[img_id].regions[j].region_attributes[attr_id] = {}
8126
if ( Object.keys(_via_attributes['region'][attr_id]['options']).includes(old_value) ) {
8127
// transform existing value as checkbox option
8128
_via_img_metadata[img_id].regions[j].region_attributes[attr_id][old_value] = true;
8129
}
8130
}
8131
} else {
8132
_via_img_metadata[img_id].regions[j].region_attributes[attr_id] = {};
8133
}
8134
8135
if ( new_checked ) {
8136
_via_img_metadata[img_id].regions[j].region_attributes[attr_id][option_id] = true;
8137
} else {
8138
// false option values are not stored
8139
delete _via_img_metadata[img_id].regions[j].region_attributes[attr_id][option_id];
8140
}
8141
update_count += 1;
8142
break;
8143
}
8144
}
8145
}
8146
} else {
8147
// update a single region in a file (for single image view)
8148
// update all regions on a file (for image grid view)
8149
for ( i = 0; i < n; ++i ) {
8150
img_index = img_index_list[i];
8151
img_id = _via_image_id_list[img_index];
8152
8153
switch( _via_attributes['region'][attr_id].type ) {
8154
case 'text': // fallback
8155
case 'dropdown': // fallback
8156
case 'radio': // fallback
8157
case 'image':
8158
_via_img_metadata[img_id].regions[region_id].region_attributes[attr_id] = new_value;
8159
update_count += 1;
8160
break;
8161
case 'checkbox':
8162
var option_id = new_value;
8163
8164
if ( _via_img_metadata[img_id].regions[region_id].region_attributes.hasOwnProperty(attr_id) ) {
8165
if ( typeof(_via_img_metadata[img_id].regions[region_id].region_attributes[attr_id]) !== 'object' ) {
8166
var old_value = _via_img_metadata[img_id].regions[region_id].region_attributes[attr_id];
8167
_via_img_metadata[img_id].regions[region_id].region_attributes[attr_id] = {};
8168
if ( Object.keys(_via_attributes['region'][attr_id]['options']).includes(old_value) ) {
8169
// transform existing value as checkbox option
8170
_via_img_metadata[img_id].regions[region_id].region_attributes[attr_id][old_value] = true;
8171
}
8172
}
8173
} else {
8174
_via_img_metadata[img_id].regions[region_id].region_attributes[attr_id] = {};
8175
}
8176
8177
if ( new_checked ) {
8178
_via_img_metadata[img_id].regions[region_id].region_attributes[attr_id][option_id] = true;
8179
} else {
8180
// false option values are not stored
8181
delete _via_img_metadata[img_id].regions[region_id].region_attributes[attr_id][option_id];
8182
}
8183
update_count += 1;
8184
break;
8185
}
8186
}
8187
}
8188
ok_callback(update_count);
8189
});
8190
}
8191
8192
function set_region_annotations_to_default_value(rid) {
8193
var attr_id;
8194
for ( attr_id in _via_attributes['region'] ) {
8195
var attr_type = _via_attributes['region'][attr_id].type;
8196
switch( attr_type ) {
8197
case 'text':
8198
var default_value = _via_attributes['region'][attr_id].default_value;
8199
if ( typeof(default_value) !== 'undefined' ) {
8200
_via_img_metadata[_via_image_id].regions[rid].region_attributes[attr_id] = default_value;
8201
}
8202
break;
8203
case 'image': // fallback
8204
case 'dropdown': // fallback
8205
case 'radio':
8206
_via_img_metadata[_via_image_id].regions[rid].region_attributes[attr_id] = '';
8207
var default_options = _via_attributes['region'][attr_id].default_options;
8208
if ( typeof(default_options) !== 'undefined' ) {
8209
_via_img_metadata[_via_image_id].regions[rid].region_attributes[attr_id] = Object.keys(default_options)[0];
8210
}
8211
break;
8212
8213
case 'checkbox':
8214
_via_img_metadata[_via_image_id].regions[rid].region_attributes[attr_id] = {};
8215
var default_options = _via_attributes['region'][attr_id].default_options;
8216
if ( typeof(default_options) !== 'underfined' ) {
8217
var option_id;
8218
for ( option_id in default_options ) {
8219
var default_value = default_options[option_id];
8220
if ( typeof(default_value) !== 'underfined' ) {
8221
_via_img_metadata[_via_image_id].regions[rid].region_attributes[attr_id][option_id] = default_value;
8222
}
8223
}
8224
}
8225
break;
8226
}
8227
}
8228
}
8229
8230
function set_file_annotations_to_default_value(image_id) {
8231
var attr_id;
8232
for ( attr_id in _via_attributes['file'] ) {
8233
var attr_type = _via_attributes['file'][attr_id].type;
8234
switch( attr_type ) {
8235
case 'text':
8236
var default_value = _via_attributes['file'][attr_id].default_value;
8237
_via_img_metadata[image_id].file_attributes[attr_id] = default_value;
8238
break;
8239
case 'image': // fallback
8240
case 'dropdown': // fallback
8241
case 'radio':
8242
_via_img_metadata[image_id].file_attributes[attr_id] = '';
8243
var default_options = _via_attributes['file'][attr_id].default_options;
8244
_via_img_metadata[image_id].file_attributes[attr_id] = Object.keys(default_options)[0];
8245
break;
8246
case 'checkbox':
8247
_via_img_metadata[image_id].file_attributes[attr_id] = {};
8248
var default_options = _via_attributes['file'][attr_id].default_options;
8249
var option_id;
8250
for ( option_id in default_options ) {
8251
var default_value = default_options[option_id];
8252
_via_img_metadata[image_id].file_attributes[attr_id][option_id] = default_value;
8253
}
8254
break;
8255
}
8256
}
8257
}
8258
8259
function annotation_editor_increase_panel_height() {
8260
var p = document.getElementById('annotation_editor_panel');
8261
if ( _via_settings.ui.annotation_editor_height < 95 ) {
8262
_via_settings.ui.annotation_editor_height += VIA_ANNOTATION_EDITOR_HEIGHT_CHANGE;
8263
p.style.height = _via_settings.ui.annotation_editor_height + '%';
8264
}
8265
}
8266
8267
function annotation_editor_decrease_panel_height() {
8268
var p = document.getElementById('annotation_editor_panel');
8269
if ( _via_settings.ui.annotation_editor_height > 10 ) {
8270
_via_settings.ui.annotation_editor_height -= VIA_ANNOTATION_EDITOR_HEIGHT_CHANGE;
8271
p.style.height = _via_settings.ui.annotation_editor_height + '%';
8272
}
8273
}
8274
8275
function annotation_editor_increase_content_size() {
8276
var p = document.getElementById('annotation_editor_panel');
8277
if ( _via_settings.ui.annotation_editor_fontsize < 1.6 ) {
8278
_via_settings.ui.annotation_editor_fontsize += VIA_ANNOTATION_EDITOR_FONTSIZE_CHANGE;
8279
p.style.fontSize = _via_settings.ui.annotation_editor_fontsize + 'rem';
8280
}
8281
}
8282
8283
function annotation_editor_decrease_content_size() {
8284
var p = document.getElementById('annotation_editor_panel');
8285
if ( _via_settings.ui.annotation_editor_fontsize > 0.4 ) {
8286
_via_settings.ui.annotation_editor_fontsize -= VIA_ANNOTATION_EDITOR_FONTSIZE_CHANGE;
8287
p.style.fontSize = _via_settings.ui.annotation_editor_fontsize + 'rem';
8288
}
8289
}
8290
8291
//
8292
// via project
8293
//
8294
function project_set_name(name) {
8295
_via_settings.project.name = name;
8296
8297
var p = document.getElementById('project_name');
8298
p.value = _via_settings.project.name;
8299
}
8300
8301
function project_init_default_project() {
8302
if ( ! _via_settings.hasOwnProperty('project') ) {
8303
_via_settings.project = {};
8304
}
8305
8306
project_set_name( project_get_default_project_name() );
8307
}
8308
8309
function project_on_name_update(p) {
8310
project_set_name(p.value);
8311
}
8312
8313
function project_get_default_project_name() {
8314
const now = new Date();
8315
var MONTH_SHORT_NAME = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
8316
var ts = now.getDate() + MONTH_SHORT_NAME[now.getMonth()] + now.getFullYear() +
8317
'_' + now.getHours() + 'h' + now.getMinutes() + 'm';
8318
8319
var project_name = 'via_project_' + ts;
8320
return project_name;
8321
}
8322
8323
function project_save_with_confirm() {
8324
var config = {'title':'Save Project' };
8325
var input = { 'project_name': { type:'text', name:'Project Name', value:_via_settings.project.name, disabled:false, size:30 },
8326
'save_annotations':{ type:'checkbox', name:'Save region and file annotations (i.e. manual annotations)', checked:true, disabled:false},
8327
'save_attributes':{ type:'checkbox', name:'Save region and file attributes.', checked:true},
8328
'save_via_settings':{ type:'checkbox', name:'Save VIA application settings', checked:true},
8329
// 'save_base64_data':{ type:'checkbox', name:'Save base64 data of images (if present)', checked:false},
8330
// 'save_images':{type:'checkbox', 'name':'Save images <span class="warning">(WARNING: only recommended for projects containing small number of images)</span>', value:false},
8331
};
8332
8333
invoke_with_user_inputs(project_save_confirmed, input, config);
8334
}
8335
8336
function project_save_confirmed(input) {
8337
if ( input.project_name.value !== _via_settings.project.name ) {
8338
project_set_name(input.project_name.value);
8339
}
8340
8341
// via project
8342
var _via_project = { '_via_settings': _via_settings,
8343
'_via_img_metadata': _via_img_metadata,
8344
'_via_attributes': _via_attributes,
8345
'_via_data_format_version': '2.0.10',
8346
'_via_image_id_list': _via_image_id_list
8347
};
8348
8349
var filename = input.project_name.value + '.json';
8350
var data_blob = new Blob( [JSON.stringify(_via_project)],
8351
{type: 'text/json;charset=utf-8'});
8352
8353
save_data_to_local_file(data_blob, filename);
8354
8355
user_input_default_cancel_handler();
8356
}
8357
8358
function project_open_select_project_file() {
8359
if (invisible_file_input) {
8360
invisible_file_input.accept = '.json';
8361
invisible_file_input.onchange = project_open;
8362
invisible_file_input.removeAttribute('multiple');
8363
invisible_file_input.click();
8364
}
8365
}
8366
8367
function project_open(event) {
8368
var selected_file = event.target.files[0];
8369
load_text_file(selected_file, project_open_parse_json_file);
8370
}
8371
8372
function project_open_parse_json_file(project_file_data) {
8373
var d = JSON.parse(project_file_data);
8374
if ( d['_via_settings'] && d['_via_img_metadata'] && d['_via_attributes'] ) {
8375
// import settings
8376
project_import_settings(d['_via_settings']);
8377
8378
// clear existing data (if any)
8379
_via_image_id_list = [];
8380
_via_image_filename_list = [];
8381
_via_img_count = 0;
8382
_via_img_metadata = {};
8383
_via_img_fileref = {};
8384
_via_img_src = {};
8385
_via_attributes = { 'region':{}, 'file':{} };
8386
_via_buffer_remove_all();
8387
8388
// import image metadata
8389
_via_img_metadata = {};
8390
for ( var img_id in d['_via_img_metadata'] ) {
8391
if('filename' in d['_via_img_metadata'][img_id] &&
8392
'size' in d['_via_img_metadata'][img_id] &&
8393
'regions' in d['_via_img_metadata'][img_id] &&
8394
'file_attributes' in d['_via_img_metadata'][img_id]) {
8395
if( !d.hasOwnProperty('_via_image_id_list') ) {
8396
_via_image_id_list.push(img_id);
8397
_via_image_filename_list.push( d['_via_img_metadata'][img_id].filename );
8398
}
8399
8400
set_file_annotations_to_default_value(img_id);
8401
_via_img_metadata[img_id] = d['_via_img_metadata'][img_id];
8402
_via_img_count += 1;
8403
} else {
8404
console.log('discarding malformed entry for ' + img_id +
8405
': ' + JSON.stringify(d['_via_img_metadata'][img_id]));
8406
}
8407
}
8408
8409
8410
// import image_id_list which records the order of images
8411
if( d.hasOwnProperty('_via_image_id_list') ) {
8412
_via_image_id_list = d['_via_image_id_list'];
8413
for(var img_id_index in d['_via_image_id_list']) {
8414
var img_id = d['_via_image_id_list'][img_id_index];
8415
_via_image_filename_list.push(_via_img_metadata[img_id]['filename']);
8416
}
8417
}
8418
8419
// import attributes
8420
_via_attributes = d['_via_attributes'];
8421
project_parse_via_attributes_from_img_metadata();
8422
var fattr_id_list = Object.keys(_via_attributes['file']);
8423
var rattr_id_list = Object.keys(_via_attributes['region']);
8424
if ( rattr_id_list.length ) {
8425
_via_attribute_being_updated = 'region';
8426
_via_current_attribute_id = rattr_id_list[0];
8427
} else {
8428
if ( fattr_id_list.length ) {
8429
_via_attribute_being_updated = 'file';
8430
_via_current_attribute_id = fattr_id_list[0];
8431
}
8432
}
8433
8434
if ( _via_settings.core.default_filepath !== '' ) {
8435
_via_file_resolve_all_to_default_filepath();
8436
}
8437
8438
show_message('Imported project [' + _via_settings['project'].name + '] with ' + _via_img_count + ' files.');
8439
8440
if ( _via_img_count > 0 ) {
8441
_via_show_img(0);
8442
update_img_fn_list();
8443
_via_reload_img_fn_list_table = true;
8444
}
8445
} else {
8446
show_message('Cannot import project from a corrupt file!');
8447
}
8448
}
8449
8450
function project_parse_via_attributes_from_img_metadata() {
8451
// parse _via_img_metadata to populate _via_attributes
8452
var img_id, fa, ra;
8453
8454
if ( ! _via_attributes.hasOwnProperty('file') ) {
8455
_via_attributes['file'] = {};
8456
}
8457
if ( ! _via_attributes.hasOwnProperty('region') ) {
8458
_via_attributes['region'] = {};
8459
}
8460
8461
for ( img_id in _via_img_metadata ) {
8462
// file attributes
8463
for ( fa in _via_img_metadata[img_id].file_attributes ) {
8464
if ( ! _via_attributes['file'].hasOwnProperty(fa) ) {
8465
_via_attributes['file'][fa] = {};
8466
_via_attributes['file'][fa]['type'] = 'text';
8467
}
8468
}
8469
// region attributes
8470
var ri;
8471
for ( ri = 0; ri < _via_img_metadata[img_id].regions.length; ++ri ) {
8472
for ( ra in _via_img_metadata[img_id].regions[ri].region_attributes ) {
8473
if ( ! _via_attributes['region'].hasOwnProperty(ra) ) {
8474
_via_attributes['region'][ra] = {};
8475
_via_attributes['region'][ra]['type'] = 'text';
8476
}
8477
}
8478
}
8479
}
8480
}
8481
8482
function project_import_settings(s) {
8483
// @todo find a generic way to import into _via_settings
8484
// only the components present in s (and not overwrite everything)
8485
var k1;
8486
for ( k1 in s ) {
8487
if ( typeof( s[k1] ) === 'object' ) {
8488
var k2;
8489
for ( k2 in s[k1] ) {
8490
if ( typeof( s[k1][k2] ) === 'object' ) {
8491
var k3;
8492
for ( k3 in s[k1][k2] ) {
8493
_via_settings[k1][k2][k3] = s[k1][k2][k3];
8494
}
8495
} else {
8496
_via_settings[k1][k2] = s[k1][k2];
8497
}
8498
}
8499
} else {
8500
_via_settings[k1] = s[k1];
8501
}
8502
}
8503
}
8504
8505
function project_file_remove_with_confirm() {
8506
var img_id = _via_image_id_list[_via_image_index];
8507
var filename = _via_img_metadata[img_id].filename;
8508
var region_count = _via_img_metadata[img_id].regions.length;
8509
8510
var config = {'title':'Remove File from Project' };
8511
var input = { 'img_index': { type:'text', name:'File Id', value:(_via_image_index+1), disabled:true, size:8 },
8512
'filename':{ type:'text', name:'Filename', value:filename, disabled:true, size:30},
8513
'region_count':{ type:'text', name:'Number of regions', disabled:true, value:region_count, size:8}
8514
};
8515
8516
invoke_with_user_inputs(project_file_remove_confirmed, input, config);
8517
}
8518
8519
function project_file_remove_confirmed(input) {
8520
var img_index = input.img_index.value - 1;
8521
project_remove_file(img_index);
8522
8523
if ( img_index === _via_img_count ) {
8524
if ( _via_img_count === 0 ) {
8525
_via_current_image_loaded = false;
8526
show_home_panel();
8527
} else {
8528
_via_show_img(img_index - 1);
8529
}
8530
} else {
8531
_via_show_img(img_index);
8532
}
8533
_via_reload_img_fn_list_table = true;
8534
update_img_fn_list();
8535
show_message('Removed file [' + input.filename.value + '] from project');
8536
user_input_default_cancel_handler();
8537
}
8538
8539
8540
function project_remove_file(img_index) {
8541
if ( img_index < 0 || img_index >= _via_img_count ) {
8542
console.log('project_remove_file(): invalid img_index ' + img_index);
8543
return;
8544
}
8545
var img_id = _via_image_id_list[img_index];
8546
8547
// remove img_index from all array
8548
// this invalidates all image_index > img_index
8549
_via_image_id_list.splice( img_index, 1 );
8550
_via_image_filename_list.splice( img_index, 1 );
8551
8552
var img_fn_list_index = _via_img_fn_list_img_index_list.indexOf(img_index);
8553
if ( img_fn_list_index !== -1 ) {
8554
_via_img_fn_list_img_index_list.splice( img_fn_list_index, 1 );
8555
}
8556
8557
// clear all buffer
8558
// @todo: it is wasteful to clear all the buffer instead of removing a single image
8559
_via_buffer_remove_all();
8560
img_fn_list_clear_css_classname('buffered');
8561
8562
_via_clear_reg_canvas();
8563
delete _via_img_metadata[img_id];
8564
delete _via_img_src[img_id];
8565
delete _via_img_fileref[img_id];
8566
8567
_via_img_count -= 1;
8568
}
8569
8570
function project_add_new_file(filename, size, file_id) {
8571
var img_id = file_id;
8572
if ( typeof(img_id) === 'undefined' ) {
8573
if ( typeof(size) === 'undefined' ) {
8574
size = -1;
8575
}
8576
img_id = _via_get_image_id(filename, size);
8577
}
8578
8579
if ( ! _via_img_metadata.hasOwnProperty(img_id) ) {
8580
_via_img_metadata[img_id] = new file_metadata(filename, size);
8581
_via_image_id_list.push(img_id);
8582
_via_image_filename_list.push(filename);
8583
_via_img_count += 1;
8584
}
8585
return img_id;
8586
}
8587
8588
function project_file_add_local(event) {
8589
var user_selected_images = event.target.files;
8590
var original_image_count = _via_img_count;
8591
8592
var new_img_index_list = [];
8593
var discarded_file_count = 0;
8594
for ( var i = 0; i < user_selected_images.length; ++i ) {
8595
var filetype = user_selected_images[i].type.substr(0, 5);
8596
if ( filetype === 'image' ) {
8597
// check which filename in project matches the user selected file
8598
var img_index = _via_image_filename_list.indexOf(user_selected_images[i].name);
8599
if( img_index === -1) {
8600
// a new file was added to project
8601
var new_img_id = project_add_new_file(user_selected_images[i].name,
8602
user_selected_images[i].size);
8603
_via_img_fileref[new_img_id] = user_selected_images[i];
8604
set_file_annotations_to_default_value(new_img_id);
8605
new_img_index_list.push( _via_image_id_list.indexOf(new_img_id) );
8606
} else {
8607
// an existing file was resolved using browser's file selector
8608
var img_id = _via_image_id_list[img_index];
8609
_via_img_fileref[img_id] = user_selected_images[i];
8610
_via_img_metadata[img_id]['size'] = user_selected_images[i].size;
8611
}
8612
} else {
8613
discarded_file_count += 1;
8614
}
8615
}
8616
8617
if ( _via_img_metadata ) {
8618
var status_msg = 'Loaded ' + new_img_index_list.length + ' images.';
8619
if ( discarded_file_count ) {
8620
status_msg += ' ( Discarded ' + discarded_file_count + ' non-image files! )';
8621
}
8622
show_message(status_msg);
8623
8624
if ( new_img_index_list.length ) {
8625
// show first of newly added image
8626
_via_show_img( new_img_index_list[0] );
8627
} else {
8628
// show original image
8629
_via_show_img ( _via_image_index );
8630
}
8631
update_img_fn_list();
8632
} else {
8633
show_message("Please upload some image files!");
8634
}
8635
}
8636
8637
function project_file_add_abs_path_with_input() {
8638
var config = {'title':'Add File using Absolute Path' };
8639
var input = { 'absolute_path': { type:'text', name:'add one absolute path', placeholder:'/home/abhishek/image1.jpg', disabled:false, size:50 },
8640
'absolute_path_list': { type:'textarea', name:'or, add multiple paths (one path per line)', placeholder:'/home/abhishek/image1.jpg\n/home/abhishek/image2.jpg\n/home/abhishek/image3.png', disabled:false, rows:5, cols:80 }
8641
};
8642
8643
invoke_with_user_inputs(project_file_add_abs_path_input_done, input, config);
8644
}
8645
8646
function project_file_add_abs_path_input_done(input) {
8647
if ( input.absolute_path.value !== '' ) {
8648
var abs_path = input.absolute_path.value.trim();
8649
var img_id = project_file_add_url(abs_path);
8650
var img_index = _via_image_id_list.indexOf(img_id);
8651
_via_show_img(img_index);
8652
show_message('Added file at absolute path [' + abs_path + ']');
8653
update_img_fn_list();
8654
user_input_default_cancel_handler();
8655
} else {
8656
if ( input.absolute_path_list.value !== '' ) {
8657
var absolute_path_list_str = input.absolute_path_list.value;
8658
import_files_url_from_csv(absolute_path_list_str);
8659
}
8660
}
8661
}
8662
8663
function project_file_add_url_with_input() {
8664
var config = {'title':'Add File using URL' };
8665
var input = { 'url': { type:'text', name:'add one URL', placeholder:'http://www.robots.ox.ac.uk/~vgg/software/via/images/swan.jpg', disabled:false, size:50 },
8666
'url_list': { type:'textarea', name:'or, add multiple URL (one url per line)', placeholder:'http://www.example.com/image1.jpg\nhttp://www.example.com/image2.jpg\nhttp://www.example.com/image3.png', disabled:false, rows:5, cols:80 }
8667
};
8668
8669
invoke_with_user_inputs(project_file_add_url_input_done, input, config);
8670
}
8671
8672
function project_file_add_url_input_done(input) {
8673
if ( input.url.value !== '' ) {
8674
var url = input.url.value.trim();
8675
var img_id = project_file_add_url(url);
8676
var img_index = _via_image_id_list.indexOf(img_id);
8677
show_message('Added file at url [' + url + ']');
8678
update_img_fn_list();
8679
_via_show_img(img_index);
8680
user_input_default_cancel_handler();
8681
} else {
8682
if ( input.url_list.value !== '' ) {
8683
var url_list_str = input.url_list.value;
8684
import_files_url_from_csv(url_list_str);
8685
}
8686
}
8687
}
8688
8689
function project_file_add_url(url) {
8690
if ( url !== '' ) {
8691
var size = -1; // convention: files added using url have size = -1
8692
var img_id = _via_get_image_id(url, size);
8693
8694
if ( ! _via_img_metadata.hasOwnProperty(img_id) ) {
8695
img_id = project_add_new_file(url);
8696
_via_img_src[img_id] = _via_img_metadata[img_id].filename;
8697
set_file_annotations_to_default_value(img_id);
8698
return img_id;
8699
}
8700
}
8701
}
8702
8703
function project_file_add_base64(filename, base64) {
8704
var size = -1; // convention: files added using url have size = -1
8705
var img_id = _via_get_image_id(filename, size);
8706
8707
if ( ! _via_img_metadata.hasOwnProperty(img_id) ) {
8708
img_id = project_add_new_file(filename, size);
8709
_via_img_src[img_id] = base64;
8710
set_file_annotations_to_default_value(img_id);
8711
}
8712
}
8713
8714
function project_file_load_on_fail(img_index) {
8715
var img_id = _via_image_id_list[img_index];
8716
_via_img_src[img_id] = '';
8717
_via_image_load_error[img_index] = true;
8718
img_fn_list_ith_entry_error(img_index, true);
8719
}
8720
8721
function project_file_load_on_success(img_index) {
8722
_via_image_load_error[img_index] = false;
8723
img_fn_list_ith_entry_error(img_index, false);
8724
}
8725
8726
function project_settings_toggle() {
8727
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.SETTINGS ) {
8728
show_single_image_view();
8729
} else {
8730
project_settings_show();
8731
}
8732
}
8733
8734
function project_settings_show() {
8735
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.SETTINGS);
8736
}
8737
8738
function project_filepath_add_from_input(p, button) {
8739
var new_path = document.getElementById(p).value.trim();
8740
var img_index = parseInt(button.getAttribute('value'));
8741
project_filepath_add(new_path);
8742
_via_show_img(img_index);
8743
}
8744
8745
function project_filepath_add(new_path) {
8746
if ( path === '' ) {
8747
return;
8748
}
8749
8750
if ( _via_settings.core.filepath.hasOwnProperty(new_path) ) {
8751
return;
8752
} else {
8753
var largest_order = 0;
8754
var path;
8755
for ( path in _via_settings.core.filepath ) {
8756
if ( _via_settings.core.filepath[path] > largest_order ) {
8757
largest_order = _via_settings.core.filepath[path];
8758
}
8759
}
8760
_via_settings.core.filepath[new_path] = largest_order + 1;
8761
8762
}
8763
}
8764
8765
function project_filepath_del(path) {
8766
if ( _via_settings.core.filepath.hasOwnProperty(path) ) {
8767
delete _via_settings.core.filepath[path];
8768
}
8769
}
8770
8771
function project_save_attributes() {
8772
var blob_attr = {type: 'application/json;charset=utf-8'};
8773
var all_region_data_blob = new Blob( [ JSON.stringify(_via_attributes) ], blob_attr);
8774
8775
save_data_to_local_file(all_region_data_blob, _via_settings.project.name + '_attributes.json');
8776
}
8777
8778
function project_import_attributes_from_file(event) {
8779
var selected_files = event.target.files;
8780
for ( var i = 0; i < selected_files.length; ++i ) {
8781
var file = selected_files[i];
8782
load_text_file(file, project_import_attributes_from_json);
8783
}
8784
}
8785
8786
function project_import_attributes_from_json(data) {
8787
try {
8788
var d = JSON.parse(data);
8789
var attr;
8790
var fattr_count = 0;
8791
var rattr_count = 0;
8792
// process file attributes
8793
for ( attr in d['file'] ) {
8794
_via_attributes['file'][attr] = JSON.parse( JSON.stringify( d['file'][attr] ) );
8795
fattr_count += 1;
8796
}
8797
8798
// process region attributes
8799
for ( attr in d['region'] ) {
8800
_via_attributes['region'][attr] = JSON.parse( JSON.stringify( d['region'][attr] ) );
8801
rattr_count += 1;
8802
}
8803
8804
if ( fattr_count > 0 || rattr_count > 0 ) {
8805
var fattr_id_list = Object.keys(_via_attributes['file']);
8806
var rattr_id_list = Object.keys(_via_attributes['region']);
8807
if ( rattr_id_list.length ) {
8808
_via_attribute_being_updated = 'region';
8809
_via_current_attribute_id = rattr_id_list[0];
8810
} else {
8811
if ( fattr_id_list.length ) {
8812
_via_attribute_being_updated = 'file';
8813
_via_current_attribute_id = fattr_id_list[0];
8814
}
8815
}
8816
attribute_update_panel_set_active_button();
8817
update_attributes_update_panel();
8818
annotation_editor_update_content();
8819
}
8820
show_message('Imported ' + fattr_count + ' file attributes and '
8821
+ rattr_count + ' region attributes');
8822
} catch (error) {
8823
show_message('Failed to import attributes: [' + error + ']');
8824
}
8825
}
8826
8827
//
8828
// image grid
8829
//
8830
function image_grid_init() {
8831
var p = document.getElementById('image_grid_content');
8832
p.focus();
8833
p.addEventListener('mousedown', image_grid_mousedown_handler, false);
8834
p.addEventListener('mouseup', image_grid_mouseup_handler, false);
8835
p.addEventListener('dblclick', image_grid_dblclick_handler, false);
8836
8837
image_grid_set_content_panel_height_fixed();
8838
8839
// select policy as defined in settings
8840
var i, option;
8841
var p = document.getElementById('image_grid_show_image_policy');
8842
var n = p.options.length;
8843
for ( i = 0; i < n; ++i ) {
8844
if ( p.options[i].value === _via_settings.ui.image_grid.show_image_policy ) {
8845
p.selectedIndex = i;
8846
break;
8847
}
8848
}
8849
}
8850
8851
function image_grid_update() {
8852
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
8853
image_grid_set_content( _via_image_grid_img_index_list );
8854
}
8855
}
8856
8857
function image_grid_toggle() {
8858
var p = document.getElementById('toolbar_image_grid_toggle');
8859
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE_GRID ) {
8860
image_grid_clear_all_groups();
8861
show_single_image_view();
8862
} else {
8863
show_image_grid_view();
8864
}
8865
}
8866
8867
function image_grid_show_all_project_images() {
8868
var all_img_index_list = [];
8869
var i, n;
8870
//n = _via_image_id_list.length;
8871
n = _via_img_fn_list_img_index_list.length;
8872
for ( i = 0; i < n; ++i ) {
8873
all_img_index_list.push( _via_img_fn_list_img_index_list[i] );
8874
}
8875
image_grid_clear_all_groups();
8876
8877
var p = document.getElementById('image_grid_toolbar_group_by_select');
8878
p.selectedIndex = 0;
8879
8880
image_grid_set_content(all_img_index_list);
8881
}
8882
8883
function image_grid_clear_all_groups() {
8884
var i, n;
8885
n = _via_image_grid_group_var.length;
8886
for ( i = 0; i < n; ++i ) {
8887
image_grid_remove_html_group_panel( _via_image_grid_group_var[i] );
8888
image_grid_group_by_select_set_disabled( _via_image_grid_group_var[i].type,
8889
_via_image_grid_group_var[i].name,
8890
false);
8891
}
8892
_via_image_grid_group = {};
8893
_via_image_grid_group_var = [];
8894
8895
}
8896
8897
function image_grid_set_content(img_index_list) {
8898
if ( img_index_list.length === 0 ) {
8899
return;
8900
}
8901
if ( _via_image_grid_load_ongoing ) {
8902
return;
8903
}
8904
8905
_via_image_grid_img_index_list = img_index_list.slice(0);
8906
_via_image_grid_selected_img_index_list = img_index_list.slice(0);
8907
8908
document.getElementById('image_grid_group_by_img_count').innerHTML = _via_image_grid_img_index_list.length;
8909
8910
_via_image_grid_page_first_index = 0;
8911
_via_image_grid_page_last_index = null;
8912
_via_image_grid_stack_prev_page = [];
8913
_via_image_grid_page_img_index_list = [];
8914
8915
image_grid_clear_content();
8916
image_grid_set_content_panel_height_fixed();
8917
_via_image_grid_load_ongoing = true;
8918
8919
var n = _via_image_grid_img_index_list.length;
8920
switch ( _via_settings.ui.image_grid.show_image_policy ) {
8921
case 'all':
8922
_via_image_grid_page_img_index_list = _via_image_grid_img_index_list.slice(0);
8923
break;
8924
case 'first_mid_last':
8925
if ( n < 3 ) {
8926
var i;
8927
for ( i = 0; i < n; ++i ) {
8928
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[i] );
8929
}
8930
} else {
8931
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[0] );
8932
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[ Math.floor(n/2) ] );
8933
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[n-1] );
8934
}
8935
break;
8936
case 'even_indexed':
8937
var i;
8938
for ( i = 0; i < n; ++i ) {
8939
if ( i % 2 !== 0 ) { // since the user views (i+1) based indexing
8940
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[i] );
8941
}
8942
}
8943
break;
8944
case 'odd_indexed':
8945
var i;
8946
for ( i = 0; i < n; ++i ) {
8947
if ( i % 2 === 0 ) { // since the user views (i+1) based indexing
8948
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[i] );
8949
}
8950
}
8951
break;
8952
case 'gap5': // fallback
8953
case 'gap25': // fallback
8954
case 'gap50': // fallback
8955
var del = parseInt( _via_settings.ui.image_grid.show_image_policy.substr( 'gap'.length ) );
8956
var i;
8957
for ( i = 0; i < n; i = i + del ) {
8958
_via_image_grid_page_img_index_list.push( _via_image_grid_img_index_list[i] );
8959
}
8960
break;
8961
}
8962
8963
_via_image_grid_visible_img_index_list = [];
8964
8965
image_grid_update_sel_count_html();
8966
annotation_editor_update_content();
8967
8968
image_grid_content_append_img( _via_image_grid_page_first_index );
8969
8970
show_message('[Click] toggles selection, ' +
8971
'[Shift + Click] selects everything a image, ' +
8972
'[Click] or [Ctrl + Click] removes selection of all subsequent or preceeding images.');
8973
}
8974
8975
function image_grid_clear_content() {
8976
var img_container = document.getElementById('image_grid_content_img');
8977
var img_rshape = document.getElementById('image_grid_content_rshape');
8978
img_container.innerHTML = '';
8979
img_rshape.innerHTML = '';
8980
_via_image_grid_visible_img_index_list = [];
8981
}
8982
8983
function image_grid_set_content_panel_height_fixed() {
8984
var pc = document.getElementById('image_grid_content');
8985
var de = document.documentElement;
8986
pc.style.height = (de.clientHeight - 5.5*ui_top_panel.offsetHeight) + 'px';
8987
}
8988
8989
// We do not know how many images will fit in the display area.
8990
// Therefore, we add images one-by-one until overflow of parent
8991
// container is detected.
8992
function image_grid_content_append_img( img_grid_index ) {
8993
var img_index = _via_image_grid_page_img_index_list[img_grid_index];
8994
var html_img_id = image_grid_get_html_img_id(img_index);
8995
var img_id = _via_image_id_list[img_index];
8996
var e = document.createElement('img');
8997
if ( _via_img_fileref[img_id] instanceof File ) {
8998
var img_reader = new FileReader();
8999
img_reader.addEventListener( "error", function() {
9000
//@todo
9001
}, false);
9002
img_reader.addEventListener( "load", function() {
9003
e.src = img_reader.result;
9004
}, false);
9005
img_reader.readAsDataURL( _via_img_fileref[img_id] );
9006
} else {
9007
e.src = _via_img_src[img_id];
9008
}
9009
e.setAttribute('id', html_img_id);
9010
e.setAttribute('height', _via_settings.ui.image_grid.img_height + 'px');
9011
e.setAttribute('title', '[' + (img_index+1) + '] ' + _via_img_metadata[img_id].filename);
9012
9013
e.addEventListener('load', image_grid_on_img_load, false);
9014
e.addEventListener('error', image_grid_on_img_error, false);
9015
document.getElementById('image_grid_content_img').appendChild(e);
9016
}
9017
9018
function image_grid_on_img_load(e) {
9019
var img = e.target;
9020
var img_index = image_grid_parse_html_img_id(img.id);
9021
project_file_load_on_success(img_index);
9022
9023
image_grid_add_img_if_possible(img);
9024
}
9025
9026
function image_grid_on_img_error(e) {
9027
var img = e.target;
9028
var img_index = image_grid_parse_html_img_id(img.id);
9029
project_file_load_on_fail(img_index);
9030
image_grid_add_img_if_possible(img);
9031
}
9032
9033
function image_grid_add_img_if_possible(img) {
9034
var img_index = image_grid_parse_html_img_id(img.id);
9035
9036
var p = document.getElementById('image_grid_content_img');
9037
var img_bottom_right_corner = parseInt(img.offsetTop) + parseInt(img.height);
9038
if ( p.clientHeight < img_bottom_right_corner ) {
9039
// stop as addition of this image caused overflow of parent container
9040
var img_container = document.getElementById('image_grid_content_img');
9041
img_container.removeChild(img);
9042
9043
if ( _via_settings.ui.image_grid.show_region_shape ) {
9044
image_grid_page_show_all_regions();
9045
}
9046
_via_image_grid_load_ongoing = false;
9047
9048
var index = _via_image_grid_page_img_index_list.indexOf(img_index);
9049
_via_image_grid_page_last_index = index;
9050
9051
// setup prev, next navigation
9052
var info = document.getElementById('image_grid_nav');
9053
var html = [];
9054
var first_index = _via_image_grid_page_first_index;
9055
var last_index = _via_image_grid_page_last_index - 1;
9056
html.push('<span>Showing&nbsp;' + (first_index + 1) +
9057
' to ' + (last_index + 1) + '&nbsp;:</span>');
9058
if ( _via_image_grid_stack_prev_page.length ) {
9059
html.push('<span class="text_button" onclick="image_grid_page_prev()">Prev</span>');
9060
} else {
9061
html.push('<span>Prev</span>');
9062
}
9063
html.push('<span class="text_button" onclick="image_grid_page_next()">Next</span');
9064
info.innerHTML = html.join('');
9065
} else {
9066
// process this image and trigger addition of next image in sequence
9067
var img_fn_list_index = _via_image_grid_page_img_index_list.indexOf(img_index);
9068
var next_img_fn_list_index = img_fn_list_index + 1;
9069
9070
_via_image_grid_visible_img_index_list.push( img_index );
9071
var is_selected = ( _via_image_grid_selected_img_index_list.indexOf(img_index) !== -1 );
9072
if ( ! is_selected ) {
9073
image_grid_update_img_select(img_index, 'unselect');
9074
}
9075
9076
if ( next_img_fn_list_index !== _via_image_grid_page_img_index_list.length ) {
9077
if ( _via_image_grid_load_ongoing ) {
9078
image_grid_content_append_img( img_fn_list_index + 1 );
9079
} else {
9080
// image grid load operation was cancelled
9081
_via_image_grid_page_last_index = _via_image_grid_page_first_index; // load this page again
9082
9083
var info = document.getElementById('image_grid_nav');
9084
var html = [];
9085
html.push('<span>Cancelled&nbsp;:</span>');
9086
if ( _via_image_grid_stack_prev_page.length ) {
9087
html.push('<span class="text_button" onclick="image_grid_page_prev()">Prev</span>');
9088
} else {
9089
html.push('<span>Prev</span>');
9090
}
9091
html.push('<span class="text_button" onclick="image_grid_page_next()">Next</span');
9092
info.innerHTML = html.join('');
9093
}
9094
} else {
9095
// last page
9096
var index = _via_image_grid_page_img_index_list.indexOf(img_index);
9097
_via_image_grid_page_last_index = index;
9098
9099
if ( _via_settings.ui.image_grid.show_region_shape ) {
9100
image_grid_page_show_all_regions();
9101
}
9102
_via_image_grid_load_ongoing = false;
9103
9104
// setup prev, next navigation
9105
var info = document.getElementById('image_grid_nav');
9106
var html = [];
9107
var first_index = _via_image_grid_page_first_index;
9108
var last_index = _via_image_grid_page_last_index;
9109
html.push('<span>Showing&nbsp;' + (first_index + 1) +
9110
' to ' + (last_index + 1) + ' (end)&nbsp;</span>');
9111
if ( _via_image_grid_stack_prev_page.length ) {
9112
html.push('<span class="text_button" onclick="image_grid_page_prev()">Prev</span>');
9113
} else {
9114
html.push('<span>Prev</span>');
9115
}
9116
html.push('<span>Next</span');
9117
9118
info.innerHTML = html.join('');
9119
}
9120
}
9121
}
9122
9123
function image_grid_onchange_show_image_policy(p) {
9124
_via_settings.ui.image_grid.show_image_policy = p.options[p.selectedIndex].value;
9125
image_grid_set_content(_via_image_grid_img_index_list);
9126
}
9127
9128
function image_grid_page_show_all_regions() {
9129
var all_promises = [];
9130
if ( _via_settings.ui.image_grid.show_region_shape ) {
9131
var p = document.getElementById('image_grid_content_img');
9132
var n = p.childNodes.length;
9133
var i;
9134
for ( i = 0; i < n; ++i ) {
9135
// draw region shape into global canvas for image grid
9136
var img_index = image_grid_parse_html_img_id( p.childNodes[i].id );
9137
var img_param = []; // [width, height, originalWidth, originalHeight, x, y]
9138
img_param.push( parseInt(p.childNodes[i].width) );
9139
img_param.push( parseInt(p.childNodes[i].height) );
9140
img_param.push( parseInt(p.childNodes[i].naturalWidth) );
9141
img_param.push( parseInt(p.childNodes[i].naturalHeight) );
9142
img_param.push( parseInt(p.childNodes[i].offsetLeft) + parseInt(p.childNodes[i].clientLeft) );
9143
img_param.push( parseInt(p.childNodes[i].offsetTop) + parseInt(p.childNodes[i].clientTop) );
9144
var promise = image_grid_show_region_shape( img_index, img_param );
9145
all_promises.push( promise );
9146
}
9147
// @todo: ensure that all promises are fulfilled
9148
}
9149
}
9150
9151
function image_grid_is_region_in_current_group(r) {
9152
var i, n;
9153
n = _via_image_grid_group_var.length;
9154
if ( n === 0 ) {
9155
return true;
9156
}
9157
9158
for ( i = 0; i < n; ++i ) {
9159
if ( _via_image_grid_group_var[i].type === 'region' ) {
9160
var group_value = _via_image_grid_group_var[i].values[ _via_image_grid_group_var[i].current_value_index ];
9161
if ( r[_via_image_grid_group_var[i].name] != group_value ) {
9162
return false;
9163
}
9164
}
9165
}
9166
return true;
9167
}
9168
9169
function image_grid_show_region_shape(img_index, img_param) {
9170
return new Promise( function(ok_callback, err_callback) {
9171
var i;
9172
var img_id = _via_image_id_list[img_index];
9173
var html_img_id = image_grid_get_html_img_id(img_index);
9174
var n = _via_img_metadata[img_id].regions.length;
9175
var is_in_group = false;
9176
for ( i = 0; i < n; ++i ) {
9177
if ( ! image_grid_is_region_in_current_group( _via_img_metadata[img_id].regions[i].region_attributes ) ) {
9178
// skip drawing this region which is not in current group
9179
continue;
9180
}
9181
9182
var r = _via_img_metadata[img_id].regions[i].shape_attributes;
9183
var dimg; // region coordinates in original image space
9184
switch( r.name ) {
9185
case VIA_REGION_SHAPE.RECT:
9186
dimg = [ r['x'], r['y'], r['x']+r['width'], r['y']+r['height'] ];
9187
break;
9188
case VIA_REGION_SHAPE.CIRCLE:
9189
dimg = [ r['cx'], r['cy'], r['cx']+r['r'], r['cy']+r['r'] ];
9190
break;
9191
case VIA_REGION_SHAPE.ELLIPSE:
9192
dimg = [ r['cx'], r['cy'], r['cx']+r['rx'], r['cy']+r['ry'] ];
9193
break;
9194
case VIA_REGION_SHAPE.POLYLINE: // handled by POLYGON
9195
case VIA_REGION_SHAPE.POLYGON:
9196
var j;
9197
dimg = [];
9198
for ( j = 0; j < r['all_points_x'].length; ++j ) {
9199
dimg.push( r['all_points_x'][j] );
9200
dimg.push( r['all_points_y'][j] );
9201
}
9202
break;
9203
case VIA_REGION_SHAPE.POINT:
9204
dimg = [ r['cx'], r['cy'] ];
9205
break;
9206
}
9207
var scale_factor = img_param[1] / img_param[3]; // new_height / original height
9208
var offset_x = img_param[4];
9209
var offset_y = img_param[5];
9210
var r2 = new _via_region( r.name, i, dimg, scale_factor, offset_x, offset_y);
9211
var r2_svg = r2.get_svg_element();
9212
r2_svg.setAttribute('id', image_grid_get_html_region_id(img_index, i));
9213
r2_svg.setAttribute('class', html_img_id);
9214
r2_svg.setAttribute('fill', _via_settings.ui.image_grid.rshape_fill);
9215
//r2_svg.setAttribute('fill-opacity', _via_settings.ui.image_grid.rshape_fill_opacity);
9216
r2_svg.setAttribute('stroke', _via_settings.ui.image_grid.rshape_stroke);
9217
r2_svg.setAttribute('stroke-width', _via_settings.ui.image_grid.rshape_stroke_width);
9218
9219
document.getElementById('image_grid_content_rshape').appendChild(r2_svg);
9220
}
9221
});
9222
}
9223
9224
function image_grid_image_size_increase() {
9225
var new_img_height = _via_settings.ui.image_grid.img_height + VIA_IMAGE_GRID_IMG_HEIGHT_CHANGE;
9226
_via_settings.ui.image_grid.img_height = new_img_height;
9227
9228
_via_image_grid_page_last_index = null;
9229
image_grid_update();
9230
}
9231
9232
function image_grid_image_size_decrease() {
9233
var new_img_height = _via_settings.ui.image_grid.img_height - VIA_IMAGE_GRID_IMG_HEIGHT_CHANGE;
9234
if ( new_img_height > 1 ) {
9235
_via_settings.ui.image_grid.img_height = new_img_height;
9236
_via_image_grid_page_last_index = null;
9237
image_grid_update();
9238
}
9239
}
9240
9241
function image_grid_image_size_reset() {
9242
var new_img_height = _via_settings.ui.image_grid.img_height;
9243
if ( new_img_height > 1 ) {
9244
_via_settings.ui.image_grid.img_height = new_img_height;
9245
_via_image_grid_page_last_index = null;
9246
image_grid_update();
9247
}
9248
}
9249
9250
function image_grid_mousedown_handler(e) {
9251
e.preventDefault();
9252
_via_image_grid_mousedown_img_index = image_grid_parse_html_img_id(e.target.id);
9253
}
9254
9255
function image_grid_mouseup_handler(e) {
9256
e.preventDefault();
9257
var last_mouseup_img_index = _via_image_grid_mouseup_img_index;
9258
_via_image_grid_mouseup_img_index = image_grid_parse_html_img_id(e.target.id);
9259
if ( isNaN(_via_image_grid_mousedown_img_index) ||
9260
isNaN(_via_image_grid_mouseup_img_index)) {
9261
last_mouseup_img_index = _via_image_grid_img_index_list[0];
9262
image_grid_group_select_none();
9263
return;
9264
}
9265
9266
var mousedown_img_arr_index = _via_image_grid_img_index_list.indexOf(_via_image_grid_mousedown_img_index);
9267
var mouseup_img_arr_index = _via_image_grid_img_index_list.indexOf(_via_image_grid_mouseup_img_index);
9268
9269
var start = -1;
9270
var end = -1;
9271
var operation = 'select'; // {'select', 'unselect', 'toggle'}
9272
if ( mousedown_img_arr_index === mouseup_img_arr_index ) {
9273
if ( e.shiftKey ) {
9274
// select all elements until this element
9275
start = _via_image_grid_img_index_list.indexOf(last_mouseup_img_index) + 1;
9276
end = mouseup_img_arr_index + 1;
9277
} else {
9278
// toggle selection of single image
9279
start = mousedown_img_arr_index;
9280
end = start + 1;
9281
operation = 'toggle';
9282
}
9283
} else {
9284
if ( mousedown_img_arr_index < mouseup_img_arr_index ) {
9285
start = mousedown_img_arr_index;
9286
end = mouseup_img_arr_index + 1;
9287
} else {
9288
start = mouseup_img_arr_index + 1;
9289
end = mousedown_img_arr_index;
9290
}
9291
operation = 'toggle';
9292
}
9293
9294
if ( start > end ) {
9295
return;
9296
}
9297
9298
var i, img_index;
9299
for ( i = start; i < end; ++i ) {
9300
img_index = _via_image_grid_img_index_list[i];
9301
image_grid_update_img_select(img_index, operation);
9302
}
9303
image_grid_update_sel_count_html();
9304
annotation_editor_update_content();
9305
}
9306
9307
function image_grid_update_sel_count_html() {
9308
document.getElementById('image_grid_group_by_sel_img_count').innerHTML = _via_image_grid_selected_img_index_list.length;
9309
}
9310
9311
// state \in {'select', 'unselect', 'toggle'}
9312
function image_grid_update_img_select(img_index, state) {
9313
var html_img_id = image_grid_get_html_img_id(img_index);
9314
var is_selected = ( _via_image_grid_selected_img_index_list.indexOf(img_index) !== -1 );
9315
if (state === 'toggle' ) {
9316
if ( is_selected ) {
9317
state = 'unselect';
9318
} else {
9319
state = 'select';
9320
}
9321
}
9322
9323
switch(state) {
9324
case 'select':
9325
if ( ! is_selected ) {
9326
_via_image_grid_selected_img_index_list.push(img_index);
9327
}
9328
if ( _via_image_grid_visible_img_index_list.indexOf(img_index) !== -1 ) {
9329
document.getElementById(html_img_id).classList.remove('not_sel');
9330
}
9331
break;
9332
case 'unselect':
9333
if ( is_selected ) {
9334
var arr_index = _via_image_grid_selected_img_index_list.indexOf(img_index);
9335
_via_image_grid_selected_img_index_list.splice(arr_index, 1);
9336
}
9337
if ( _via_image_grid_visible_img_index_list.indexOf(img_index) !== -1 ) {
9338
document.getElementById(html_img_id).classList.add('not_sel');
9339
}
9340
break;
9341
}
9342
}
9343
9344
function image_grid_group_select_all() {
9345
image_grid_group_set_all_selection_state('select');
9346
image_grid_update_sel_count_html();
9347
annotation_editor_update_content();
9348
show_message('Selected all images in the current group');
9349
}
9350
9351
function image_grid_group_select_none() {
9352
image_grid_group_set_all_selection_state('unselect');
9353
image_grid_update_sel_count_html();
9354
annotation_editor_update_content();
9355
show_message('Removed selection of all images in the current group');
9356
}
9357
9358
function image_grid_group_set_all_selection_state(state) {
9359
var i, img_index;
9360
for ( i = 0; i < _via_image_grid_img_index_list.length; ++i ) {
9361
img_index = _via_image_grid_img_index_list[i];
9362
image_grid_update_img_select(img_index, state);
9363
}
9364
}
9365
9366
function image_grid_group_toggle_select_all() {
9367
if ( _via_image_grid_selected_img_index_list.length === _via_image_grid_img_index_list.length ) {
9368
image_grid_group_select_none();
9369
} else {
9370
image_grid_group_select_all();
9371
}
9372
}
9373
9374
function image_grid_parse_html_img_id(html_img_id) {
9375
var img_index = html_img_id.substr(2);
9376
return parseInt(img_index);
9377
}
9378
9379
function image_grid_get_html_img_id(img_index) {
9380
return 'im' + img_index;
9381
}
9382
9383
function image_grid_parse_html_region_id(html_region_id) {
9384
var chunks = html_region_id.split('_');
9385
if ( chunks.length === 2 ) {
9386
var img_index = parseInt(chunks[0].substr(2));
9387
var region_id = parseInt(chunks[1].substr(2));
9388
return {'img_index':img_index, 'region_id':region_id};
9389
} else {
9390
console.log('image_grid_parse_html_region_id(): invalid html_region_id');
9391
return {};
9392
}
9393
}
9394
9395
function image_grid_get_html_region_id(img_index, region_id) {
9396
return image_grid_get_html_img_id(img_index) + '_rs' + region_id;
9397
}
9398
9399
function image_grid_dblclick_handler(e) {
9400
_via_image_index = image_grid_parse_html_img_id(e.target.id);
9401
show_single_image_view();
9402
}
9403
9404
function image_grid_toolbar_update_group_by_select() {
9405
var p = document.getElementById('image_grid_toolbar_group_by_select');
9406
p.innerHTML = '';
9407
9408
var o = document.createElement('option');
9409
o.setAttribute('value', '');
9410
o.setAttribute('selected', 'selected');
9411
o.innerHTML = 'All Images';
9412
p.appendChild(o);
9413
9414
// add file attributes
9415
var fattr;
9416
for ( fattr in _via_attributes['file'] ) {
9417
var o = document.createElement('option');
9418
o.setAttribute('value', image_grid_toolbar_group_by_select_get_html_id('file', fattr));
9419
o.innerHTML = '[file] ' + fattr;
9420
p.appendChild(o);
9421
}
9422
9423
// add region attributes
9424
var rattr;
9425
for ( rattr in _via_attributes['region'] ) {
9426
var o = document.createElement('option');
9427
o.setAttribute('value', image_grid_toolbar_group_by_select_get_html_id('region', rattr));
9428
o.innerHTML = '[region] ' + rattr;
9429
p.appendChild(o);
9430
}
9431
}
9432
9433
function image_grid_toolbar_group_by_select_get_html_id(type, name) {
9434
if ( type === 'file' ) {
9435
return 'f_' + name;
9436
}
9437
if ( type === 'region' ) {
9438
return 'r_' + name;
9439
}
9440
}
9441
9442
function image_grid_toolbar_group_by_select_parse_html_id(id) {
9443
if ( id.startsWith('f_') ) {
9444
return { 'attr_type':'file', 'attr_name':id.substr(2) };
9445
}
9446
if ( id.startsWith('r_') ) {
9447
return { 'attr_type':'region', 'attr_name':id.substr(2) };
9448
}
9449
}
9450
9451
function image_grid_toolbar_onchange_group_by_select(p) {
9452
if ( p.options[p.selectedIndex].value === '' ) {
9453
image_grid_show_all_project_images();
9454
return;
9455
}
9456
9457
var v = image_grid_toolbar_group_by_select_parse_html_id( p.options[p.selectedIndex].value );
9458
var attr_type = v.attr_type;
9459
var attr_name = v.attr_name;
9460
image_grid_group_by(attr_type, attr_name);
9461
9462
image_grid_group_by_select_set_disabled(attr_type, attr_name, true);
9463
p.blur(); // to avoid adding new groups using keyboard keys as dropdown is still in focus
9464
}
9465
9466
function image_grid_remove_html_group_panel(d) {
9467
var p = document.getElementById('group_toolbar_' + d.group_index);
9468
document.getElementById('image_grid_group_panel').removeChild(p);
9469
}
9470
9471
function image_grid_add_html_group_panel(d) {
9472
var p = document.createElement('div');
9473
p.classList.add('image_grid_group_toolbar');
9474
p.setAttribute('id', 'group_toolbar_' + d.group_index);
9475
9476
var del = document.createElement('span');
9477
del.classList.add('text_button');
9478
del.setAttribute('onclick', 'image_grid_remove_group_by(this)');
9479
del.innerHTML = '&times;';
9480
p.appendChild(del);
9481
9482
var prev = document.createElement('button');
9483
prev.innerHTML = '<';
9484
prev.setAttribute('value', d.group_index);
9485
prev.setAttribute('onclick', 'image_grid_group_prev(this)');
9486
p.appendChild(prev);
9487
9488
var sel = document.createElement('select');
9489
sel.setAttribute('id', image_grid_group_select_get_html_id(d.group_index));
9490
sel.setAttribute('onchange', 'image_grid_group_value_onchange(this)');
9491
var i, value;
9492
var n = d.values.length;
9493
var current_value = d.values[ d.current_value_index ];
9494
for ( i = 0; i < n; ++i ) {
9495
value = d.values[i];
9496
var o = document.createElement('option');
9497
o.setAttribute('value', value);
9498
o.innerHTML = (i+1) + '/' + n + ': ' + d.name + ' = ' + value;
9499
if ( value === current_value ) {
9500
o.setAttribute('selected', 'selected');
9501
}
9502
9503
sel.appendChild(o);
9504
}
9505
p.appendChild(sel);
9506
9507
var next = document.createElement('button');
9508
next.innerHTML = '>';
9509
next.setAttribute('value', d.group_index);
9510
next.setAttribute('onclick', 'image_grid_group_next(this)');
9511
p.appendChild(next);
9512
9513
document.getElementById('image_grid_group_panel').appendChild(p);
9514
}
9515
9516
function image_grid_group_panel_set_selected_value(group_index) {
9517
var sel = document.getElementById(image_grid_group_select_get_html_id(group_index));
9518
sel.selectedIndex = _via_image_grid_group_var[group_index].current_value_index;
9519
}
9520
9521
function image_grid_group_panel_set_options(group_index) {
9522
var sel = document.getElementById(image_grid_group_select_get_html_id(group_index));
9523
sel.innerHTML = '';
9524
9525
var i, value;
9526
var n = _via_image_grid_group_var[group_index].values.length;
9527
var name = _via_image_grid_group_var[group_index].name;
9528
var current_value = _via_image_grid_group_var[group_index].values[ _via_image_grid_group_var[group_index].current_value_index ]
9529
for ( i = 0; i < n; ++i ) {
9530
value = _via_image_grid_group_var[group_index].values[i];
9531
var o = document.createElement('option');
9532
o.setAttribute('value', value);
9533
o.innerHTML = (i+1) + '/' + n + ': ' + name + ' = ' + value;
9534
if ( value === current_value ) {
9535
o.setAttribute('selected', 'selected');
9536
}
9537
sel.appendChild(o);
9538
}
9539
}
9540
9541
function image_grid_group_select_get_html_id(group_index) {
9542
return 'gi_' + group_index;
9543
}
9544
9545
function image_grid_group_select_parse_html_id(id) {
9546
return parseInt(id.substr(3));
9547
}
9548
9549
function image_grid_group_by_select_set_disabled(type, name, is_disabled) {
9550
var p = document.getElementById('image_grid_toolbar_group_by_select');
9551
var sel_option_value = image_grid_toolbar_group_by_select_get_html_id(type, name);
9552
9553
var n = p.options.length;
9554
var option_value;
9555
var i;
9556
for ( i = 0; i < n; ++i ) {
9557
if ( sel_option_value === p.options[i].value ) {
9558
if ( is_disabled ) {
9559
p.options[i].setAttribute('disabled', 'disabled');
9560
} else {
9561
p.options[i].removeAttribute('disabled');
9562
}
9563
break;
9564
}
9565
}
9566
}
9567
9568
function image_grid_remove_group_by(p) {
9569
var prefix = 'group_toolbar_';
9570
var group_index = parseInt( p.parentNode.id.substr( prefix.length ) );
9571
9572
if ( group_index === 0 ) {
9573
image_grid_show_all_project_images();
9574
} else {
9575
// merge all groups that are child of group_index
9576
image_grid_group_by_merge(_via_image_grid_group, 0, group_index);
9577
9578
var n = _via_image_grid_group_var.length;
9579
var p = document.getElementById('image_grid_group_panel');
9580
var group_panel_id;
9581
var i;
9582
for ( i = group_index; i < n; ++i ) {
9583
image_grid_remove_html_group_panel( _via_image_grid_group_var[i] );
9584
image_grid_group_by_select_set_disabled( _via_image_grid_group_var[i].type,
9585
_via_image_grid_group_var[i].name,
9586
false);
9587
}
9588
_via_image_grid_group_var.splice(group_index);
9589
9590
image_grid_set_content_to_current_group();
9591
}
9592
}
9593
9594
function image_grid_group_by(type, name) {
9595
if ( Object.keys(_via_image_grid_group).length === 0 ) {
9596
// first group
9597
var img_index_array = [];
9598
var n = _via_img_fn_list_img_index_list.length;
9599
var i;
9600
for ( i = 0; i < n; ++i ) {
9601
img_index_array.push( _via_img_fn_list_img_index_list[i] );
9602
}
9603
9604
_via_image_grid_group = image_grid_split_array_to_group(img_index_array, type, name);
9605
var new_group_values = Object.keys(_via_image_grid_group);
9606
_via_image_grid_group_var = [];
9607
_via_image_grid_group_var.push( { 'type':type, 'name':name, 'current_value_index':0, 'values':new_group_values, 'group_index':0 } );
9608
9609
image_grid_add_html_group_panel(_via_image_grid_group_var[0]);
9610
} else {
9611
image_grid_group_split_all_arrays( _via_image_grid_group, type, name );
9612
9613
var i, n, value;
9614
var current_group_value = _via_image_grid_group;
9615
n = _via_image_grid_group_var.length;
9616
9617
for ( i = 0; i < n; ++i ) {
9618
value = _via_image_grid_group_var[i].values[ _via_image_grid_group_var[i].current_value_index ];
9619
current_group_value = current_group_value[ value ];
9620
}
9621
var new_group_values = Object.keys(current_group_value);
9622
var group_var_index = _via_image_grid_group_var.length;
9623
_via_image_grid_group_var.push( { 'type':type, 'name':name, 'current_value_index':0, 'values':new_group_values, 'group_index':group_var_index } );
9624
image_grid_add_html_group_panel( _via_image_grid_group_var[group_var_index] );
9625
}
9626
9627
image_grid_set_content_to_current_group();
9628
}
9629
9630
function image_grid_group_by_merge(group, current_level, target_level) {
9631
var child_value;
9632
var group_data = [];
9633
if ( current_level === target_level ) {
9634
return image_grid_group_by_collapse(group);
9635
} else {
9636
for ( child_value in group ) {
9637
group[child_value] = image_grid_group_by_merge(group[child_value], current_level + 1, target_level);
9638
}
9639
}
9640
}
9641
9642
function image_grid_group_by_collapse(group) {
9643
var child_value;
9644
var child_collapsed_value;
9645
var group_data = [];
9646
for ( child_value in group ) {
9647
if ( Array.isArray(group[child_value]) ) {
9648
group_data = group_data.concat(group[child_value]);
9649
} else {
9650
group_data = group_data.concat(image_grid_group_by_collapse(group[child_value]));
9651
}
9652
}
9653
return group_data;
9654
}
9655
9656
// recursively collapse all arrays to list
9657
function image_grid_group_split_all_arrays(group, type, name) {
9658
if ( Array.isArray(group) ) {
9659
return image_grid_split_array_to_group(group, type, name);
9660
} else {
9661
var group_value;
9662
for ( group_value in group ) {
9663
if ( Array.isArray( group[group_value] ) ) {
9664
group[group_value] = image_grid_split_array_to_group(group[group_value], type, name);
9665
} else {
9666
image_grid_group_split_all_arrays(group[group_value], type, name);
9667
}
9668
}
9669
}
9670
}
9671
9672
function image_grid_split_array_to_group(img_index_array, attr_type, attr_name) {
9673
var grp = {};
9674
var img_index, img_id, i;
9675
var n = img_index_array.length;
9676
var attr_value;
9677
9678
switch(attr_type) {
9679
case 'file':
9680
for ( i = 0; i < n; ++i ) {
9681
img_index = img_index_array[i];
9682
img_id = _via_image_id_list[img_index];
9683
if ( _via_img_metadata[img_id].file_attributes.hasOwnProperty(attr_name) ) {
9684
attr_value = _via_img_metadata[img_id].file_attributes[attr_name];
9685
9686
if ( ! grp.hasOwnProperty(attr_value) ) {
9687
grp[attr_value] = [];
9688
}
9689
grp[attr_value].push(img_index);
9690
}
9691
}
9692
break;
9693
case 'region':
9694
var j;
9695
var region_count;
9696
for ( i = 0; i < n; ++i ) {
9697
img_index = img_index_array[i];
9698
img_id = _via_image_id_list[img_index];
9699
region_count = _via_img_metadata[img_id].regions.length;
9700
for ( j = 0; j < region_count; ++j ) {
9701
if ( _via_img_metadata[img_id].regions[j].region_attributes.hasOwnProperty(attr_name) ) {
9702
attr_value = _via_img_metadata[img_id].regions[j].region_attributes[attr_name];
9703
9704
if ( ! grp.hasOwnProperty(attr_value) ) {
9705
grp[attr_value] = [];
9706
}
9707
if ( grp[attr_value].includes(img_index) ) {
9708
continue;
9709
} else {
9710
grp[attr_value].push(img_index);
9711
}
9712
}
9713
}
9714
}
9715
break;
9716
}
9717
return grp;
9718
}
9719
9720
function image_grid_group_next(p) {
9721
var group_index = parseInt( p.value );
9722
var group_value_list = _via_image_grid_group_var[group_index].values;
9723
var n = group_value_list.length;
9724
var current_index = _via_image_grid_group_var[group_index].current_value_index;
9725
var next_index = current_index + 1;
9726
if ( next_index >= n ) {
9727
if ( group_index === 0 ) {
9728
next_index = next_index - n;
9729
image_grid_jump_to_group(group_index, next_index);
9730
} else {
9731
// next of parent group
9732
var parent_group_index = group_index - 1;
9733
var parent_current_val_index = _via_image_grid_group_var[parent_group_index].current_value_index;
9734
var parent_next_val_index = parent_current_val_index + 1;
9735
while ( parent_group_index !== 0 ) {
9736
if ( parent_next_val_index >= _via_image_grid_group_var[parent_group_index].values.length ) {
9737
parent_group_index = group_index - 1;
9738
parent_current_val_index = _via_image_grid_group_var[parent_group_index].current_value_index;
9739
parent_next_val_index = parent_current_val_index + 1;
9740
} else {
9741
break;
9742
}
9743
}
9744
9745
if ( parent_next_val_index >= _via_image_grid_group_var[parent_group_index].values.length ) {
9746
parent_next_val_index = 0;
9747
}
9748
image_grid_jump_to_group(parent_group_index, parent_next_val_index);
9749
}
9750
} else {
9751
image_grid_jump_to_group(group_index, next_index);
9752
}
9753
image_grid_set_content_to_current_group();
9754
}
9755
9756
function image_grid_group_prev(p) {
9757
var group_index = parseInt( p.value );
9758
var group_value_list = _via_image_grid_group_var[group_index].values;
9759
var n = group_value_list.length;
9760
var current_index = _via_image_grid_group_var[group_index].current_value_index;
9761
var prev_index = current_index - 1;
9762
if ( prev_index < 0 ) {
9763
if ( group_index === 0 ) {
9764
prev_index = n + prev_index;
9765
image_grid_jump_to_group(group_index, prev_index);
9766
} else {
9767
// prev of parent group
9768
var parent_group_index = group_index - 1;
9769
var parent_current_val_index = _via_image_grid_group_var[parent_group_index].current_value_index;
9770
var parent_prev_val_index = parent_current_val_index - 1;
9771
while ( parent_group_index !== 0 ) {
9772
if ( parent_prev_val_index < 0 ) {
9773
parent_group_index = group_index - 1;
9774
parent_current_val_index = _via_image_grid_group_var[parent_group_index].current_value_index;
9775
parent_prev_val_index = parent_current_val_index - 1;
9776
} else {
9777
break;
9778
}
9779
}
9780
9781
if ( parent_prev_val_index < 0 ) {
9782
parent_prev_val_index = _via_image_grid_group_var[parent_group_index].values.length - 1;
9783
}
9784
image_grid_jump_to_group(parent_group_index, parent_prev_val_index);
9785
}
9786
} else {
9787
image_grid_jump_to_group(group_index, prev_index);
9788
}
9789
image_grid_set_content_to_current_group();
9790
}
9791
9792
9793
function image_grid_group_value_onchange(p) {
9794
var group_index = image_grid_group_select_parse_html_id(p.id);
9795
image_grid_jump_to_group(group_index, p.selectedIndex);
9796
image_grid_set_content_to_current_group();
9797
}
9798
9799
function image_grid_jump_to_group(group_index, value_index) {
9800
var n = _via_image_grid_group_var[group_index].values.length;
9801
if ( value_index >=n || value_index < 0 ) {
9802
return;
9803
}
9804
9805
_via_image_grid_group_var[group_index].current_value_index = value_index;
9806
image_grid_group_panel_set_selected_value( group_index );
9807
9808
// reset the value of lower groups
9809
var i, value;
9810
if ( group_index + 1 < _via_image_grid_group_var.length ) {
9811
var e = _via_image_grid_group;
9812
for ( i = 0; i <= group_index; ++i ) {
9813
value = _via_image_grid_group_var[i].values[ _via_image_grid_group_var[i].current_value_index ];
9814
e = e[ value ];
9815
}
9816
9817
for ( i = group_index + 1; i < _via_image_grid_group_var.length; ++i ) {
9818
_via_image_grid_group_var[i].values = Object.keys(e);
9819
if ( _via_image_grid_group_var[i].values.length === 0 ) {
9820
_via_image_grid_group_var[i].current_value_index = -1;
9821
_via_image_grid_group_var.splice(i);
9822
image_grid_group_panel_set_options(i);
9823
break;
9824
} else {
9825
_via_image_grid_group_var[i].current_value_index = 0;
9826
value = _via_image_grid_group_var[i].values[0]
9827
e = e[value];
9828
image_grid_group_panel_set_options(i);
9829
}
9830
}
9831
}
9832
}
9833
9834
function image_grid_set_content_to_current_group() {
9835
var n = _via_image_grid_group_var.length;
9836
9837
if ( n === 0 ) {
9838
image_grid_show_all_project_images();
9839
} else {
9840
var group_img_index_list = [];
9841
var img_index_list = _via_image_grid_group;
9842
var i, n, value, current_value_index;
9843
for ( i = 0; i < n; ++i ) {
9844
value = _via_image_grid_group_var[i].values[ _via_image_grid_group_var[i].current_value_index ];
9845
img_index_list = img_index_list[ value ];
9846
}
9847
9848
if ( Array.isArray(img_index_list) ) {
9849
image_grid_set_content(img_index_list);
9850
} else {
9851
console.log('Error: image_grid_set_content_to_current_group(): expected array while got ' + typeof(img_index_list));
9852
}
9853
}
9854
}
9855
9856
function image_grid_page_next() {
9857
_via_image_grid_stack_prev_page.push(_via_image_grid_page_first_index);
9858
_via_image_grid_page_first_index = _via_image_grid_page_last_index;
9859
9860
image_grid_clear_content();
9861
_via_image_grid_load_ongoing = true;
9862
image_grid_page_nav_show_cancel();
9863
image_grid_content_append_img( _via_image_grid_page_first_index );
9864
}
9865
9866
function image_grid_page_prev() {
9867
_via_image_grid_page_first_index = _via_image_grid_stack_prev_page.pop();
9868
_via_image_grid_page_last_index = -1;
9869
9870
image_grid_clear_content();
9871
_via_image_grid_load_ongoing = true;
9872
image_grid_page_nav_show_cancel();
9873
image_grid_content_append_img( _via_image_grid_page_first_index );
9874
}
9875
9876
function image_grid_page_nav_show_cancel() {
9877
var info = document.getElementById('image_grid_nav');
9878
var html = [];
9879
html.push('<span>Loading images ... </span>');
9880
html.push('<span class="text_button" onclick="image_grid_cancel_load_ongoing()">Cancel</span>');
9881
info.innerHTML = html.join('');
9882
}
9883
9884
function image_grid_cancel_load_ongoing() {
9885
_via_image_grid_load_ongoing = false;
9886
}
9887
9888
9889
// everything to do with image zooming
9890
function image_zoom_init() {
9891
9892
}
9893
9894
//
9895
// hooks for sub-modules
9896
// implemented by sub-modules
9897
//
9898
//function _via_hook_next_image() {}
9899
//function _via_hook_prev_image() {}
9900
9901
9902
////////////////////////////////////////////////////////////////////////////////
9903
//
9904
// Code borrowed from via2 branch
9905
// - in future, the <canvas> based reigon shape drawing will be replaced by <svg>
9906
// because svg allows independent manipulation of individual regions without
9907
// requiring to clear the canvas every time some region is updated.
9908
//
9909
////////////////////////////////////////////////////////////////////////////////
9910
9911
////////////////////////////////////////////////////////////////////////////////
9912
//
9913
// @file _via_region.js
9914
// @description Implementation of region shapes like rectangle, circle, etc.
9915
// @author Abhishek Dutta <adutta@robots.ox.ac.uk>
9916
// @date 17 June 2017
9917
//
9918
////////////////////////////////////////////////////////////////////////////////
9919
9920
function _via_region( shape, id, data_img_space, view_scale_factor, view_offset_x, view_offset_y) {
9921
// Note the following terminology:
9922
// view space :
9923
// - corresponds to the x-y plane on which the scaled version of original image is shown to the user
9924
// - all the region query operations like is_inside(), is_on_edge(), etc are performed in view space
9925
// - all svg draw operations like get_svg() are also in view space
9926
//
9927
// image space :
9928
// - corresponds to the x-y plane which corresponds to the spatial space of the original image
9929
// - region save, export, git push operations are performed in image space
9930
// - to avoid any rounding issues (caused by floating scale factor),
9931
// * user drawn regions in view space is first converted to image space
9932
// * this region in image space is now used to initialize region in view space
9933
//
9934
// The two spaces are related by _via_model.now.tform.scale which is computed by the method
9935
// _via_ctrl.compute_view_panel_to_nowfile_tform()
9936
// and applied as follows:
9937
// x coordinate in image space = scale_factor * x coordinate in view space
9938
//
9939
// shape : {rect, circle, ellipse, line, polyline, polygon, point}
9940
// id : unique region-id
9941
// d[] : (in view space) data whose meaning depend on region shape as follows:
9942
// rect : d[x1,y1,x2,y2] or d[corner1_x, corner1_y, corner2_x, corner2_y]
9943
// circle : d[x1,y1,x2,y2] or d[center_x, center_y, circumference_x, circumference_y]
9944
// ellipse : d[x1,y1,x2,y2,transform]
9945
// line : d[x1,y1,x2,y2]
9946
// polyline : d[x1,y1,...,xn,yn]
9947
// polygon : d[x1,y1,...,xn,yn]
9948
// point : d[cx,cy]
9949
// scale_factor : for conversion from view space to image space
9950
//
9951
// Note: no svg data are stored with prefix "_". For example: _scale_factor, _x2
9952
this.shape = shape;
9953
this.id = id;
9954
this.scale_factor = view_scale_factor;
9955
this.offset_x = view_offset_x;
9956
this.offset_y = view_offset_y;
9957
this.recompute_svg = false;
9958
this.attributes = {};
9959
9960
var n = data_img_space.length;
9961
var i;
9962
this.dview = new Array(n);
9963
this.dimg = new Array(n);
9964
9965
if ( n !== 0 ) {
9966
// IMPORTANT:
9967
// to avoid any rounding issues (caused by floating scale factor), we stick to
9968
// the principal that image space coordinates are the ground truth for every region.
9969
// Hence, we proceed as:
9970
// * user drawn regions in view space is first converted to image space
9971
// * this region in image space is now used to initialize region in view space
9972
for ( i = 0; i < n; i++ ) {
9973
this.dimg[i] = data_img_space[i];
9974
9975
var offset = this.offset_x;
9976
if ( i % 2 !== 0 ) {
9977
// y coordinate
9978
offset = this.offset_y;
9979
}
9980
this.dview[i] = Math.round( this.dimg[i] * this.scale_factor ) + offset;
9981
}
9982
}
9983
9984
// set svg attributes for each shape
9985
switch( this.shape ) {
9986
case "rect":
9987
_via_region_rect.call( this );
9988
this.svg_attributes = ['x', 'y', 'width', 'height'];
9989
break;
9990
case "circle":
9991
_via_region_circle.call( this );
9992
this.svg_attributes = ['cx', 'cy', 'r'];
9993
break;
9994
case "ellipse":
9995
_via_region_ellipse.call( this );
9996
this.svg_attributes = ['cx', 'cy', 'rx', 'ry','transform'];
9997
break;
9998
case "line":
9999
_via_region_line.call( this );
10000
this.svg_attributes = ['x1', 'y1', 'x2', 'y2'];
10001
break;
10002
case "polyline":
10003
_via_region_polyline.call( this );
10004
this.svg_attributes = ['points'];
10005
break;
10006
case "polygon":
10007
_via_region_polygon.call( this );
10008
this.svg_attributes = ['points'];
10009
break;
10010
case "point":
10011
_via_region_point.call( this );
10012
// point is a special circle with minimal radius required for visualization
10013
this.shape = 'circle';
10014
this.svg_attributes = ['cx', 'cy', 'r'];
10015
break;
10016
}
10017
10018
this.initialize();
10019
}
10020
10021
10022
_via_region.prototype.prepare_svg_element = function() {
10023
var _VIA_SVG_NS = "http://www.w3.org/2000/svg";
10024
this.svg_element = document.createElementNS(_VIA_SVG_NS, this.shape);
10025
this.svg_string = '<' + this.shape;
10026
this.svg_element.setAttributeNS(null, 'id', this.id);
10027
10028
var n = this.svg_attributes.length;
10029
for ( var i = 0; i < n; i++ ) {
10030
this.svg_element.setAttributeNS(null, this.svg_attributes[i], this[this.svg_attributes[i]]);
10031
this.svg_string += ' ' + this.svg_attributes[i] + '="' + this[this.svg_attributes[i]] + '"';
10032
}
10033
this.svg_string += '/>';
10034
}
10035
10036
_via_region.prototype.get_svg_element = function() {
10037
if ( this.recompute_svg ) {
10038
this.prepare_svg_element();
10039
this.recompute_svg = false;
10040
}
10041
return this.svg_element;
10042
}
10043
10044
_via_region.prototype.get_svg_string = function() {
10045
if ( this.recompute_svg ) {
10046
this.prepare_svg_element();
10047
this.recompute_svg = false;
10048
}
10049
return this.svg_string;
10050
}
10051
10052
///
10053
/// Region shape : rectangle
10054
///
10055
function _via_region_rect() {
10056
this.is_inside = _via_region_rect.prototype.is_inside;
10057
this.is_on_edge = _via_region_rect.prototype.is_on_edge;
10058
this.move = _via_region_rect.prototype.move;
10059
this.resize = _via_region_rect.prototype.resize;
10060
this.initialize = _via_region_rect.prototype.initialize;
10061
this.dist_to_nearest_edge = _via_region_rect.prototype.dist_to_nearest_edge;
10062
}
10063
10064
_via_region_rect.prototype.initialize = function() {
10065
// ensure that this.(x,y) corresponds to top-left corner of rectangle
10066
// Note: this.(x2,y2) is defined for convenience in calculations
10067
if ( this.dview[0] < this.dview[2] ) {
10068
this.x = this.dview[0];
10069
this.x2 = this.dview[2];
10070
} else {
10071
this.x = this.dview[2];
10072
this.x2 = this.dview[0];
10073
}
10074
if ( this.dview[1] < this.dview[3] ) {
10075
this.y = this.dview[1];
10076
this.y2 = this.dview[3];
10077
} else {
10078
this.y = this.dview[3];
10079
this.y2 = this.dview[1];
10080
}
10081
this.width = this.x2 - this.x;
10082
this.height = this.y2 - this.y;
10083
this.recompute_svg = true;
10084
}
10085
10086
///
10087
/// Region shape : circle
10088
///
10089
function _via_region_circle() {
10090
this.is_inside = _via_region_circle.prototype.is_inside;
10091
this.is_on_edge = _via_region_circle.prototype.is_on_edge;
10092
this.move = _via_region_circle.prototype.move;
10093
this.resize = _via_region_circle.prototype.resize;
10094
this.initialize = _via_region_circle.prototype.initialize;
10095
this.dist_to_nearest_edge = _via_region_circle.prototype.dist_to_nearest_edge;
10096
}
10097
10098
_via_region_circle.prototype.initialize = function() {
10099
this.cx = this.dview[0];
10100
this.cy = this.dview[1];
10101
var dx = this.dview[2] - this.dview[0];
10102
var dy = this.dview[3] - this.dview[1];
10103
this.r = Math.round( Math.sqrt(dx * dx + dy * dy) );
10104
this.r2 = this.r * this.r;
10105
this.recompute_svg = true;
10106
}
10107
10108
10109
///
10110
/// Region shape : ellipse
10111
///
10112
function _via_region_ellipse() {
10113
this.is_inside = _via_region_ellipse.prototype.is_inside;
10114
this.is_on_edge = _via_region_ellipse.prototype.is_on_edge;
10115
this.move = _via_region_ellipse.prototype.move;
10116
this.resize = _via_region_ellipse.prototype.resize;
10117
this.initialize = _via_region_ellipse.prototype.initialize;
10118
this.dist_to_nearest_edge = _via_region_ellipse.prototype.dist_to_nearest_edge;
10119
}
10120
10121
_via_region_ellipse.prototype.initialize = function() {
10122
this.cx = this.dview[0];
10123
this.cy = this.dview[1];
10124
this.rx = Math.abs(this.dview[2] - this.dview[0]);
10125
this.ry = Math.abs(this.dview[3] - this.dview[1]);
10126
10127
this.inv_rx2 = 1 / (this.rx * this.rx);
10128
this.inv_ry2 = 1 / (this.ry * this.ry);
10129
10130
this.recompute_svg = true;
10131
}
10132
10133
10134
10135
///
10136
/// Region shape : line
10137
///
10138
function _via_region_line() {
10139
this.is_inside = _via_region_line.prototype.is_inside;
10140
this.is_on_edge = _via_region_line.prototype.is_on_edge;
10141
this.move = _via_region_line.prototype.move;
10142
this.resize = _via_region_line.prototype.resize;
10143
this.initialize = _via_region_line.prototype.initialize;
10144
this.dist_to_nearest_edge = _via_region_line.prototype.dist_to_nearest_edge;
10145
}
10146
10147
_via_region_line.prototype.initialize = function() {
10148
this.x1 = this.dview[0];
10149
this.y1 = this.dview[1];
10150
this.x2 = this.dview[2];
10151
this.y2 = this.dview[3];
10152
this.dx = this.x1 - this.x2;
10153
this.dy = this.y1 - this.y2;
10154
this.mconst = (this.x1 * this.y2) - (this.x2 * this.y1);
10155
10156
this.recompute_svg = true;
10157
}
10158
10159
10160
///
10161
/// Region shape : polyline
10162
///
10163
function _via_region_polyline() {
10164
this.is_inside = _via_region_polyline.prototype.is_inside;
10165
this.is_on_edge = _via_region_polyline.prototype.is_on_edge;
10166
this.move = _via_region_polyline.prototype.move;
10167
this.resize = _via_region_polyline.prototype.resize;
10168
this.initialize = _via_region_polyline.prototype.initialize;
10169
this.dist_to_nearest_edge = _via_region_polyline.prototype.dist_to_nearest_edge;
10170
}
10171
10172
_via_region_polyline.prototype.initialize = function() {
10173
var n = this.dview.length;
10174
var points = new Array(n/2);
10175
var points_index = 0;
10176
for ( var i = 0; i < n; i += 2 ) {
10177
points[points_index] = ( this.dview[i] + ' ' + this.dview[i+1] );
10178
points_index++;
10179
}
10180
this.points = points.join(',');
10181
this.recompute_svg = true;
10182
}
10183
10184
10185
///
10186
/// Region shape : polygon
10187
///
10188
function _via_region_polygon() {
10189
this.is_inside = _via_region_polygon.prototype.is_inside;
10190
this.is_on_edge = _via_region_polygon.prototype.is_on_edge;
10191
this.move = _via_region_polygon.prototype.move;
10192
this.resize = _via_region_polygon.prototype.resize;
10193
this.initialize = _via_region_polygon.prototype.initialize;
10194
this.dist_to_nearest_edge = _via_region_polygon.prototype.dist_to_nearest_edge;
10195
}
10196
10197
_via_region_polygon.prototype.initialize = function() {
10198
var n = this.dview.length;
10199
var points = new Array(n/2);
10200
var points_index = 0;
10201
for ( var i = 0; i < n; i += 2 ) {
10202
points[points_index] = ( this.dview[i] + ' ' + this.dview[i+1] );
10203
points_index++;
10204
}
10205
this.points = points.join(',');
10206
this.recompute_svg = true;
10207
}
10208
10209
10210
///
10211
/// Region shape : point
10212
///
10213
function _via_region_point() {
10214
this.is_inside = _via_region_point.prototype.is_inside;
10215
this.is_on_edge = _via_region_point.prototype.is_on_edge;
10216
this.move = _via_region_point.prototype.move;
10217
this.resize = _via_region_point.prototype.resize
10218
this.initialize = _via_region_point.prototype.initialize;
10219
this.dist_to_nearest_edge = _via_region_point.prototype.dist_to_nearest_edge;
10220
}
10221
10222
_via_region_point.prototype.initialize = function() {
10223
this.cx = this.dview[0];
10224
this.cy = this.dview[1];
10225
this.r = 2;
10226
this.r2 = this.r * this.r;
10227
this.recompute_svg = true;
10228
}
10229
10230
//
10231
// image buffering
10232
//
10233
10234
function _via_cancel_current_image_loading() {
10235
var panel = document.getElementById('project_panel_title');
10236
panel.innerHTML = 'Project';
10237
_via_is_loading_current_image = false;
10238
}
10239
10240
function _via_show_img(img_index) {
10241
if ( _via_is_loading_current_image ) {
10242
return;
10243
}
10244
10245
var img_id = _via_image_id_list[img_index];
10246
10247
if ( ! _via_img_metadata.hasOwnProperty(img_id) ) {
10248
console.log('_via_show_img(): [' + img_index + '] ' + img_id + ' does not exist!')
10249
show_message('The requested image does not exist!')
10250
return;
10251
}
10252
10253
// file_resolve() is not necessary for files selected using browser's file selector
10254
if ( typeof(_via_img_fileref[img_id]) === 'undefined' || ! _via_img_fileref[img_id] instanceof File ) {
10255
// try preload from local file or url
10256
if ( typeof(_via_img_src[img_id]) === 'undefined' || _via_img_src[img_id] === '' ) {
10257
if ( is_url( _via_img_metadata[img_id].filename ) ) {
10258
_via_img_src[img_id] = _via_img_metadata[img_id].filename;
10259
_via_show_img(img_index);
10260
return;
10261
} else {
10262
var search_path_list = _via_file_get_search_path_list();
10263
if ( search_path_list.length === 0 ) {
10264
search_path_list.push(''); // search using just the filename
10265
}
10266
10267
_via_file_resolve(img_index, search_path_list).then( function(ok_file_index) {
10268
_via_show_img(img_index);
10269
}, function(err_file_index) {
10270
show_page_404(img_index);
10271
});
10272
return;
10273
}
10274
}
10275
}
10276
10277
if ( _via_buffer_img_index_list.includes(img_index) ) {
10278
_via_current_image_loaded = false;
10279
_via_show_img_from_buffer(img_index).then( function(ok_img_index) {
10280
// trigger preload of images in buffer corresponding to img_index
10281
// but, wait until all previous promises get cancelled
10282
Promise.all(_via_preload_img_promise_list).then( function(values) {
10283
_via_preload_img_promise_list = [];
10284
var preload_promise = _via_img_buffer_start_preload( img_index, 0 )
10285
_via_preload_img_promise_list.push(preload_promise);
10286
});
10287
}, function(err_img_index) {
10288
console.log('_via_show_img_from_buffer() failed for file: ' + _via_image_filename_list[err_img_index]);
10289
_via_current_image_loaded = false;
10290
});
10291
} else {
10292
// image not in buffer, so first add this image to buffer
10293
_via_is_loading_current_image = true;
10294
img_loading_spinbar(img_index, true);
10295
_via_img_buffer_add_image(img_index).then( function(ok_img_index) {
10296
_via_is_loading_current_image = false;
10297
img_loading_spinbar(img_index, false);
10298
_via_show_img(img_index);
10299
}, function(err_img_index) {
10300
_via_is_loading_current_image = false;
10301
img_loading_spinbar(img_index, false);
10302
show_page_404(img_index);
10303
console.log('_via_img_buffer_add_image() failed for file: ' + _via_image_filename_list[err_img_index]);
10304
});
10305
}
10306
}
10307
10308
function _via_buffer_hide_current_image() {
10309
img_fn_list_ith_entry_selected(_via_image_index, false);
10310
_via_clear_reg_canvas(); // clear old region shapes
10311
if ( _via_current_image ) {
10312
_via_current_image.classList.remove('visible');
10313
}
10314
}
10315
10316
function _via_show_img_from_buffer(img_index) {
10317
return new Promise( function(ok_callback, err_callback) {
10318
_via_buffer_hide_current_image();
10319
10320
var cimg_html_id = _via_img_buffer_get_html_id(img_index);
10321
_via_current_image = document.getElementById(cimg_html_id);
10322
if ( ! _via_current_image ) {
10323
// the said image is not present in buffer, which could be because
10324
// the image got removed from the buffer
10325
err_callback(img_index);
10326
return;
10327
}
10328
_via_current_image.classList.add('visible'); // now show the new image
10329
10330
_via_image_index = img_index;
10331
_via_image_id = _via_image_id_list[_via_image_index];
10332
_via_current_image_filename = _via_img_metadata[_via_image_id].filename;
10333
_via_current_image_loaded = true;
10334
10335
var arr_index = _via_buffer_img_index_list.indexOf(img_index);
10336
_via_buffer_img_shown_timestamp[arr_index] = Date.now(); // update shown timestamp
10337
10338
// update the current state of application
10339
_via_click_x0 = 0; _via_click_y0 = 0;
10340
_via_click_x1 = 0; _via_click_y1 = 0;
10341
_via_is_user_drawing_region = false;
10342
_via_is_window_resized = false;
10343
_via_is_user_resizing_region = false;
10344
_via_is_user_moving_region = false;
10345
_via_is_user_drawing_polygon = false;
10346
_via_is_region_selected = false;
10347
_via_user_sel_region_id = -1;
10348
_via_current_image_width = _via_current_image.naturalWidth;
10349
_via_current_image_height = _via_current_image.naturalHeight;
10350
10351
if ( _via_current_image_width === 0 || _via_current_image_height === 0 ) {
10352
// for error image icon
10353
_via_current_image_width = 640;
10354
_via_current_image_height = 480;
10355
}
10356
10357
// set the size of canvas
10358
// based on the current dimension of browser window
10359
var de = document.documentElement;
10360
var image_panel_width = de.clientWidth - leftsidebar.clientWidth - 20;
10361
if ( leftsidebar.style.display === 'none' ) {
10362
image_panel_width = de.clientWidth;
10363
}
10364
var image_panel_height = de.clientHeight - 2*ui_top_panel.offsetHeight;
10365
10366
_via_canvas_width = _via_current_image_width;
10367
_via_canvas_height = _via_current_image_height;
10368
10369
if ( _via_canvas_width > image_panel_width ) {
10370
// resize image to match the panel width
10371
var scale_width = image_panel_width / _via_current_image.naturalWidth;
10372
_via_canvas_width = image_panel_width;
10373
_via_canvas_height = _via_current_image.naturalHeight * scale_width;
10374
}
10375
if ( _via_canvas_height > image_panel_height ) {
10376
// resize further image if its height is larger than the image panel
10377
var scale_height = image_panel_height / _via_canvas_height;
10378
_via_canvas_height = image_panel_height;
10379
_via_canvas_width = _via_canvas_width * scale_height;
10380
}
10381
_via_canvas_width = Math.round(_via_canvas_width);
10382
_via_canvas_height = Math.round(_via_canvas_height);
10383
_via_canvas_scale = _via_current_image.naturalWidth / _via_canvas_width;
10384
_via_canvas_scale_without_zoom = _via_canvas_scale;
10385
set_all_canvas_size(_via_canvas_width, _via_canvas_height);
10386
//set_all_canvas_scale(_via_canvas_scale_without_zoom);
10387
10388
// reset all regions to "not selected" state
10389
toggle_all_regions_selection(false);
10390
10391
// ensure that all the canvas are visible
10392
set_display_area_content( VIA_DISPLAY_AREA_CONTENT_NAME.IMAGE );
10393
10394
// update img_fn_list
10395
img_fn_list_ith_entry_selected(_via_image_index, true);
10396
img_fn_list_scroll_to_current_file();
10397
10398
// refresh the annotations panel
10399
annotation_editor_update_content();
10400
10401
_via_load_canvas_regions(); // image to canvas space transform
10402
_via_redraw_reg_canvas();
10403
_via_reg_canvas.focus();
10404
10405
// Preserve zoom level
10406
if (_via_is_canvas_zoomed) {
10407
set_zoom( _via_canvas_zoom_level_index );
10408
}
10409
ok_callback(img_index);
10410
});
10411
}
10412
10413
function _via_img_buffer_add_image(img_index) {
10414
return new Promise( function(ok_callback, err_callback) {
10415
if ( _via_buffer_img_index_list.includes(img_index) ) {
10416
//console.log('_via_img_buffer_add_image(): image ' + img_index + ' already exists in buffer!')
10417
ok_callback(img_index);
10418
return;
10419
}
10420
10421
var img_id = _via_image_id_list[img_index];
10422
var img_filename = _via_img_metadata[img_id].filename;
10423
if ( !_via_img_metadata.hasOwnProperty(img_id)) {
10424
err_callback(img_index);
10425
return;
10426
}
10427
10428
// check if user has given access to local file using
10429
// browser's file selector
10430
if ( _via_img_fileref[img_id] instanceof File ) {
10431
var tmp_file_object_url = URL.createObjectURL(_via_img_fileref[img_id]);
10432
var img_id = _via_image_id_list[img_index];
10433
var bimg = document.createElement('img');
10434
bimg.setAttribute('id', _via_img_buffer_get_html_id(img_index));
10435
bimg.setAttribute('src', tmp_file_object_url);
10436
bimg.setAttribute('alt', 'Image loaded from base64 data of a local file selected by user.');
10437
bimg.addEventListener('error', function() {
10438
URL.revokeObjectURL(tmp_file_object_url);
10439
project_file_load_on_fail(img_index);
10440
err_callback(img_index);
10441
});
10442
bimg.addEventListener('load', function() {
10443
URL.revokeObjectURL(tmp_file_object_url);
10444
img_stat_set(img_index, [bimg.naturalWidth, bimg.naturalHeight]);
10445
_via_img_panel.insertBefore(bimg, _via_reg_canvas);
10446
project_file_load_on_success(img_index);
10447
img_fn_list_ith_entry_add_css_class(img_index, 'buffered')
10448
// add timestamp so that we can apply Least Recently Used (LRU)
10449
// scheme to remove elements when buffer is full
10450
var arr_index = _via_buffer_img_index_list.length;
10451
_via_buffer_img_index_list.push(img_index);
10452
_via_buffer_img_shown_timestamp[arr_index] = Date.now(); // though, not seen yet
10453
ok_callback(img_index);
10454
});
10455
return;
10456
}
10457
10458
if ( typeof(_via_img_src[img_id]) === 'undefined' || _via_img_src[img_id] === '' ) {
10459
err_callback(img_index);
10460
} else {
10461
var img_id = _via_image_id_list[img_index];
10462
10463
var bimg = document.createElement('img');
10464
bimg.setAttribute('id', _via_img_buffer_get_html_id(img_index));
10465
_via_img_src[img_id] = _via_img_src[img_id].replace('#', '%23');
10466
bimg.setAttribute('src', _via_img_src[img_id]);
10467
if ( _via_img_src[img_id].startsWith('data:image') ) {
10468
bimg.setAttribute('alt', 'Source: image data in base64 format');
10469
} else {
10470
bimg.setAttribute('alt', 'Source: ' + _via_img_src[img_id]);
10471
}
10472
10473
bimg.addEventListener('abort', function() {
10474
project_file_load_on_fail(img_index);
10475
err_callback(img_index);
10476
});
10477
bimg.addEventListener('error', function() {
10478
project_file_load_on_fail(img_index);
10479
err_callback(img_index);
10480
});
10481
10482
// Note: _via_current_image.{naturalWidth,naturalHeight} is only accessible after
10483
// the "load" event. Therefore, all processing must happen inside this event handler.
10484
bimg.addEventListener('load', function() {
10485
img_stat_set(img_index, [bimg.naturalWidth, bimg.naturalHeight]);
10486
_via_img_panel.insertBefore(bimg, _via_reg_canvas);
10487
10488
project_file_load_on_success(img_index);
10489
img_fn_list_ith_entry_add_css_class(img_index, 'buffered')
10490
// add timestamp so that we can apply Least Recently Used (LRU)
10491
// scheme to remove elements when buffer is full
10492
var arr_index = _via_buffer_img_index_list.length;
10493
_via_buffer_img_index_list.push(img_index);
10494
_via_buffer_img_shown_timestamp[arr_index] = Date.now(); // though, not seen yet
10495
ok_callback(img_index);
10496
}, false);
10497
}
10498
}, false);
10499
}
10500
10501
function _via_img_buffer_get_html_id(img_index) {
10502
return 'bim' + img_index;
10503
}
10504
10505
function _via_img_buffer_parse_html_id(html_id) {
10506
return parseInt( html_id.substr(3) );
10507
}
10508
10509
function _via_img_buffer_start_preload(img_index, preload_index) {
10510
return new Promise( function(ok_callback, err_callback) {
10511
_via_buffer_preload_img_index = img_index;
10512
_via_img_buffer_preload_img(_via_buffer_preload_img_index, 0).then( function(ok_img_index_list) {
10513
ok_callback(ok_img_index_list);
10514
});
10515
});
10516
}
10517
10518
function _via_img_buffer_preload_img(img_index, preload_index) {
10519
return new Promise( function(ok_callback, err_callback) {
10520
var preload_img_index = _via_img_buffer_get_preload_img_index(img_index, preload_index);
10521
10522
if ( _via_buffer_preload_img_index !== _via_image_index ) {
10523
ok_callback([]);
10524
return;
10525
}
10526
10527
// ensure that there is sufficient buffer space left for preloading image
10528
if ( _via_buffer_img_index_list.length > _via_settings.core.buffer_size ) {
10529
while( _via_buffer_img_index_list.length > _via_settings.core.buffer_size ) {
10530
_via_img_buffer_remove_least_useful_img();
10531
if ( _via_image_index !== _via_buffer_preload_img_index ) {
10532
// current image has changed therefore, we need to cancel this preload operation
10533
ok_callback([]);
10534
return;
10535
}
10536
}
10537
}
10538
10539
_via_img_buffer_add_image(preload_img_index).then( function(ok_img_index) {
10540
if ( _via_image_index !== _via_buffer_preload_img_index ) {
10541
ok_callback( [ok_img_index] );
10542
return;
10543
}
10544
10545
var next_preload_index = preload_index + 1;
10546
if ( next_preload_index !== VIA_IMG_PRELOAD_COUNT ) {
10547
_via_img_buffer_preload_img(img_index, next_preload_index).then( function(ok_img_index_list) {
10548
ok_img_index_list.push( ok_img_index )
10549
ok_callback( ok_img_index_list );
10550
});
10551
} else {
10552
ok_callback( [ok_img_index] );
10553
}
10554
}, function(err_img_index) {
10555
// continue with preload of other images in sequence
10556
var next_preload_index = preload_index + 1;
10557
if ( next_preload_index !== VIA_IMG_PRELOAD_COUNT ) {
10558
_via_img_buffer_preload_img(img_index, next_preload_index).then( function(ok_img_index_list) {
10559
ok_callback( ok_img_index_list );
10560
});
10561
} else {
10562
ok_callback([]);
10563
}
10564
});
10565
});
10566
}
10567
10568
function _via_img_buffer_get_preload_img_index(img_index, preload_index) {
10569
var preload_img_index = img_index + VIA_IMG_PRELOAD_INDICES[preload_index];
10570
if ( (preload_img_index < 0) || (preload_img_index >= _via_img_count) ) {
10571
if ( preload_img_index < 0 ) {
10572
preload_img_index = _via_img_count + preload_img_index;
10573
} else {
10574
preload_img_index = preload_img_index - _via_img_count;
10575
}
10576
}
10577
return preload_img_index;
10578
}
10579
10580
// the least useful image is, one with the following properties:
10581
// - preload list for current image will always get loaded, so there is no point in removing them from buffer
10582
// - all the other images in buffer were seen more recently by the image
10583
// - all the other images are closer (in terms of their image index) to the image currently being shown
10584
function _via_img_buffer_remove_least_useful_img() {
10585
var not_in_preload_list = _via_buffer_img_not_in_preload_list();
10586
var oldest_buffer_index = _via_buffer_get_oldest_in_list(not_in_preload_list);
10587
10588
if ( _via_buffer_img_index_list[oldest_buffer_index] !== _via_image_index ) {
10589
//console.log('removing oldest_buffer index: ' + oldest_buffer_index);
10590
_via_buffer_remove(oldest_buffer_index);
10591
} else {
10592
var furthest_buffer_index = _via_buffer_get_buffer_furthest_from_current_img();
10593
_via_buffer_remove(furthest_buffer_index);
10594
}
10595
}
10596
10597
function _via_buffer_remove( buffer_index ) {
10598
var img_index = _via_buffer_img_index_list[buffer_index];
10599
var bimg_html_id = _via_img_buffer_get_html_id(img_index);
10600
var bimg = document.getElementById(bimg_html_id);
10601
if ( bimg ) {
10602
_via_buffer_img_index_list.splice(buffer_index, 1);
10603
_via_buffer_img_shown_timestamp.splice(buffer_index, 1);
10604
_via_img_panel.removeChild(bimg);
10605
10606
img_fn_list_ith_entry_remove_css_class(img_index, 'buffered')
10607
}
10608
}
10609
10610
function _via_buffer_remove_all() {
10611
var i, n;
10612
n = _via_buffer_img_index_list.length;
10613
for ( i = 0 ; i < n; ++i ) {
10614
var img_index = _via_buffer_img_index_list[i];
10615
var bimg_html_id = _via_img_buffer_get_html_id(img_index);
10616
var bimg = document.getElementById(bimg_html_id);
10617
if ( bimg ) {
10618
_via_img_panel.removeChild(bimg);
10619
}
10620
}
10621
_via_buffer_img_index_list = [];
10622
_via_buffer_img_shown_timestamp = [];
10623
}
10624
10625
function _via_buffer_get_oldest_in_list(not_in_preload_list) {
10626
var i;
10627
var n = not_in_preload_list.length;
10628
var oldest_buffer_index = -1;
10629
var oldest_buffer_timestamp = Date.now();
10630
10631
for ( i = 0; i < n; ++i ) {
10632
var _via_buffer_index = not_in_preload_list[i];
10633
if ( _via_buffer_img_shown_timestamp[_via_buffer_index] < oldest_buffer_timestamp ) {
10634
oldest_buffer_timestamp = _via_buffer_img_shown_timestamp[i];
10635
oldest_buffer_index = i;
10636
}
10637
}
10638
return oldest_buffer_index;
10639
}
10640
10641
function _via_buffer_get_buffer_furthest_from_current_img() {
10642
var now_img_index = _via_image_index;
10643
var i, dist1, dist2, dist;
10644
var n = _via_buffer_img_index_list.length;
10645
var furthest_buffer_index = 0;
10646
dist1 = Math.abs( _via_buffer_img_index_list[0] - now_img_index );
10647
dist2 = _via_img_count - dist1; // assuming the list is circular
10648
var furthest_buffer_dist = Math.min(dist1, dist2);
10649
10650
for ( i = 1; i < n; ++i ) {
10651
dist1 = Math.abs( _via_buffer_img_index_list[i] - now_img_index );
10652
dist2 = _via_img_count - dist1; // assuming the list is circular
10653
dist = Math.min(dist1, dist2);
10654
// image has been seen by user at least once
10655
if ( dist > furthest_buffer_dist ) {
10656
furthest_buffer_dist = dist;
10657
furthest_buffer_index = i;
10658
}
10659
}
10660
return furthest_buffer_index;
10661
}
10662
10663
function _via_buffer_img_not_in_preload_list() {
10664
var preload_list = _via_buffer_get_current_preload_list();
10665
var i;
10666
var not_in_preload_list = [];
10667
for ( i = 0; i < _via_buffer_img_index_list.length; ++i ) {
10668
if ( ! preload_list.includes( _via_buffer_img_index_list[i] ) ) {
10669
not_in_preload_list.push( i );
10670
}
10671
}
10672
return not_in_preload_list;
10673
}
10674
10675
function _via_buffer_get_current_preload_list() {
10676
var i;
10677
var preload_list = [_via_image_index];
10678
var img_index = _via_image_index;
10679
for ( i = 0; i < VIA_IMG_PRELOAD_COUNT; ++i ) {
10680
var preload_index = img_index + VIA_IMG_PRELOAD_INDICES[i];
10681
if ( preload_index < 0 ) {
10682
preload_index = _via_img_count + preload_index;
10683
}
10684
if ( preload_index >= _via_img_count ) {
10685
preload_index = preload_index - _via_img_count;
10686
}
10687
preload_list.push(preload_index);
10688
}
10689
return preload_list;
10690
}
10691
10692
//
10693
// settings
10694
//
10695
function settings_panel_toggle() {
10696
if ( _via_display_area_content_name === VIA_DISPLAY_AREA_CONTENT_NAME.SETTINGS ) {
10697
if ( _via_display_area_content_name_prev !== '' ) {
10698
set_display_area_content(_via_display_area_content_name_prev);
10699
} else {
10700
show_single_image_view();
10701
_via_redraw_rleg_canvas();
10702
}
10703
}
10704
else {
10705
settings_init();
10706
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.SETTINGS);
10707
}
10708
}
10709
10710
function settings_init() {
10711
settings_region_visualisation_update_options();
10712
settings_filepath_update_html();
10713
settings_show_current_value();
10714
}
10715
10716
function settings_save() {
10717
// check if default path was updated
10718
var default_path_updated = false;
10719
if ( document.getElementById('_via_settings.core.default_filepath').value !== _via_settings.core.default_filepath ) {
10720
default_path_updated = true;
10721
}
10722
10723
var p = document.getElementById('settings_panel');
10724
var vl = p.getElementsByClassName('value');
10725
var n = vl.length;
10726
var i;
10727
for ( i = 0; i < n; ++i ) {
10728
var s = vl[i].childNodes[1];
10729
var sid_parts = s.id.split('.');
10730
if ( sid_parts[0] === '_via_settings' ) {
10731
var el = _via_settings;
10732
var found = true;
10733
var j;
10734
for ( j = 1; j < sid_parts.length - 1; ++j ) {
10735
if ( el.hasOwnProperty( sid_parts[j] ) ) {
10736
el = el[ sid_parts[j] ];
10737
} else {
10738
// unrecognized setting
10739
found = false;
10740
break;
10741
}
10742
}
10743
if ( found ) {
10744
var param = sid_parts[ sid_parts.length - 1 ];
10745
if ( s.value !== '' || typeof(s.value) !== 'undefined' ) {
10746
el[param] = s.value;
10747
}
10748
}
10749
}
10750
}
10751
10752
// non-standard settings
10753
var p;
10754
p = document.getElementById('settings_input_new_filepath');
10755
if ( p.value !== '' ) {
10756
project_filepath_add(p.value.trim());
10757
}
10758
p = document.getElementById('project_name');
10759
if ( p.value !== _via_settings.project.name ) {
10760
p.value = _via_settings.project.name;
10761
}
10762
10763
if ( default_path_updated ) {
10764
_via_file_resolve_all_to_default_filepath();
10765
_via_show_img(_via_image_index);
10766
}
10767
10768
show_message('Settings saved.');
10769
settings_panel_toggle();
10770
}
10771
10772
function settings_show_current_value() {
10773
var p = document.getElementById('settings_panel');
10774
var vl = p.getElementsByClassName('value');
10775
var n = vl.length;
10776
var i;
10777
for ( i = 0; i < n; ++i ) {
10778
var s = vl[i].childNodes[1];
10779
var sid_parts = s.id.split('.');
10780
if ( sid_parts[0] === '_via_settings' ) {
10781
var el = _via_settings;
10782
var found = true;
10783
var j;
10784
for ( j = 1; j < sid_parts.length; ++j ) {
10785
if ( el.hasOwnProperty( sid_parts[j] ) ) {
10786
el = el[ sid_parts[j] ];
10787
} else {
10788
// unrecognized setting
10789
found = false;
10790
break;
10791
}
10792
}
10793
10794
if ( found ) {
10795
s.value = el;
10796
}
10797
}
10798
}
10799
}
10800
10801
function settings_region_visualisation_update_options() {
10802
var region_setting_list = {'region_label': {
10803
'default_option':'__via_region_id__',
10804
'default_label':'Region id (1, 2, ...)',
10805
'label_prefix':'Show value of region attribute: ',
10806
}, 'region_color': {
10807
'default_option':'__via_default_region_color__',
10808
'default_label':'Default Region Colour',
10809
'label_prefix':'Based on value of region attribute: ',
10810
}};
10811
10812
for ( var setting in region_setting_list ) {
10813
var select = document.getElementById('_via_settings.ui.image.' + setting);
10814
select.innerHTML = '';
10815
var default_option = document.createElement('option');
10816
default_option.setAttribute('value', region_setting_list[setting]['default_option']);
10817
if ( _via_settings.ui.image[setting] === region_setting_list[setting]['default_option'] ) {
10818
default_option.setAttribute('selected', 'selected');
10819
}
10820
default_option.innerHTML = region_setting_list[setting]['default_label'];
10821
select.appendChild(default_option);
10822
10823
// options: add region attributes
10824
var rattr;
10825
for ( rattr in _via_attributes['region'] ) {
10826
var o = document.createElement('option');
10827
o.setAttribute('value', rattr);
10828
o.innerHTML = region_setting_list[setting]['label_prefix'] + rattr;
10829
if ( _via_settings.ui.image.region_label === rattr ) {
10830
o.setAttribute('selected', 'selected');
10831
}
10832
select.appendChild(o);
10833
}
10834
}
10835
}
10836
10837
function settings_filepath_update_html() {
10838
var p = document.getElementById('_via_settings.core.filepath');
10839
p.innerHTML = '';
10840
var i, path, order;
10841
for ( path in _via_settings.core.filepath ) {
10842
order = _via_settings.core.filepath[path]
10843
if ( order !== 0 ) {
10844
var li = document.createElement('li');
10845
li.innerHTML = path + '<span class="text_button" title="Delete image path" onclick="project_filepath_del(\"' + path + '\"); settings_filepath_update_html();">&times;</span>';
10846
p.appendChild(li);
10847
}
10848
}
10849
}
10850
10851
//
10852
// find location of file
10853
//
10854
10855
function _via_file_resolve_all_to_default_filepath() {
10856
var img_id;
10857
for ( img_id in _via_img_metadata ) {
10858
if ( _via_img_metadata.hasOwnProperty(img_id) ) {
10859
_via_file_resolve_file_to_default_filepath(img_id);
10860
}
10861
}
10862
}
10863
10864
function _via_file_resolve_file_to_default_filepath(img_id) {
10865
if ( _via_img_metadata.hasOwnProperty(img_id) ) {
10866
if ( typeof(_via_img_fileref[img_id]) === 'undefined' || ! _via_img_fileref[img_id] instanceof File ) {
10867
if ( is_url( _via_img_metadata[img_id].filename ) ) {
10868
_via_img_src[img_id] = _via_img_metadata[img_id].filename;
10869
} else {
10870
_via_img_src[img_id] = _via_settings.core.default_filepath + _via_img_metadata[img_id].filename;
10871
}
10872
}
10873
}
10874
}
10875
10876
function _via_file_resolve_all() {
10877
return new Promise( function(ok_callback, err_callback) {
10878
var all_promises = [];
10879
10880
var search_path_list = _via_file_get_search_path_list();
10881
var i, img_id;
10882
for ( i = 0; i < _via_img_count; ++i ) {
10883
img_id = _via_image_id_list[i];
10884
if ( typeof(_via_img_src[img_id]) === 'undefined' || _via_img_src[img_id] === '' ) {
10885
var p = _via_file_resolve(i, search_path_list);
10886
all_promises.push(p);
10887
}
10888
}
10889
10890
Promise.all( all_promises ).then( function(ok_file_index_list) {
10891
console.log(ok_file_index_list);
10892
ok_callback();
10893
//project_file_load_on_success(ok_file_index);
10894
}, function(err_file_index_list) {
10895
console.log(err_file_index_list);
10896
err_callback();
10897
//project_file_load_on_fail(err_file_index);
10898
});
10899
10900
});
10901
}
10902
10903
function _via_file_get_search_path_list() {
10904
var search_path_list = [];
10905
var path;
10906
for ( path in _via_settings.core.filepath ) {
10907
if ( _via_settings.core.filepath[path] !== 0 ) {
10908
search_path_list.push(path);
10909
}
10910
}
10911
return search_path_list;
10912
}
10913
10914
function _via_file_resolve(file_index, search_path_list) {
10915
return new Promise( function(ok_callback, err_callback) {
10916
var path_index = 0;
10917
var p = _via_file_resolve_check_path(file_index, path_index, search_path_list).then(function(ok) {
10918
ok_callback(ok);
10919
}, function(err) {
10920
err_callback(err);
10921
});
10922
}, false);
10923
}
10924
10925
function _via_file_resolve_check_path(file_index, path_index, search_path_list) {
10926
return new Promise( function(ok_callback, err_callback) {
10927
var img_id = _via_image_id_list[file_index];
10928
var img = new Image(0,0);
10929
10930
var img_path = search_path_list[path_index] + _via_img_metadata[img_id].filename;
10931
if ( is_url( _via_img_metadata[img_id].filename ) ) {
10932
if ( search_path_list[path_index] !== '' ) {
10933
// we search for the the image filename pointed by URL in local search paths
10934
img_path = search_path_list[path_index] + get_filename_from_url( _via_img_metadata[img_id].filename );
10935
}
10936
}
10937
10938
img.setAttribute('src', img_path);
10939
10940
img.addEventListener('load', function() {
10941
_via_img_src[img_id] = img_path;
10942
ok_callback(file_index);
10943
}, false);
10944
img.addEventListener('abort', function() {
10945
err_callback(file_index);
10946
});
10947
img.addEventListener('error', function() {
10948
var new_path_index = path_index + 1;
10949
if ( new_path_index < search_path_list.length ) {
10950
_via_file_resolve_check_path(file_index, new_path_index, search_path_list).then( function(ok) {
10951
ok_callback(file_index);
10952
}, function(err) {
10953
err_callback(file_index);
10954
});
10955
} else {
10956
err_callback(file_index);
10957
}
10958
}, false);
10959
}, false);
10960
}
10961
10962
//
10963
// page 404 (file not found)
10964
//
10965
function show_page_404(img_index) {
10966
_via_buffer_hide_current_image();
10967
10968
set_display_area_content(VIA_DISPLAY_AREA_CONTENT_NAME.PAGE_404);
10969
10970
_via_image_index = img_index;
10971
_via_image_id = _via_image_id_list[_via_image_index];
10972
_via_current_image_loaded = false;
10973
img_fn_list_ith_entry_selected(_via_image_index, true);
10974
10975
document.getElementById('page_404_filename').innerHTML = '[' + (_via_image_index+1) + ']' + _via_img_metadata[_via_image_id].filename;
10976
}
10977
10978
10979
//
10980
// utils
10981
//
10982
10983
function is_url( s ) {
10984
// @todo: ensure that this is sufficient to capture all image url
10985
if ( s.startsWith('http://') || s.startsWith('https://') || s.startsWith('www.') ) {
10986
return true;
10987
} else {
10988
return false;
10989
}
10990
}
10991
10992
function get_filename_from_url( url ) {
10993
return url.substring( url.lastIndexOf('/') + 1 );
10994
}
10995
10996
function fixfloat(x) {
10997
return parseFloat( x.toFixed(VIA_FLOAT_PRECISION) );
10998
}
10999
11000
function shape_attribute_fixfloat(sa) {
11001
for ( var attr in sa ) {
11002
switch(attr) {
11003
case 'x':
11004
case 'y':
11005
case 'width':
11006
case 'height':
11007
case 'r':
11008
case 'rx':
11009
case 'ry':
11010
sa[attr] = fixfloat( sa[attr] );
11011
break;
11012
case 'all_points_x':
11013
case 'all_points_y':
11014
for ( var i in sa[attr] ) {
11015
sa[attr][i] = fixfloat( sa[attr][i] );
11016
}
11017
}
11018
}
11019
}
11020
11021
// start with the array having smallest number of elements
11022
// check the remaining arrays if they all contain the elements of this shortest array
11023
function array_intersect( array_list ) {
11024
if ( array_list.length === 0 ) {
11025
return [];
11026
}
11027
if ( array_list.length === 1 ) {
11028
return array_list[0];
11029
}
11030
11031
var shortest_array = array_list[0];
11032
var shortest_array_index = 0;
11033
var i;
11034
for ( i = 1; i < array_list.length; ++i ) {
11035
if ( array_list[i].length < shortest_array.length ) {
11036
shortest_array = array_list[i];
11037
shortest_array_index = i;
11038
}
11039
}
11040
11041
var intersect = [];
11042
var element_count = {};
11043
11044
var array_index_i;
11045
for ( i = 0; i < array_list.length; ++i ) {
11046
if ( i === 0 ) {
11047
// in the first iteration, process the shortest element array
11048
array_index_i = shortest_array_index;
11049
} else {
11050
array_index_i = i;
11051
}
11052
11053
var j;
11054
for ( j = 0; j < array_list[array_index_i].length; ++j ) {
11055
if ( element_count[ array_list[array_index_i][j] ] === (i-1) ) {
11056
if ( i === array_list.length - 1 ) {
11057
intersect.push( array_list[array_index_i][j] );
11058
element_count[ array_list[array_index_i][j] ] = 0;
11059
} else {
11060
element_count[ array_list[array_index_i][j] ] = i;
11061
}
11062
} else {
11063
element_count[ array_list[array_index_i][j] ] = 0;
11064
}
11065
}
11066
}
11067
return intersect;
11068
}
11069
11070
function generate_img_index_list(input) {
11071
var all_img_index_list = [];
11072
11073
// condition: count format a,b
11074
var count_format_img_index_list = [];
11075
if ( input.prev_next_count.value !== '' ) {
11076
var prev_next_split = input.prev_next_count.value.split(',');
11077
if ( prev_next_split.length === 2 ) {
11078
var prev = parseInt( prev_next_split[0] );
11079
var next = parseInt( prev_next_split[1] );
11080
var i;
11081
for ( i = (_via_image_index - prev); i <= (_via_image_index + next); i++ ) {
11082
count_format_img_index_list.push(i);
11083
}
11084
}
11085
}
11086
if ( count_format_img_index_list.length !== 0 ) {
11087
all_img_index_list.push(count_format_img_index_list);
11088
}
11089
11090
//condition: image index list expression
11091
var expr_img_index_list = [];
11092
if ( input.img_index_list.value !== '' ) {
11093
var img_index_expr = input.img_index_list.value.split(',');
11094
if ( img_index_expr.length !== 0 ) {
11095
var i;
11096
for ( i = 0; i < img_index_expr.length; ++i ) {
11097
if ( img_index_expr[i].includes('-') ) {
11098
var ab = img_index_expr[i].split('-');
11099
var a = parseInt( ab[0] ) - 1; // 0 based indexing
11100
var b = parseInt( ab[1] ) - 1;
11101
var j;
11102
for ( j = a; j <= b; ++j ) {
11103
expr_img_index_list.push(j);
11104
}
11105
} else {
11106
expr_img_index_list.push( parseInt(img_index_expr[i]) - 1 );
11107
}
11108
}
11109
}
11110
}
11111
if ( expr_img_index_list.length !== 0 ) {
11112
all_img_index_list.push(expr_img_index_list);
11113
}
11114
11115
11116
// condition: regular expression
11117
var regex_img_index_list = [];
11118
if ( input.regex.value !== '' ) {
11119
var regex = input.regex.value;
11120
for ( var i=0; i < _via_image_filename_list.length; ++i ) {
11121
var filename = _via_image_filename_list[i];
11122
if ( filename.match(regex) !== null ) {
11123
regex_img_index_list.push(i);
11124
}
11125
}
11126
}
11127
if ( regex_img_index_list.length !== 0 ) {
11128
all_img_index_list.push(regex_img_index_list);
11129
}
11130
11131
var intersect = array_intersect(all_img_index_list);
11132
return intersect;
11133
}
11134
11135
if ( ! _via_is_debug_mode ) {
11136
// warn user of possible loss of data
11137
window.onbeforeunload = function (e) {
11138
e = e || window.event;
11139
11140
// For IE and Firefox prior to version 4
11141
if (e) {
11142
e.returnValue = 'Did you save your data?';
11143
}
11144
11145
// For Safari
11146
return 'Did you save your data?';
11147
};
11148
}
11149
11150
//
11151
// keep a record of image statistics (e.g. width, height, ...)
11152
//
11153
function img_stat_set(img_index, stat) {
11154
if ( stat.length ) {
11155
_via_img_stat[img_index] = stat;
11156
} else {
11157
delete _via_img_stat[img_index];
11158
}
11159
}
11160
11161
function img_stat_set_all() {
11162
return new Promise( function(ok_callback, err_callback) {
11163
var promise_list = [];
11164
var img_id;
11165
for ( var img_index in _via_image_id_list ) {
11166
if ( ! _via_img_stat.hasOwnProperty(img_index) ) {
11167
img_id = _via_image_id_list[img_index];
11168
if ( _via_img_metadata[img_id].file_attributes.hasOwnProperty('width') &&
11169
_via_img_metadata[img_id].file_attributes.hasOwnProperty('height')
11170
) {
11171
_via_img_stat[img_index] = [_via_img_metadata[img_id].file_attributes['width'],
11172
_via_img_metadata[img_id].file_attributes['height'],
11173
];
11174
} else {
11175
promise_list.push( img_stat_get(img_index) );
11176
}
11177
}
11178
}
11179
if ( promise_list.length ) {
11180
Promise.all(promise_list).then( function(ok) {
11181
ok_callback();
11182
}.bind(this), function(err) {
11183
console.warn('Failed to read statistics of all images!');
11184
err_callback();
11185
});
11186
} else {
11187
ok_callback();
11188
}
11189
}.bind(this));
11190
}
11191
11192
function img_stat_get(img_index) {
11193
return new Promise( function(ok_callback, err_callback) {
11194
var img_id = _via_image_id_list[img_index];
11195
var tmp_img = document.createElement('img');
11196
var tmp_file_object_url = null;
11197
tmp_img.addEventListener('load', function() {
11198
_via_img_stat[img_index] = [tmp_img.naturalWidth, tmp_img.naturalHeight];
11199
if ( tmp_file_object_url !== null ) {
11200
URL.revokeObjectURL(tmp_file_object_url);
11201
}
11202
ok_callback();
11203
}.bind(this));
11204
tmp_img.addEventListener('error', function() {
11205
_via_img_stat[img_index] = [-1, -1];
11206
if ( tmp_file_object_url !== null ) {
11207
URL.revokeObjectURL(tmp_file_object_url);
11208
}
11209
ok_callback();
11210
}.bind(this));
11211
11212
if ( _via_img_fileref[img_id] instanceof File ) {
11213
tmp_file_object_url = URL.createObjectURL(_via_img_fileref[img_id]);
11214
tmp_img.src = tmp_file_object_url;
11215
} else {
11216
tmp_img.src = _via_img_src[img_id];
11217
}
11218
}.bind(this));
11219
}
11220
11221
11222
// pts = [x0,y0,x1,y1,....]
11223
function polygon_to_bbox(pts) {
11224
var xmin = +Infinity;
11225
var xmax = -Infinity;
11226
var ymin = +Infinity;
11227
var ymax = -Infinity;
11228
for ( var i = 0; i < pts.length; i = i + 2 ) {
11229
if ( pts[i] > xmax ) {
11230
xmax = pts[i];
11231
}
11232
if ( pts[i] < xmin ) {
11233
xmin = pts[i];
11234
}
11235
if ( pts[i+1] > ymax ) {
11236
ymax = pts[i+1];
11237
}
11238
if ( pts[i+1] < ymin ) {
11239
ymin = pts[i+1];
11240
}
11241
}
11242
return [xmin, ymin, xmax-xmin, ymax-ymin];
11243
}
11244
</script>
11245
<!-- END: Contents of file: via.js-->
11246
</body>
11247
</html>
11248