@@ -2,22 +2,25 @@ function selectFileTypeToSave(){
2
2
$ . dialog ( {
3
3
title : 'Save/Export as' ,
4
4
content : `<div style="text-align:center;">
5
- <div>
6
- <button class="btn btn-primary savebtn" onclick="javascript:saveAsNimn()" id="saveAsNimn">Project file</button>
7
- </div>
8
- <div>
9
- <button class="btn btn-primary savebtn" onclick="javascript:saveAsDlibXML()" id="saveAsNimn">Dlib XML</button>
10
- </div>
11
- <div>
12
- <button class="btn btn-primary savebtn" onclick="javascript:saveAsDlibPts()" id="saveAsNimn">Dlib pts</button>
13
- </div>
14
- <div>
15
- <button class="btn btn-primary savebtn" onclick="javascript:saveAsCOCO()" id="saveAsCOCO">COCO JSON</button>
16
- </div>
17
- <div>
18
- <button class="btn btn-primary savebtn" onclick="javascript:saveAsPascalVOC()" id="saveAsPascalVOC">Pascal VOC XML</button>
19
- </div>
20
- <div>` ,
5
+ <div>
6
+ <button class="btn btn-primary savebtn" onclick="javascript:saveAsNimn()" id="saveAsNimn">Project file</button>
7
+ </div>
8
+ <div>
9
+ <button class="btn btn-primary savebtn" onclick="javascript:saveAsDlibXML()" id="saveAsNimn">Dlib XML</button>
10
+ </div>
11
+ <div>
12
+ <button class="btn btn-primary savebtn" onclick="javascript:saveAsDlibPts()" id="saveAsNimn">Dlib pts</button>
13
+ </div>
14
+ <div>
15
+ <button class="btn btn-primary savebtn" onclick="javascript:saveAsCOCO()" id="saveAsCOCO">COCO JSON</button>
16
+ </div>
17
+ <div>
18
+ <button class="btn btn-primary savebtn" onclick="javascript:saveAsPascalVOC()" id="saveAsPascalVOC">Pascal VOC XML</button>
19
+ </div>
20
+ <div>
21
+ <button class="btn btn-primary savebtn" onclick="javascript:saveAsYoloV5Pytorch()" id="saveAsYoloV5Pytorch">YOLO V5 Pytorch</button>
22
+ </div>
23
+ <div>` ,
21
24
escapeKey : true ,
22
25
backgroundDismiss : true ,
23
26
} ) ;
@@ -131,6 +134,174 @@ function saveAsPascalVOC(){
131
134
132
135
}
133
136
137
+ /**
138
+ * Save labelled data as YOLO supported TXT Files file.
139
+ * It will export files in a zip format
140
+ */
141
+
142
+ function saveAsYoloV5Pytorch ( ) {
143
+ // COUNT TOTAL MASKED IMAGES
144
+ var totalLabbeledImages = 0 ;
145
+ for ( const key in labellingData ) {
146
+ if ( labellingData [ key ] . shapes [ 0 ] && labellingData [ key ] . shapes [ 0 ] . points . length > 0 ) {
147
+ totalLabbeledImages ++ ;
148
+ }
149
+ }
150
+
151
+ // MODAL FOR RECEIVING DATA SPLIT INPUTS
152
+ $ . dialog ( {
153
+ title : `Split ${ totalLabbeledImages } Labeled Images into :` ,
154
+ content : `<div class="col w-75 m-auto">
155
+ <div class="row">
156
+ <div ref="label-data" class="col"> Train </div>
157
+ <div ref="label-data" class="col"> Test </div>
158
+ <div ref="label-data" class="col"> Valid </div>
159
+ </div>
160
+ <div class="row justify-content-between">
161
+ <input type="text" class="col" value="${ Math . floor ( totalLabbeledImages / 100 * 70 ) } " onchange="javascript:validateImageSplit()" style="width: 20px;" placeholder="">
162
+ <input type="text" class="col" value="${ Math . floor ( totalLabbeledImages / 100 * 30 ) } " onchange="javascript:validateImageSplit()" style="width: 20px;" placeholder="">
163
+ <input type="text" class="col" value="${ Math . floor ( totalLabbeledImages / 100 * 10 ) } " onchange="javascript:validateImageSplit()" style="width: 20px;" placeholder="">
164
+ </div>
165
+ </div>
166
+ <div class="d-flex justify-content-center">
167
+ <span class="mt-2 w-75" id="splitMsg" > </span>
168
+ </div>
169
+ <div class="d-flex" style="text-align:center;">
170
+ <button class="btn btn-primary savebtn" onclick="javascript:yoloDataRendering()">Generate</button>
171
+ </div>` ,
172
+ escapeKey : true ,
173
+ backgroundDismiss : true ,
174
+ } ) ;
175
+ }
176
+
177
+ function validateImageSplit ( ) {
178
+ // COLLECTING USER INPUT VALUES
179
+ const valueElements = document . querySelectorAll ( '[style="width: 20px;"]' ) ;
180
+ const trainImages = parseInt ( valueElements [ 0 ] . value ) ;
181
+ const testImages = parseInt ( valueElements [ 1 ] . value ) ;
182
+ const validImages = parseInt ( valueElements [ 2 ] . value ) ;
183
+
184
+ var totalLabbeledImages = 0 ;
185
+ for ( const key in labellingData ) {
186
+ if ( labellingData [ key ] . shapes [ 0 ] && labellingData [ key ] . shapes [ 0 ] . points . length > 0 ) {
187
+ totalLabbeledImages ++ ;
188
+ }
189
+ }
190
+
191
+ var remainingImages = totalLabbeledImages - trainImages - testImages - validImages ;
192
+ if ( remainingImages > 0 ) {
193
+ document . getElementById ( 'splitMsg' ) . innerText = `${ remainingImages } Labelled Images remaining to split.` ;
194
+ }
195
+
196
+ if ( trainImages + testImages + validImages == totalLabbeledImages ) {
197
+ document . getElementById ( 'splitMsg' ) . innerText = "" ;
198
+ return [ trainImages , testImages , validImages , true ] ;
199
+ } else {
200
+ showSnackBar ( `Total of ( Train+Test+Valid ) must be ${ totalLabbeledImages } .` ) ;
201
+ return [ 0 , 0 , 0 , false ] ;
202
+ }
203
+ }
204
+
205
+ function yoloDataRendering ( ) {
206
+ var [ train , test , valid , isValuesValid ] = validateImageSplit ( ) ;
207
+
208
+ // CHECK FOR VALID INPUTS
209
+ if ( ! isValuesValid ) return ;
210
+
211
+ // COLLECT AND SAVE UNIQUE LABELS
212
+ const labels = new Set ( ) ;
213
+ for ( const image in labellingData ) {
214
+ const img = labellingData [ image ] ;
215
+ img . shapes . forEach ( ( shape ) => {
216
+ labels . add ( shape . label ) ;
217
+ } ) ;
218
+ }
219
+
220
+ const zip = new JSZip ( ) ;
221
+ const finalLabels = [ ...labels ] ;
222
+
223
+ var trainImgLimit = train ;
224
+ var testImgLimit = train + test ;
225
+ var validImgLimit = testImgLimit + valid ;
226
+ var currImage = 0 ;
227
+
228
+ // CREATE DATA.YAML FILE
229
+ const dataYamlFile = `train: ../train/images\nval: ../valid/images\ntest: ../test/images\n\nnc: ${ finalLabels . length } \nnames: ['${ finalLabels . join ( "','" ) } ']` ;
230
+ zip . file ( "data.yaml" , dataYamlFile ) ;
231
+
232
+ // GENERATE YOLO REQUIRED FORMAT DATA
233
+ for ( const image in labellingData ) {
234
+ let outputArr = [ ] ;
235
+ let fileName = image . substring ( 0 , image . lastIndexOf ( "." ) ) + ".txt" ;
236
+
237
+ // RECONSTRUCT IMAGES
238
+ let base64Image = document . querySelector ( `[label="${ image } "]` ) . src ;
239
+ var byteCharacters = atob ( base64Image . split ( "," ) [ 1 ] ) ;
240
+ var byteNumbers = new Array ( byteCharacters . length ) ;
241
+ for ( var i = 0 ; i < byteCharacters . length ; i ++ ) {
242
+ byteNumbers [ i ] = byteCharacters . charCodeAt ( i ) ;
243
+ }
244
+ var byteArray = new Uint8Array ( byteNumbers ) ;
245
+ var blob = new Blob ( [ byteArray ] , { type : "image/jpg" } ) ; // Specify the appropriate MIME type
246
+
247
+ // ITRATE THROUGH EACH SHAPE
248
+ labellingData [ image ] . shapes . forEach ( ( shape ) => {
249
+ let currArr = [ ] ;
250
+ currArr . push ( finalLabels . indexOf ( shape . label ) ) ;
251
+
252
+ // CALCULATE POINTS
253
+ if ( shape . points [ 0 ] [ 0 ] ) {
254
+ let coordinates = shape . points ;
255
+ coordinates . forEach ( ( point ) => {
256
+ let xNorm = point [ 0 ] / labellingData [ image ] . size . width ;
257
+ let yNorm = point [ 1 ] / labellingData [ image ] . size . height ;
258
+ currArr . push ( xNorm , yNorm ) ;
259
+ } ) ;
260
+ } else {
261
+ let [ xMin , yMin , w , h ] = shape . points ;
262
+ const yoloXCenter = ( xMin + w / 2 ) / labellingData [ image ] . size . width ;
263
+ const yoloYCenter = ( yMin + h / 2 ) / labellingData [ image ] . size . height ;
264
+ const yoloBoxWidth = w / labellingData [ image ] . size . width ;
265
+ const yoloBoxHeight = h / labellingData [ image ] . size . height ;
266
+ currArr . push ( yoloXCenter , yoloYCenter , yoloBoxWidth , yoloBoxHeight ) ;
267
+ }
268
+ outputArr . push ( currArr . join ( " " ) ) ;
269
+ } ) ;
270
+
271
+ // ADD FILES TO ZIP IF LABELLED
272
+ if ( outputArr . length > 0 ) {
273
+ if ( currImage < trainImgLimit ) {
274
+ saveDirectory = "train" ;
275
+ } else if ( currImage < testImgLimit ) {
276
+ saveDirectory = "test" ;
277
+ } else if ( currImage <= validImgLimit ) {
278
+ saveDirectory = "valid" ;
279
+ }
280
+ currImage ++ ;
281
+
282
+ zip . file ( `${ saveDirectory } /images/${ image } ` , blob ) ;
283
+ zip . file ( `${ saveDirectory } /labels/${ fileName } ` , outputArr . join ( "\n" ) ) ;
284
+ }
285
+ }
286
+
287
+ // SET FILE NAME
288
+ var curTimeStamp = new Date ( ) ;
289
+ var timeStamp = `${ curTimeStamp . getDate ( ) } / ${ curTimeStamp . getMonth ( ) + 1 } / ${ curTimeStamp . getFullYear ( ) } /
290
+ ${ curTimeStamp . getHours ( ) } : ${ curTimeStamp . getMinutes ( ) } : ${ curTimeStamp . getSeconds ( ) } ` ;
291
+
292
+ // CREATE ZIP AND SAVE
293
+ zip . generateAsync ( { type : "blob" } )
294
+ . then ( function ( content ) {
295
+ saveAs ( content , timeStamp + ".zip" ) ;
296
+ showSnackBar ( `File will be downloaded automatically` ) ;
297
+ analytics_reportExportType ( "yoloV5PyTorch" ) ;
298
+ } )
299
+ . catch ( function ( error ) {
300
+ showSnackBar ( "Error occoured while creating ZIP file" ) ;
301
+ console . error ( "Error creating ZIP file:" , error ) ;
302
+ } ) ;
303
+ }
304
+
134
305
/**
135
306
* Save given data to a file
136
307
* @param {* } data
0 commit comments