├── .gitignore ├── README.md ├── demo-1.jpg ├── demo-2.jpg ├── favicon.png ├── index.html ├── oss_data ├── images.bytes ├── images.png ├── images.tsv ├── oss_demo_projector_config.json ├── words.bytes └── words.tsv └── projector.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Intro 2 | Visualization of high dimensional data is very important in data science. Google has open-sourced a tool called `Embedding Projector` which makes it much easier. It is a build-in tool of Google's [TensorFlow](https://tensorflow.org) framework. And there is also a standalone version [here](https://github.com/tensorflow/embedding-projector-standalone) 3 | 4 | The following content is about **HOW TO** use the **STANDALONE** tool. 5 | 6 | 7 | 8 | ## Steps 9 | 10 | ### Check out this project 11 | 12 | Depends on `opencv-python`, install the module if you don't have one on your system. 13 | 14 | ```shell 15 | git clone https://github.com/cnzeki/embedding-projector 16 | cd embedding-projector 17 | ``` 18 | 19 | ### Try it out 20 | 21 | ```shell 22 | python projector.py 23 | ``` 24 | 25 | Open your browser to : http://localhost:8000 26 | 27 | #### With sprite: 28 | 29 | ![](demo-1.jpg) 30 | 31 | #### With label 32 | 33 | ![](demo-2.jpg) 34 | 35 | ### How to visualize your own data 36 | 37 | The `Embedding Projector` takes a NxD tensor as input, N is the number of samples (or embeddings), D is the dimension of each sample. The tensor is stored in a file (raw float bytes for tsv). A sample is a point in the plot. We can attach some metas to a sample, a image (called `sprite`), or labels ( class id or names). 38 | 39 | A example sprite image: 40 | 41 | ![](oss_data/images.png) 42 | 43 | I wrote a function to make everything done, just call it with your data. Tensor is stored as binary bytes. 44 | 45 | ``` 46 | write_image_embeddings(root, title, feats, labels, imgs, sprite_size) 47 | ''' 48 | :param root: root dir of `Embedding Project` tool 49 | :param title: name of the tensor 50 | :param feats: embedding tensor NxDim 51 | :param labels: labels for each sample NxNumClasses 52 | :param [optional] imgs: images in format NHWC 53 | :param [optional] sprite_size: image sprite size 54 | :param mode: 'w' -- write, 'w+' -- update or append, '+' -- append 55 | ''' 56 | ``` 57 | 58 | If you want to write tensor in tsv format, use this instead: 59 | 60 | ```python 61 | write_tsv_embeddings(prefix, feats, labels=None): 62 | ``` 63 | 64 | Tsv files can be load in the web page on-line. 65 | 66 | For more details, read the code please. 67 | 68 | -------------------------------------------------------------------------------- /demo-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnzeki/embedding-projector/5a697ff5b1a9c5aadb1548b84f7aabaaeac849e9/demo-1.jpg -------------------------------------------------------------------------------- /demo-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnzeki/embedding-projector/5a697ff5b1a9c5aadb1548b84f7aabaaeac849e9/demo-2.jpg -------------------------------------------------------------------------------- /favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnzeki/embedding-projector/5a697ff5b1a9c5aadb1548b84f7aabaaeac849e9/favicon.png -------------------------------------------------------------------------------- /oss_data/images.bytes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnzeki/embedding-projector/5a697ff5b1a9c5aadb1548b84f7aabaaeac849e9/oss_data/images.bytes -------------------------------------------------------------------------------- /oss_data/images.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnzeki/embedding-projector/5a697ff5b1a9c5aadb1548b84f7aabaaeac849e9/oss_data/images.png -------------------------------------------------------------------------------- /oss_data/images.tsv: -------------------------------------------------------------------------------- 1 | 0 2 | 0 3 | 0 4 | 0 5 | 0 6 | 0 7 | 0 8 | 0 9 | 0 10 | 0 11 | 0 12 | 0 13 | 0 14 | 0 15 | 0 16 | 0 17 | 0 18 | 0 19 | 0 20 | 0 21 | 0 22 | 0 23 | 0 24 | 0 25 | 0 26 | 0 27 | 0 28 | 0 29 | 0 30 | 0 31 | 0 32 | 0 33 | 0 34 | 0 35 | 0 36 | 0 37 | 0 38 | 0 39 | 0 40 | 0 41 | 0 42 | 0 43 | 0 44 | 0 45 | 0 46 | 0 47 | 0 48 | 0 49 | 0 50 | 0 51 | 1 52 | 1 53 | 1 54 | 1 55 | 1 56 | 1 57 | 1 58 | 1 59 | 1 60 | 1 61 | 1 62 | 1 63 | 1 64 | 1 65 | 1 66 | 1 67 | 1 68 | 1 69 | 1 70 | 1 71 | 1 72 | 1 73 | 1 74 | 1 75 | 1 76 | 1 77 | 1 78 | 1 79 | 1 80 | 1 81 | 1 82 | 1 83 | 1 84 | 1 85 | 1 86 | 1 87 | 1 88 | 1 89 | 1 90 | 1 91 | 1 92 | 1 93 | 1 94 | 1 95 | 1 96 | 1 97 | 1 98 | 1 99 | 1 100 | 1 101 | 2 102 | 2 103 | 2 104 | 2 105 | 2 106 | 2 107 | 2 108 | 2 109 | 2 110 | 2 111 | 2 112 | 2 113 | 2 114 | 2 115 | 2 116 | 2 117 | 2 118 | 2 119 | 2 120 | 2 121 | 2 122 | 2 123 | 2 124 | 2 125 | 2 126 | 2 127 | 2 128 | 2 129 | 2 130 | 2 131 | 2 132 | 2 133 | 2 134 | 2 135 | 2 136 | 2 137 | 2 138 | 2 139 | 2 140 | 2 141 | 2 142 | 2 143 | 2 144 | 2 145 | 2 146 | 2 147 | 2 148 | 2 149 | 2 150 | 2 151 | 3 152 | 3 153 | 3 154 | 3 155 | 3 156 | 3 157 | 3 158 | 3 159 | 3 160 | 3 161 | 3 162 | 3 163 | 3 164 | 3 165 | 3 166 | 3 167 | 3 168 | 3 169 | 3 170 | 3 171 | 3 172 | 3 173 | 3 174 | 3 175 | 3 176 | 3 177 | 3 178 | 3 179 | 3 180 | 3 181 | 3 182 | 3 183 | 3 184 | 3 185 | 3 186 | 3 187 | 3 188 | 3 189 | 3 190 | 3 191 | 3 192 | 3 193 | 3 194 | 3 195 | 3 196 | 3 197 | 3 198 | 3 199 | 3 200 | 3 201 | 4 202 | 4 203 | 4 204 | 4 205 | 4 206 | 4 207 | 4 208 | 4 209 | 4 210 | 4 211 | 4 212 | 4 213 | 4 214 | 4 215 | 4 216 | 4 217 | 4 218 | 4 219 | 4 220 | 4 221 | 4 222 | 4 223 | 4 224 | 4 225 | 4 226 | 4 227 | 4 228 | 4 229 | 4 230 | 4 231 | 4 232 | 4 233 | 4 234 | 4 235 | 4 236 | 4 237 | 4 238 | 4 239 | 4 240 | 4 241 | 4 242 | 4 243 | 4 244 | 4 245 | 4 246 | 4 247 | 4 248 | 4 249 | 4 250 | 4 251 | 5 252 | 5 253 | 5 254 | 5 255 | 5 256 | 5 257 | 5 258 | 5 259 | 5 260 | 5 261 | 5 262 | 5 263 | 5 264 | 5 265 | 5 266 | 5 267 | 5 268 | 5 269 | 5 270 | 5 271 | 5 272 | 5 273 | 5 274 | 5 275 | 5 276 | 5 277 | 5 278 | 5 279 | 5 280 | 5 281 | 5 282 | 5 283 | 5 284 | 5 285 | 5 286 | 5 287 | 5 288 | 5 289 | 5 290 | 5 291 | 5 292 | 5 293 | 5 294 | 5 295 | 5 296 | 5 297 | 5 298 | 5 299 | 5 300 | 5 301 | 6 302 | 6 303 | 6 304 | 6 305 | 6 306 | 6 307 | 6 308 | 6 309 | 6 310 | 6 311 | 6 312 | 6 313 | 6 314 | 6 315 | 6 316 | 6 317 | 6 318 | 6 319 | 6 320 | 6 321 | 6 322 | 6 323 | 6 324 | 6 325 | 6 326 | 6 327 | 6 328 | 6 329 | 6 330 | 6 331 | 6 332 | 6 333 | 6 334 | 6 335 | 6 336 | 6 337 | 6 338 | 6 339 | 6 340 | 6 341 | 6 342 | 6 343 | 6 344 | 6 345 | 6 346 | 6 347 | 6 348 | 6 349 | 6 350 | 6 351 | 7 352 | 7 353 | 7 354 | 7 355 | 7 356 | 7 357 | 7 358 | 7 359 | 7 360 | 7 361 | 7 362 | 7 363 | 7 364 | 7 365 | 7 366 | 7 367 | 7 368 | 7 369 | 7 370 | 7 371 | 7 372 | 7 373 | 7 374 | 7 375 | 7 376 | 7 377 | 7 378 | 7 379 | 7 380 | 7 381 | 7 382 | 7 383 | 7 384 | 7 385 | 7 386 | 7 387 | 7 388 | 7 389 | 7 390 | 7 391 | 7 392 | 7 393 | 7 394 | 7 395 | 7 396 | 7 397 | 7 398 | 7 399 | 7 400 | 7 401 | 8 402 | 8 403 | 8 404 | 8 405 | 8 406 | 8 407 | 8 408 | 8 409 | 8 410 | 8 411 | 8 412 | 8 413 | 8 414 | 8 415 | 8 416 | 8 417 | 8 418 | 8 419 | 8 420 | 8 421 | 8 422 | 8 423 | 8 424 | 8 425 | 8 426 | 8 427 | 8 428 | 8 429 | 8 430 | 8 431 | 8 432 | 8 433 | 8 434 | 8 435 | 8 436 | 8 437 | 8 438 | 8 439 | 8 440 | 8 441 | 8 442 | 8 443 | 8 444 | 8 445 | 8 446 | 8 447 | 8 448 | 8 449 | 8 450 | 8 451 | 9 452 | 9 453 | 9 454 | 9 455 | 9 456 | 9 457 | 9 458 | 9 459 | 9 460 | 9 461 | 9 462 | 9 463 | 9 464 | 9 465 | 9 466 | 9 467 | 9 468 | 9 469 | 9 470 | 9 471 | 9 472 | 9 473 | 9 474 | 9 475 | 9 476 | 9 477 | 9 478 | 9 479 | 9 480 | 9 481 | 9 482 | 9 483 | 9 484 | 9 485 | 9 486 | 9 487 | 9 488 | 9 489 | 9 490 | 9 491 | 9 492 | 9 493 | 9 494 | 9 495 | 9 496 | 9 497 | 9 498 | 9 499 | 9 500 | 9 501 | -------------------------------------------------------------------------------- /oss_data/oss_demo_projector_config.json: -------------------------------------------------------------------------------- 1 | { 2 | "embeddings": [ 3 | { 4 | "metadataPath": "oss_data/images.tsv", 5 | "sprite": { 6 | "imagePath": "oss_data/images.png", 7 | "singleImageDim": [ 8 | 24, 9 | 24 10 | ] 11 | }, 12 | "tensorName": "images", 13 | "tensorPath": "oss_data/images.bytes", 14 | "tensorShape": [ 15 | 500, 16 | 48 17 | ] 18 | }, 19 | { 20 | "metadataPath": "oss_data/words.tsv", 21 | "tensorName": "words", 22 | "tensorPath": "oss_data/words.bytes", 23 | "tensorShape": [ 24 | 500, 25 | 48 26 | ] 27 | } 28 | ], 29 | "modelCheckpointPath": "Demo datasets" 30 | } -------------------------------------------------------------------------------- /oss_data/words.bytes: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/cnzeki/embedding-projector/5a697ff5b1a9c5aadb1548b84f7aabaaeac849e9/oss_data/words.bytes -------------------------------------------------------------------------------- /oss_data/words.tsv: -------------------------------------------------------------------------------- 1 | Red 2 | Red 3 | Red 4 | Red 5 | Red 6 | Red 7 | Red 8 | Red 9 | Red 10 | Red 11 | Red 12 | Red 13 | Red 14 | Red 15 | Red 16 | Red 17 | Red 18 | Red 19 | Red 20 | Red 21 | Red 22 | Red 23 | Red 24 | Red 25 | Red 26 | Red 27 | Red 28 | Red 29 | Red 30 | Red 31 | Red 32 | Red 33 | Red 34 | Red 35 | Red 36 | Red 37 | Red 38 | Red 39 | Red 40 | Red 41 | Red 42 | Red 43 | Red 44 | Red 45 | Red 46 | Red 47 | Red 48 | Red 49 | Red 50 | Red 51 | Orange 52 | Orange 53 | Orange 54 | Orange 55 | Orange 56 | Orange 57 | Orange 58 | Orange 59 | Orange 60 | Orange 61 | Orange 62 | Orange 63 | Orange 64 | Orange 65 | Orange 66 | Orange 67 | Orange 68 | Orange 69 | Orange 70 | Orange 71 | Orange 72 | Orange 73 | Orange 74 | Orange 75 | Orange 76 | Orange 77 | Orange 78 | Orange 79 | Orange 80 | Orange 81 | Orange 82 | Orange 83 | Orange 84 | Orange 85 | Orange 86 | Orange 87 | Orange 88 | Orange 89 | Orange 90 | Orange 91 | Orange 92 | Orange 93 | Orange 94 | Orange 95 | Orange 96 | Orange 97 | Orange 98 | Orange 99 | Orange 100 | Orange 101 | Yellow 102 | Yellow 103 | Yellow 104 | Yellow 105 | Yellow 106 | Yellow 107 | Yellow 108 | Yellow 109 | Yellow 110 | Yellow 111 | Yellow 112 | Yellow 113 | Yellow 114 | Yellow 115 | Yellow 116 | Yellow 117 | Yellow 118 | Yellow 119 | Yellow 120 | Yellow 121 | Yellow 122 | Yellow 123 | Yellow 124 | Yellow 125 | Yellow 126 | Yellow 127 | Yellow 128 | Yellow 129 | Yellow 130 | Yellow 131 | Yellow 132 | Yellow 133 | Yellow 134 | Yellow 135 | Yellow 136 | Yellow 137 | Yellow 138 | Yellow 139 | Yellow 140 | Yellow 141 | Yellow 142 | Yellow 143 | Yellow 144 | Yellow 145 | Yellow 146 | Yellow 147 | Yellow 148 | Yellow 149 | Yellow 150 | Yellow 151 | Green 152 | Green 153 | Green 154 | Green 155 | Green 156 | Green 157 | Green 158 | Green 159 | Green 160 | Green 161 | Green 162 | Green 163 | Green 164 | Green 165 | Green 166 | Green 167 | Green 168 | Green 169 | Green 170 | Green 171 | Green 172 | Green 173 | Green 174 | Green 175 | Green 176 | Green 177 | Green 178 | Green 179 | Green 180 | Green 181 | Green 182 | Green 183 | Green 184 | Green 185 | Green 186 | Green 187 | Green 188 | Green 189 | Green 190 | Green 191 | Green 192 | Green 193 | Green 194 | Green 195 | Green 196 | Green 197 | Green 198 | Green 199 | Green 200 | Green 201 | Blue 202 | Blue 203 | Blue 204 | Blue 205 | Blue 206 | Blue 207 | Blue 208 | Blue 209 | Blue 210 | Blue 211 | Blue 212 | Blue 213 | Blue 214 | Blue 215 | Blue 216 | Blue 217 | Blue 218 | Blue 219 | Blue 220 | Blue 221 | Blue 222 | Blue 223 | Blue 224 | Blue 225 | Blue 226 | Blue 227 | Blue 228 | Blue 229 | Blue 230 | Blue 231 | Blue 232 | Blue 233 | Blue 234 | Blue 235 | Blue 236 | Blue 237 | Blue 238 | Blue 239 | Blue 240 | Blue 241 | Blue 242 | Blue 243 | Blue 244 | Blue 245 | Blue 246 | Blue 247 | Blue 248 | Blue 249 | Blue 250 | Blue 251 | Violet 252 | Violet 253 | Violet 254 | Violet 255 | Violet 256 | Violet 257 | Violet 258 | Violet 259 | Violet 260 | Violet 261 | Violet 262 | Violet 263 | Violet 264 | Violet 265 | Violet 266 | Violet 267 | Violet 268 | Violet 269 | Violet 270 | Violet 271 | Violet 272 | Violet 273 | Violet 274 | Violet 275 | Violet 276 | Violet 277 | Violet 278 | Violet 279 | Violet 280 | Violet 281 | Violet 282 | Violet 283 | Violet 284 | Violet 285 | Violet 286 | Violet 287 | Violet 288 | Violet 289 | Violet 290 | Violet 291 | Violet 292 | Violet 293 | Violet 294 | Violet 295 | Violet 296 | Violet 297 | Violet 298 | Violet 299 | Violet 300 | Violet 301 | Brown 302 | Brown 303 | Brown 304 | Brown 305 | Brown 306 | Brown 307 | Brown 308 | Brown 309 | Brown 310 | Brown 311 | Brown 312 | Brown 313 | Brown 314 | Brown 315 | Brown 316 | Brown 317 | Brown 318 | Brown 319 | Brown 320 | Brown 321 | Brown 322 | Brown 323 | Brown 324 | Brown 325 | Brown 326 | Brown 327 | Brown 328 | Brown 329 | Brown 330 | Brown 331 | Brown 332 | Brown 333 | Brown 334 | Brown 335 | Brown 336 | Brown 337 | Brown 338 | Brown 339 | Brown 340 | Brown 341 | Brown 342 | Brown 343 | Brown 344 | Brown 345 | Brown 346 | Brown 347 | Brown 348 | Brown 349 | Brown 350 | Brown 351 | Black 352 | Black 353 | Black 354 | Black 355 | Black 356 | Black 357 | Black 358 | Black 359 | Black 360 | Black 361 | Black 362 | Black 363 | Black 364 | Black 365 | Black 366 | Black 367 | Black 368 | Black 369 | Black 370 | Black 371 | Black 372 | Black 373 | Black 374 | Black 375 | Black 376 | Black 377 | Black 378 | Black 379 | Black 380 | Black 381 | Black 382 | Black 383 | Black 384 | Black 385 | Black 386 | Black 387 | Black 388 | Black 389 | Black 390 | Black 391 | Black 392 | Black 393 | Black 394 | Black 395 | Black 396 | Black 397 | Black 398 | Black 399 | Black 400 | Black 401 | Grey 402 | Grey 403 | Grey 404 | Grey 405 | Grey 406 | Grey 407 | Grey 408 | Grey 409 | Grey 410 | Grey 411 | Grey 412 | Grey 413 | Grey 414 | Grey 415 | Grey 416 | Grey 417 | Grey 418 | Grey 419 | Grey 420 | Grey 421 | Grey 422 | Grey 423 | Grey 424 | Grey 425 | Grey 426 | Grey 427 | Grey 428 | Grey 429 | Grey 430 | Grey 431 | Grey 432 | Grey 433 | Grey 434 | Grey 435 | Grey 436 | Grey 437 | Grey 438 | Grey 439 | Grey 440 | Grey 441 | Grey 442 | Grey 443 | Grey 444 | Grey 445 | Grey 446 | Grey 447 | Grey 448 | Grey 449 | Grey 450 | Grey 451 | White 452 | White 453 | White 454 | White 455 | White 456 | White 457 | White 458 | White 459 | White 460 | White 461 | White 462 | White 463 | White 464 | White 465 | White 466 | White 467 | White 468 | White 469 | White 470 | White 471 | White 472 | White 473 | White 474 | White 475 | White 476 | White 477 | White 478 | White 479 | White 480 | White 481 | White 482 | White 483 | White 484 | White 485 | White 486 | White 487 | White 488 | White 489 | White 490 | White 491 | White 492 | White 493 | White 494 | White 495 | White 496 | White 497 | White 498 | White 499 | White 500 | White 501 | -------------------------------------------------------------------------------- /projector.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from __future__ import print_function 3 | import os 4 | import argparse 5 | import json 6 | 7 | import numpy as np 8 | 9 | 10 | def save_tensor_bytes(save_path, x): 11 | y = x.flatten() 12 | n = len(y) 13 | with open(save_path, 'wb') as f: 14 | for i in range(n): 15 | v = y[i] 16 | f.write(v) 17 | 18 | 19 | def save_label_tsv(save_path, x): 20 | y = x.flatten() 21 | n = len(y) 22 | with open(save_path, 'w') as f: 23 | for i in range(n): 24 | v = y[i] 25 | f.write('%s\n' % (str(v))) 26 | 27 | 28 | def create_sprite_image(images, image_size=(-1, -1)): 29 | import cv2 30 | """Returns a sprite image consisting of images passed as argument. Images should be count x width x height""" 31 | # image NHWC 32 | if isinstance(images, list): 33 | images = np.array(images) 34 | if image_size[0] > 0: 35 | img_h = image_size[0] 36 | img_w = image_size[1] 37 | else: 38 | img_h = images.shape[1] 39 | img_w = images.shape[2] 40 | img_c = images.shape[3] 41 | n_plots = int(np.ceil(np.sqrt(images.shape[0]))) 42 | 43 | spriteimage = np.ones((img_h * n_plots, img_w * n_plots, img_c)) * 255 44 | 45 | for i in range(n_plots): 46 | for j in range(n_plots): 47 | this_filter = i * n_plots + j 48 | if this_filter < images.shape[0]: 49 | this_img = images[this_filter] 50 | this_img = cv2.resize(this_img, (img_w, img_h)) 51 | this_img = this_img.reshape(img_w, img_h, img_c) 52 | if img_c > 1: 53 | this_img = cv2.cvtColor(this_img, cv2.COLOR_RGB2BGR) 54 | spriteimage[i * img_h:(i + 1) * img_h, j * img_w:(j + 1) * img_w, :] = this_img 55 | 56 | return spriteimage, [img_h, img_w] 57 | 58 | 59 | def write_image_embeddings(root, title, feats, labels, imgs=None, sprite_size=(-1, -1), mode='w+'): 60 | ''' 61 | Write embedding data for the `Embedding Project` tool to visualize 62 | :param root: root dir of `Embedding Project` tool 63 | :param title: name of the tensor 64 | :param feats: embedding tensor NxDim 65 | :param labels: labels for each sample NxNumClasses 66 | :param [optional] imgs: images in format NHWC 67 | :param [optional] sprite_size: image sprite size 68 | :param mode: 'w' -- write, 'w+' -- update or append, '+' -- append 69 | :return: None 70 | ''' 71 | import cv2 72 | prefix = 'oss_data/' + title 73 | try: 74 | os.makedirs(os.path.join(root, 'oss_data/')) 75 | except: 76 | pass 77 | tensorPath = prefix + '.bytes' 78 | metadataPath = prefix + '.tsv' 79 | if imgs is not None: 80 | imagePath = prefix + '.png' 81 | # save sprites 82 | sprite, singleImageDim = create_sprite_image(imgs, sprite_size) 83 | cv2.imwrite(os.path.join(root,imagePath), sprite) 84 | # save tensor 85 | save_tensor_bytes(os.path.join(root,tensorPath), feats) 86 | # save meta 87 | save_label_tsv(os.path.join(root,metadataPath), labels) 88 | # config 89 | config_path = os.path.join(root,'oss_data/oss_demo_projector_config.json') 90 | config = {"modelCheckpointPath": "Demo datasets", "embeddings": []} 91 | # update or append mode 92 | if mode == 'w+' or mode == '+': 93 | if os.path.exists(config_path): 94 | with open(config_path, 'r') as f: 95 | config = json.load(f) 96 | # new tensor item 97 | item = { 98 | "tensorName": title, 99 | "tensorShape": list(feats.shape), 100 | "tensorPath": tensorPath, 101 | "metadataPath": metadataPath, 102 | 103 | } 104 | if imgs is not None: 105 | item["sprite"] = { 106 | "imagePath": imagePath, 107 | "singleImageDim": list(singleImageDim) 108 | } 109 | embeddings = config["embeddings"] 110 | if mode == 'w+': 111 | # check existence 112 | id = -1 113 | for i in range(len(embeddings)): 114 | if embeddings[i]["tensorName"] == title: 115 | id = i 116 | break 117 | if id >= 0: 118 | embeddings[id] = item 119 | else: 120 | config["embeddings"].append(item) 121 | else: 122 | config["embeddings"].append(item) 123 | # write out 124 | with open(config_path, 'w') as f: 125 | json.dump(config, f, sort_keys=True, indent=4) 126 | 127 | 128 | def save_tensor_tsv(path, feats): 129 | with open(path, 'w') as f: 130 | num, dim = feats.shape 131 | for line in range(num): 132 | for d in range(dim): 133 | if d > 0: 134 | f.write('\t') 135 | v = str(feats[line][d]) 136 | f.write(v) 137 | f.write('\n') 138 | 139 | 140 | def write_tsv_embeddings(prefix, feats, labels=None): 141 | ''' 142 | Write a tensor (or meta) to a tsv file for the `Embedding Project` tool 143 | :param prefix: output file prefix 144 | :param feats: embedding tensor NxDim 145 | :param labels: meta data 146 | :return: None 147 | ''' 148 | feat_path = prefix + '_data.tsv' 149 | save_tensor_tsv(feat_path, feats) 150 | if labels is None: 151 | return 152 | dims = len(labels.shape) 153 | label_path = prefix + '_meta.tsv' 154 | if dims == 1: 155 | save_label_tsv(label_path, labels) 156 | else: 157 | save_tensor_tsv(label_path, labels) 158 | 159 | 160 | def _gen_fake_feats(num_class, total_size, feat_dim, var=0.2): 161 | num_each_class = total_size // num_class 162 | # generate centers 163 | centers = np.random.randn(num_class, feat_dim) 164 | feats = np.random.randn(total_size, feat_dim) 165 | labels = [] 166 | for i in range(total_size): 167 | cid = i // num_each_class 168 | cid = min(cid, num_class-1) 169 | feats[i] = feats[i] * var + centers[cid] 170 | labels.append(cid) 171 | labels = np.array(labels) 172 | feats = np.float32(feats) 173 | return feats, labels 174 | 175 | 176 | def demo_test_word(root): 177 | title = 'words' 178 | batch_size = 500 179 | feat_dim = 48 180 | color_names = ['Red', 'Orange', 'Yellow', 'Green', 'Blue', 'Violet', 'Brown', 'Black', 'Grey', 'White'] 181 | num_colors = len(color_names) 182 | 183 | # Fake metas 184 | feats, labels = _gen_fake_feats(num_colors, batch_size, feat_dim) 185 | metas = [color_names[labels[i]] for i in range(batch_size)] 186 | metas = np.array(metas) 187 | # write tsv files 188 | #write_tsv_embeddings(title, feats, metas) 189 | write_image_embeddings(root, title, feats, metas) 190 | 191 | 192 | def demo_test_image(root): 193 | import cv2 194 | title = 'images' 195 | num_class = 10 196 | total_size = 500 197 | feat_dim = 48 198 | feats, labels = _gen_fake_feats(num_class, total_size, feat_dim) 199 | # generate fake images 200 | imgs = [] 201 | for i in range(total_size): 202 | cid = labels[i] 203 | img = np.ones((40, 40, 1), dtype=np.uint8) * 255 204 | 205 | ox = np.random.randint(0, 25) 206 | oy = np.random.randint(20, 35) 207 | cv2.putText(img, str(cid), (ox, oy), 0, 1, (0, 0, 0)) 208 | imgs.append(img) 209 | imgs = np.stack(imgs) 210 | write_image_embeddings(root, title, feats, labels, imgs, sprite_size=(24, 24)) 211 | 212 | 213 | 214 | if __name__ == '__main__': 215 | parser = parser = argparse.ArgumentParser(description='projector', conflict_handler='resolve') 216 | parser.add_argument('--port', type=int, default=8000, help='server port') 217 | parser.add_argument('--root', default='.', type=str, help='projector root dir') 218 | parser.add_argument('--demo', default=False, type=bool, help='write demo data') 219 | args = parser.parse_args() 220 | if args.demo: 221 | demo_test_image(args.root) 222 | demo_test_word(args.root) 223 | # start server 224 | import sys 225 | import os 226 | if sys.version_info.major == 2: 227 | os.system('python -m SimpleHTTPServer %s' % args.port) 228 | else: 229 | os.system('python -m http.server %s' % args.port) 230 | --------------------------------------------------------------------------------