diff --git a/.gitignore b/.gitignore index f417c17..abb3de6 100644 --- a/.gitignore +++ b/.gitignore @@ -155,3 +155,4 @@ out-img/ converted_models/ *.pth *.onnx +.devcontainer diff --git a/README2.md b/README2.md new file mode 100644 index 0000000..8d6614d --- /dev/null +++ b/README2.md @@ -0,0 +1,20 @@ +# Spoter Embeddings + +## Creating dataset +First, make a folder where all you're videos are located. When this is done, all keypoints can be extracted from the videos using the following command. This will extract the keypoints and store them in \. +``` +python3 preprocessing.py extract --videos-folder --output-landmark +``` + +When this is done, the dataset can be created using the following command: +``` +python3 preprocessing.py create --landmarks-dataset --videos-folder --dataset-folder (--create-new-split --test-size ) +``` +The above command generates a train (and val) csv file which includes all the extracted keypoints. These can then be used to train or generates embeddings. + +## Creating Embeddings +The embeddings can be created using the following command: +``` +python3 export_embeddings.py --checkpoint --dataset --output +``` +The command above generates the embeddings for a given dataset and saves them as a csv file. \ No newline at end of file diff --git a/checkpoints/checkpoint_embed_3006.pth b/checkpoints/checkpoint_embed_3006.pth new file mode 100644 index 0000000..7123eca Binary files /dev/null and b/checkpoints/checkpoint_embed_3006.pth differ diff --git a/checkpoints/checkpoint_embed_3835.pth b/checkpoints/checkpoint_embed_3835.pth new file mode 100644 index 0000000..d3195e2 Binary files /dev/null and b/checkpoints/checkpoint_embed_3835.pth differ diff --git a/conversion_requirements.txt b/conversion_requirements.txt index 4d37abe..7526746 100644 --- a/conversion_requirements.txt +++ b/conversion_requirements.txt @@ -17,5 +17,4 @@ requests==2.28.1 onnx==1.12.0 onnx-tf==1.10.0 onnxruntime==1.12.1 -tensorflow -tensorflow-probability +coremltools==6.3.0 \ No newline at end of file diff --git a/embeddings/basic-signs/embeddings-online-dict.csv b/embeddings/basic-signs/embeddings-online-dict.csv new file mode 100644 index 0000000..2b86d35 --- /dev/null +++ b/embeddings/basic-signs/embeddings-online-dict.csv @@ -0,0 +1,91 @@ +embeddings,label_name,labels,embeddings2 +"[[ 0.01295076 -0.1074132 0.5111911 0.08452003 -0.7638924 0.6458221 + -1.3892679 -1.1791427 -0.30736607 -0.41543546 -0.6358013 0.31411174 + 0.878936 1.7265923 -0.09298562 0.12005516 0.26995468 -1.5934305 + -0.12619524 0.9111336 0.91827893 0.5948979 0.90334046 -0.84089845 + -0.29575598 -0.3024254 -0.03074584 1.4402957 -0.22309914 0.54750854 + -0.68767273 -0.0665718 ]]",BEDANKEN,0,"[0.012950755655765533, -0.10741320252418518, 0.5111911296844482, 0.08452003449201584, -0.763892412185669, 0.6458221077919006, -1.389267921447754, -1.179142713546753, -0.3073660731315613, -0.41543546319007874, -0.6358013153076172, 0.31411173939704895, 0.8789359927177429, 1.7265923023223877, -0.0929856151342392, 0.12005516141653061, 0.2699546813964844, -1.593430519104004, -0.12619523704051971, 0.9111335873603821, 0.9182789325714111, 0.5948979258537292, 0.9033404588699341, -0.8408984541893005, -0.2957559823989868, -0.30242541432380676, -0.030745841562747955, 1.440295696258545, -0.22309914231300354, 0.5475085377693176, -0.6876727342605591, -0.06657180190086365]" +"[[ 1.0287632 -0.94657993 0.7852507 -0.37169546 0.3614158 0.5360078 + -0.9989285 -0.24402924 -0.47437504 -0.17307773 0.9196662 0.5362411 + -0.50366765 0.7600024 0.6701926 -0.5288625 -0.38939306 -1.0722619 + -1.33836 1.331829 -0.6958481 0.57767504 0.87002045 -0.59203666 + -0.57576096 0.70008135 0.8224436 -0.32370126 0.5276206 -0.62150276 + -0.9639951 -0.40879178]]",GOED,1,"[1.0287631750106812, -0.9465799331665039, 0.785250723361969, -0.37169545888900757, 0.3614158034324646, 0.536007821559906, -0.9989284873008728, -0.244029238820076, -0.47437503933906555, -0.17307773232460022, 0.9196661710739136, 0.5362411141395569, -0.5036676526069641, 0.7600023746490479, 0.6701925992965698, -0.528862476348877, -0.38939306139945984, -1.072261929512024, -1.3383599519729614, 1.3318289518356323, -0.6958481073379517, 0.5776750445365906, 0.8700204491615295, -0.5920366644859314, -0.5757609605789185, 0.7000813484191895, 0.8224436044692993, -0.32370126247406006, 0.5276206135749817, -0.6215027570724487, -0.963995099067688, -0.40879178047180176]" +"[[-0.5988377 -1.6454532 0.5696761 0.27801913 -0.39687645 1.5192578 + -0.57908726 -1.9851501 1.1918951 -1.3280408 0.27390718 0.71642965 + -1.0471189 0.9825219 -0.6625729 -0.28641602 2.0109527 -0.97667754 + -2.3978348 2.666861 0.15066166 -1.6922158 0.9681514 -1.1413386 + 0.08027886 -0.6215762 -0.09759905 0.6157277 -0.01341684 0.9806912 + -0.09793527 -1.9003383 ]]",GOEDEMIDDAG,2,"[-0.598837673664093, -1.6454532146453857, 0.5696761012077332, 0.27801913022994995, -0.3968764543533325, 1.5192577838897705, -0.5790872573852539, -1.9851500988006592, 1.1918951272964478, -1.3280408382415771, 0.2739071846008301, 0.7164296507835388, -1.047118902206421, 0.9825218915939331, -0.6625729203224182, -0.28641602396965027, 2.0109527111053467, -0.9766775369644165, -2.3978347778320312, 2.666861057281494, 0.1506616622209549, -1.6922158002853394, 0.9681513905525208, -1.141338586807251, 0.08027885854244232, -0.621576189994812, -0.09759905189275742, 0.6157277226448059, -0.013416841626167297, 0.9806911945343018, -0.0979352742433548, -1.9003382921218872]" +"[[-0.11578767 0.28371835 0.8604608 -0.31061298 -0.78153455 -0.24914108 + -2.443886 -0.3267159 0.29237527 -0.7879326 -0.12082034 0.32809633 + -1.0938975 0.7125962 0.49117383 -0.3119098 0.15369919 0.39569452 + -1.18983 1.2498406 -0.38752007 -0.54896545 -0.620718 0.02654967 + -0.10994279 -0.39713857 -0.18035033 -1.554728 0.12178666 -0.5698373 + 1.1097438 -1.2350351 ]]",GOEDEMORGEN,3,"[-0.11578767001628876, 0.2837183475494385, 0.8604608178138733, -0.3106129765510559, -0.7815345525741577, -0.24914108216762543, -2.4438860416412354, -0.326715886592865, 0.29237526655197144, -0.7879325747489929, -0.12082034349441528, 0.32809633016586304, -1.0938974618911743, 0.7125961780548096, 0.4911738336086273, -0.3119097948074341, 0.15369918942451477, 0.3956945240497589, -1.18982994556427, 1.2498406171798706, -0.38752007484436035, -0.5489654541015625, -0.6207180023193359, 0.02654966711997986, -0.10994279384613037, -0.3971385657787323, -0.18035033345222473, -1.5547280311584473, 0.12178666144609451, -0.5698372721672058, 1.1097438335418701, -1.2350350618362427]" +"[[-0.9557147 0.03368077 1.0923759 -0.41077584 -1.6992741 -0.59566665 + -1.1562454 0.8824008 -0.25122 0.625515 1.6720039 0.5639413 + -0.35894975 -0.05896414 0.84854823 0.4072139 -1.1763096 -1.5435666 + -1.5066593 0.66299886 -1.3121625 1.4410669 -0.6341419 -0.4873513 + 0.8113033 -0.01502099 0.32262453 2.5855515 -0.7294207 0.7582124 + -0.1386989 -0.17011487]]",GOEDENACHT,4,"[-0.9557147026062012, 0.03368076682090759, 1.0923758745193481, -0.4107758402824402, -1.6992740631103516, -0.5956666469573975, -1.1562453508377075, 0.8824008107185364, -0.2512199878692627, 0.6255149841308594, 1.6720038652420044, 0.5639412999153137, -0.35894975066185, -0.058964136987924576, 0.8485482335090637, 0.40721389651298523, -1.176309585571289, -1.5435665845870972, -1.5066592693328857, 0.6629988551139832, -1.3121625185012817, 1.441066861152649, -0.6341419219970703, -0.48735129833221436, 0.8113033175468445, -0.015020990744233131, 0.322624534368515, 2.5855515003204346, -0.7294207215309143, 0.7582123875617981, -0.13869890570640564, -0.17011487483978271]" +"[[ 0.13687058 -1.099074 1.1867181 -1.2216619 -0.965039 -0.6957289 + -1.6400954 0.7317837 0.5887569 0.18756844 1.4867041 0.63357025 + -1.5853169 0.4157976 0.76919526 0.08512082 -1.0937556 -0.07339763 + -2.8580015 1.6835626 -1.4538742 1.2302468 -0.49323368 -0.81243044 + -0.11378415 -0.09592562 0.31165385 -0.32617894 0.02981896 -0.01485211 + 1.1880591 -0.40726334]]",GOEDENAVOND,5,"[0.1368705779314041, -1.0990740060806274, 1.1867181062698364, -1.221661925315857, -0.9650390148162842, -0.6957288980484009, -1.6400953531265259, 0.7317836880683899, 0.5887569189071655, 0.18756844103336334, 1.4867041110992432, 0.6335702538490295, -1.5853168964385986, 0.4157975912094116, 0.7691952586174011, 0.08512081950902939, -1.0937556028366089, -0.07339762896299362, -2.858001470565796, 1.6835626363754272, -1.4538742303848267, 1.2302467823028564, -0.49323368072509766, -0.8124304413795471, -0.11378414928913116, -0.09592562168836594, 0.31165385246276855, -0.3261789381504059, 0.029818959534168243, -0.014852114021778107, 1.1880590915679932, -0.4072633385658264]" +"[[ 1.3673804 1.7083405 0.58775777 0.26056585 -0.29101595 -0.69954723 + 0.45681733 1.6908163 0.02684825 0.7537957 2.2054908 0.28831166 + -1.5712252 -1.6869793 0.8009781 -0.51264 -1.3544708 -0.72502786 + -0.31009498 -0.23777717 -1.7524906 1.6333568 1.5263942 0.22317217 + -0.40576094 2.2136266 1.4030526 -1.0599502 0.8686069 -1.1141618 + -0.01899157 -1.2256656 ]]",JA,6,"[1.3673803806304932, 1.7083405256271362, 0.5877577662467957, 0.260565847158432, -0.29101595282554626, -0.6995472311973572, 0.4568173289299011, 1.6908162832260132, 0.02684824913740158, 0.7537956833839417, 2.205490827560425, 0.2883116602897644, -1.5712251663208008, -1.6869792938232422, 0.8009781241416931, -0.5126399993896484, -1.3544708490371704, -0.725027859210968, -0.3100949823856354, -0.23777717351913452, -1.7524906396865845, 1.6333568096160889, 1.526394248008728, 0.2231721729040146, -0.4057609438896179, 2.2136266231536865, 1.403052568435669, -1.0599502325057983, 0.8686069250106812, -1.1141618490219116, -0.01899157091975212, -1.22566556930542]" +"[[ 1.8208723 -0.59625745 0.15060106 -0.23053254 1.0344827 1.7016335 + 0.1968202 -0.48188782 0.3004466 -0.53336793 0.89704275 0.26869062 + -1.0112952 0.0343281 0.32149622 -0.8189871 0.33571526 -0.57059044 + -0.7577773 1.4743904 -0.01735806 -0.4116615 2.3637483 -0.6602343 + -0.6101709 1.8139238 1.041902 -0.9006022 0.6082014 -0.23616463 + -0.62204087 -0.524899 ]]",LINKS,7,"[1.8208723068237305, -0.5962574481964111, 0.15060105919837952, -0.23053254187107086, 1.034482717514038, 1.7016334533691406, 0.1968201994895935, -0.4818878173828125, 0.30044659972190857, -0.533367931842804, 0.8970427513122559, 0.2686906158924103, -1.011295199394226, 0.034328099340200424, 0.32149621844291687, -0.8189870715141296, 0.33571526408195496, -0.5705904364585876, -0.7577772736549377, 1.4743903875350952, -0.01735806092619896, -0.4116615056991577, 2.36374831199646, -0.660234272480011, -0.6101709008216858, 1.8139238357543945, 1.04190194606781, -0.9006022214889526, 0.6082013845443726, -0.2361646294593811, -0.622040867805481, -0.5248990058898926]" +"[[ 1.3968729 2.340378 0.567814 0.5684975 -0.8795973 -1.0090083 + -0.01301649 1.9747832 -0.3257468 0.76960254 1.402801 0.39424965 + 0.06948688 -0.51904166 1.0961802 -0.34335235 -1.8730735 -1.0801809 + 0.2751789 -0.96998405 -1.9822828 2.1046805 1.3094746 0.5770473 + -0.19693638 1.8973799 0.99536693 0.2668684 0.28499565 -0.9838486 + -0.19578832 -0.46982569]]",NEE,8,"[1.396872878074646, 2.3403780460357666, 0.5678139925003052, 0.5684974789619446, -0.8795973062515259, -1.0090082883834839, -0.013016488403081894, 1.974783182144165, -0.3257468044757843, 0.7696025371551514, 1.4028010368347168, 0.39424964785575867, 0.06948687881231308, -0.5190416574478149, 1.0961802005767822, -0.343352347612381, -1.8730734586715698, -1.0801808834075928, 0.2751789093017578, -0.9699840545654297, -1.9822827577590942, 2.1046805381774902, 1.3094745874404907, 0.5770472884178162, -0.19693638384342194, 1.8973798751831055, 0.9953669309616089, 0.2668684124946594, 0.2849956452846527, -0.9838485717773438, -0.19578832387924194, -0.4698256850242615]" +"[[ 1.8143647 -0.9315294 0.28706568 -1.0938002 -0.2451038 0.42331484 + -0.31653756 0.00268575 0.89822304 1.0639151 1.7406261 1.1707302 + -1.6639041 0.30558044 0.22984704 -1.0260515 -0.08818819 -0.14696571 + -1.5269523 0.55015475 -0.12936743 0.93951946 1.3917109 -0.62517196 + -1.3869016 1.4833041 1.4647726 -1.0285482 -0.3704591 1.4672718 + 0.40315557 0.04754414]]",RECHTS,9,"[1.8143646717071533, -0.9315294027328491, 0.28706568479537964, -1.0938001871109009, -0.24510380625724792, 0.4233148396015167, -0.3165375590324402, 0.002685748040676117, 0.8982230424880981, 1.0639151334762573, 1.7406260967254639, 1.1707302331924438, -1.663904070854187, 0.3055804371833801, 0.2298470437526703, -1.0260515213012695, -0.08818819373846054, -0.14696571230888367, -1.5269522666931152, 0.5501547455787659, -0.1293674260377884, 0.939519464969635, 1.391710877418518, -0.625171959400177, -1.386901617050171, 1.4833041429519653, 1.4647725820541382, -1.028548240661621, -0.37045910954475403, 1.4672718048095703, 0.4031555652618408, 0.04754413664340973]" +"[[ 1.681777 -0.79441184 0.07110912 -0.01304399 1.110081 2.1115103 + -0.27283993 -1.5035654 0.55927217 -0.9903696 0.05718866 0.19891238 + -0.7289791 0.71738267 -0.41428873 -0.58389515 1.1087953 -0.6616588 + -0.6330258 1.5830203 0.60013187 -1.0640136 2.3917787 -0.70012283 + -0.7359694 0.9847692 0.94458187 -0.5915589 0.3662199 0.39359796 + -0.6499281 -0.30565363]]",SALUUT,10,"[1.681777000427246, -0.794411838054657, 0.07110912352800369, -0.013043994084000587, 1.1100809574127197, 2.1115102767944336, -0.2728399336338043, -1.5035654306411743, 0.5592721700668335, -0.9903696179389954, 0.05718865618109703, 0.1989123821258545, -0.7289791107177734, 0.7173826694488525, -0.414288729429245, -0.5838951468467712, 1.1087952852249146, -0.6616588234901428, -0.6330258250236511, 1.5830203294754028, 0.6001318693161011, -1.0640136003494263, 2.3917787075042725, -0.7001228332519531, -0.7359694242477417, 0.9847692251205444, 0.9445818662643433, -0.5915588736534119, 0.3662199079990387, 0.39359796047210693, -0.649928092956543, -0.3056536316871643]" +"[[ 0.56773967 -0.6260798 0.23771092 -0.10016712 0.86817515 0.92371875 + -0.16313902 -0.3785063 -0.41955388 -0.586288 -0.33712262 -0.07999519 + -0.98128295 0.18787864 -0.36432508 -0.06605241 0.00710656 -0.36359936 + -0.00642961 1.257384 0.64647824 -1.1618687 1.3707273 -0.46546304 + -0.14522405 0.29306158 0.41898865 -0.12311607 0.24604419 -0.48434448 + -0.79076517 0.753163 ]]",SLECHT,11,"[0.5677396655082703, -0.626079797744751, 0.23771092295646667, -0.10016711801290512, 0.8681751489639282, 0.9237187504768372, -0.16313901543617249, -0.37850630283355713, -0.41955387592315674, -0.5862879753112793, -0.3371226191520691, -0.07999519258737564, -0.9812829494476318, 0.18787863850593567, -0.36432507634162903, -0.06605241447687149, 0.007106557488441467, -0.36359935998916626, -0.006429608445614576, 1.257383942604065, 0.6464782357215881, -1.161868691444397, 1.370727300643921, -0.4654630422592163, -0.14522404968738556, 0.29306158423423767, 0.4189886450767517, -0.12311606854200363, 0.24604418873786926, -0.484344482421875, -0.7907651662826538, 0.7531629800796509]" +"[[ 0.9251696 -2.8536968 0.6521715 -1.8256427 0.44136596 0.33299872 + -1.4674346 -0.7897836 0.47153538 0.1437631 0.7396151 1.0851275 + -1.2100328 1.626544 0.69652647 0.0048107 0.13927785 -0.63199776 + -3.0121155 2.1738136 -0.7935879 0.2744053 0.281097 -1.0899199 + -0.7062637 -0.04824958 0.3436054 -0.69366074 0.22799876 0.8932708 + 0.51823634 -0.08065546]]",SMAKELIJK,12,"[0.9251695871353149, -2.853696823120117, 0.6521714925765991, -1.825642704963684, 0.44136595726013184, 0.33299872279167175, -1.4674346446990967, -0.7897835969924927, 0.4715353846549988, 0.14376309514045715, 0.7396150827407837, 1.0851274728775024, -1.2100328207015991, 1.6265439987182617, 0.6965264678001404, 0.004810698330402374, 0.139277845621109, -0.6319977641105652, -3.012115478515625, 2.173813581466675, -0.7935879230499268, 0.274405300617218, 0.2810969948768616, -1.089919924736023, -0.7062637209892273, -0.04824957996606827, 0.3436053991317749, -0.6936607360839844, 0.2279987633228302, 0.8932707905769348, 0.5182363390922546, -0.08065545558929443]" +"[[-1.460053 2.9393604 0.5533726 1.3786266 -1.5367306 -1.2867874 + -1.2442408 0.62938243 -0.7092637 -1.0278705 -1.3296479 -0.8105969 + 0.69997054 0.16618343 0.5113248 0.15563956 -0.4815752 -0.10130378 + 1.4232539 -1.2767068 -0.3229611 0.45140803 -1.3901687 1.0403453 + 1.454544 -1.308266 -1.145199 0.661388 -0.20519465 -1.0480955 + -0.04290716 -0.36275834]]",SORRY,13,"[-1.4600529670715332, 2.9393603801727295, 0.5533726215362549, 1.3786265850067139, -1.5367306470870972, -1.2867873907089233, -1.2442407608032227, 0.6293824315071106, -0.7092636823654175, -1.027870535850525, -1.3296478986740112, -0.8105968832969666, 0.699970543384552, 0.16618342697620392, 0.5113248229026794, 0.15563955903053284, -0.4815751910209656, -0.10130377858877182, 1.4232538938522339, -1.2767068147659302, -0.32296109199523926, 0.4514080286026001, -1.3901686668395996, 1.040345311164856, 1.454543948173523, -1.308266043663025, -1.145198941230774, 0.6613879799842834, -0.2051946520805359, -1.048095464706421, -0.04290715977549553, -0.3627583384513855]" +"[[ 1.0733138 -0.66348886 0.36737278 0.01765811 0.5730918 1.7617474 + -0.45580968 -1.0973133 0.4730748 -0.4665998 0.48594972 0.548745 + -0.91712314 0.4846773 0.3774526 -0.5996147 0.28750947 -1.0166394 + -0.6239797 1.7935088 -0.03666669 -0.51941574 2.045076 -0.9045104 + -0.6312211 0.9698636 1.0522215 -0.14772844 0.7406032 0.2901447 + -0.48710442 -0.4619298 ]]",TOT-ZIENS,14,"[1.07331383228302, -0.6634888648986816, 0.3673727810382843, 0.017658105120062828, 0.5730918049812317, 1.7617473602294922, -0.45580968260765076, -1.0973132848739624, 0.4730747938156128, -0.46659979224205017, 0.48594972491264343, 0.5487449765205383, -0.9171231389045715, 0.4846773147583008, 0.3774526119232178, -0.599614679813385, 0.2875094711780548, -1.0166393518447876, -0.6239796876907349, 1.793508768081665, -0.036666687577962875, -0.5194157361984253, 2.0450758934020996, -0.9045103788375854, -0.6312211155891418, 0.9698635935783386, 1.0522215366363525, -0.14772844314575195, 0.7406032085418701, 0.2901447117328644, -0.4871044158935547, -0.4619297981262207]" diff --git a/export_embeddings.py b/export_embeddings.py new file mode 100644 index 0000000..c305308 --- /dev/null +++ b/export_embeddings.py @@ -0,0 +1,97 @@ +import multiprocessing +import os +import torch +import argparse +from datasets.dataset_loader import LocalDatasetLoader +from datasets.embedding_dataset import SLREmbeddingDataset +from torch.utils.data import DataLoader +from datasets import SLREmbeddingDataset, collate_fn_padd +from models.spoter_embedding_model import SPOTER_EMBEDDINGS +import numpy as np +import random +import pandas as pd + +seed = 43 +random.seed(seed) +np.random.seed(seed) +os.environ["PYTHONHASHSEED"] = str(seed) +torch.manual_seed(seed) +torch.cuda.manual_seed(seed) +torch.cuda.manual_seed_all(seed) +torch.backends.cudnn.deterministic = True +torch.use_deterministic_algorithms(True) +generator = torch.Generator() +generator.manual_seed(seed) + +def seed_worker(worker_id): + worker_seed = torch.initial_seed() % 2**32 + np.random.seed(worker_seed) + random.seed(worker_seed) + +generator = torch.Generator() +generator.manual_seed(seed) +import os +os.environ["CUBLAS_WORKSPACE_CONFIG"] = ":4096:8" + +def parse_args(): + parser = argparse.ArgumentParser(description='Export embeddings') + parser.add_argument('--checkpoint', type=str, default=None, help='Path to checkpoint') + parser.add_argument('--output', type=str, default=None, help='Path to output') + parser.add_argument('--dataset', type=str, default=None, help='Path to data') + parser.add_argument('--format', type=str, default='csv', help='Format of the output file (csv, json)') + args = parser.parse_args() + return args + +args = parse_args() + +device = torch.device("cpu") +if torch.cuda.is_available(): + device = torch.device("cuda") + +# load the model +checkpoint = torch.load(args.checkpoint, map_location=device) + +model = SPOTER_EMBEDDINGS( + features=checkpoint["config_args"].vector_length, + hidden_dim=checkpoint["config_args"].hidden_dim, + norm_emb=checkpoint["config_args"].normalize_embeddings, +).to(device) + +model.load_state_dict(checkpoint["state_dict"]) + +dataset_loader = LocalDatasetLoader() +dataset = SLREmbeddingDataset(args.dataset, triplet=False, augmentations=False) + +data_loader = DataLoader( + dataset, + batch_size=1, + shuffle=False, + collate_fn=collate_fn_padd, + pin_memory=torch.cuda.is_available(), + #num_workers=0, # Uncomment this line (and comment out next line) if you want to disable multithreading + num_workers=multiprocessing.cpu_count(), + worker_init_fn=seed_worker, + generator=generator, + ) + +embeddings = [] +k = 0 +with torch.no_grad(): + for i, (inputs, labels, masks) in enumerate(data_loader): + k += 1 + inputs = inputs.to(device) + masks = masks.to(device) + outputs = model(inputs, masks) + + for n in range(outputs.shape[0]): + embeddings.append(outputs[n].cpu().numpy()) + +df = pd.read_csv(args.dataset) +df["embeddings"] = embeddings +df = df[['embeddings', 'label_name', 'labels']] +df['embeddings'] = df['embeddings'].apply(lambda x: x.tolist()[0]) + +if args.format == 'json': + df.to_json(args.output, orient='records') +elif args.format == 'csv': + df.to_csv(args.output, index=False) \ No newline at end of file diff --git a/export_model.py b/export_model.py new file mode 100644 index 0000000..51163f2 --- /dev/null +++ b/export_model.py @@ -0,0 +1,67 @@ +# to run this script, you need torch 1.13.1 and torchvision 0.14.1 + +import numpy as np +import onnx +import torch +import torchvision +import os + +from models.spoter_embedding_model import SPOTER_EMBEDDINGS + +# set parameters of the model +model_name = 'fingerspelling_embedding_model' + +# load PyTorch model from .pth file + +device = torch.device("cpu") +# if torch.cuda.is_available(): +# device = torch.device("cuda") + +CHECKPOINT_PATH = "checkpoints/fingerspelling_checkpoint.pth" +checkpoint = torch.load(CHECKPOINT_PATH, map_location=device) + +model = SPOTER_EMBEDDINGS( + features=checkpoint["config_args"].vector_length, + hidden_dim=checkpoint["config_args"].hidden_dim, + norm_emb=checkpoint["config_args"].normalize_embeddings, +).to(device) +model.load_state_dict(checkpoint["state_dict"]) +# set model to evaluation mode +model.eval() + +dummy_input = torch.randn(1, 10, 54, 2) + +# check if models folder exists +if not os.path.exists('out-models'): + os.makedirs('out-models') + +for model_export in ["onnx", "coreml"]: + if model_export == "coreml": + # set device for dummy input + dummy_input = dummy_input.to(device) + traced_model = torch.jit.trace(model, dummy_input) + + out = traced_model(dummy_input) + import coremltools as ct + + # Convert to Core ML + coreml_model = ct.convert( + traced_model, + inputs=[ct.TensorType(name="input", shape=dummy_input.shape)], + ) + + # Save Core ML model + coreml_model.save("out-models/" + model_name + ".mlmodel") + else: + # set device for dummy input + dummy_input = dummy_input.to(device) + + torch.onnx.export(model, # model being run + dummy_input, # model input (or a tuple for multiple inputs) + 'out-models/' + model_name + '.onnx', # where to save the model (can be a file or file-like object) + export_params=True, # store the trained parameter weights inside the model file + opset_version=9, # the ONNX version to export the model to + do_constant_folding=True, # whether to execute constant folding for optimization + input_names = ['X'], # the model's input names + output_names = ['Y'] # the model's output names + ) \ No newline at end of file diff --git a/hyperparam_opt.py b/hyperparam_opt.py new file mode 100644 index 0000000..a7caabe --- /dev/null +++ b/hyperparam_opt.py @@ -0,0 +1,73 @@ +from clearml.automation import UniformParameterRange, UniformIntegerParameterRange, DiscreteParameterRange +from clearml.automation import HyperParameterOptimizer +from clearml.automation.optuna import OptimizerOptuna +from optuna.pruners import HyperbandPruner, MedianPruner +from clearml import Task + +task = Task.init( + project_name='SpoterEmbedding', + task_name='Automatic Hyper-Parameter Optimization', + task_type=Task.TaskTypes.optimizer, + reuse_last_task_id=False +) + + +optimizer = HyperParameterOptimizer( + # specifying the task to be optimized, task must be in system already so it can be cloned + base_task_id="4504e0b3ec6745249d3d4c94d3d40652", + # setting the hyperparameters to optimize + hyper_parameters=[ + # epochs: + DiscreteParameterRange('Args/epochs', [200]), + # learning rate + UniformParameterRange('Args/lr', 0.000001, 0.01), + # optimizer + DiscreteParameterRange('Args/optimizer', ['ADAM', 'SGD']), + # vector length + UniformIntegerParameterRange('Args/vector_length', 10, 100), + ], + # setting the objective metric we want to maximize/minimize + objective_metric_title='train_loss', + objective_metric_series='loss', + objective_metric_sign='min', + + # setting optimizer + optimizer_class=OptimizerOptuna, + + # configuring optimization parameters + execution_queue='default', + optimization_time_limit=360, + compute_time_limit=480, + total_max_jobs=20, + min_iteration_per_job=0, + max_iteration_per_job=150000, + pool_period_min=0.1, + + save_top_k_tasks_only=3, + optuna_pruner=MedianPruner(), + + ) + +def job_complete_callback( + job_id, # type: str + objective_value, # type: float + objective_iteration, # type: int + job_parameters, # type: dict + top_performance_job_id # type: str +): + print('Job completed!', job_id, objective_value, objective_iteration, job_parameters) + if job_id == top_performance_job_id: + print('WOOT WOOT we broke the record! Objective reached {}'.format(objective_value)) + +task.execute_remotely(queue_name='hypertuning', exit_process=True) + +optimizer.set_report_period(0.3) + +optimizer.start(job_complete_callback=job_complete_callback) + +optimizer.wait() + +top_exp = optimizer.get_top_experiments(top_k=3) +print([t.id for t in top_exp]) + +optimizer.stop() \ No newline at end of file diff --git a/models/utils.py b/models/utils.py index 498affa..899f421 100644 --- a/models/utils.py +++ b/models/utils.py @@ -88,9 +88,10 @@ def train_epoch_embedding_online(model, epoch_iters, train_loader, val_loader, c if enable_batch_sorting: if labels_size < train_loader.batch_size: trim_count = labels_size % mini_batch - inputs = inputs[:-trim_count] - labels = labels[:-trim_count] - masks = masks[:-trim_count] + if trim_count > 0: + inputs = inputs[:-trim_count] + labels = labels[:-trim_count] + masks = masks[:-trim_count] embeddings = None with torch.no_grad(): for j in range(batch_loop_count): diff --git a/normalization/blazepose_mapping.py b/normalization/blazepose_mapping.py index 666aba7..ad24b55 100644 --- a/normalization/blazepose_mapping.py +++ b/normalization/blazepose_mapping.py @@ -61,20 +61,25 @@ def map_blazepose_keypoint(column): return f"{mapped}_{hand}{suffix}" -def map_blazepose_df(df): +def map_blazepose_df(df, rename=True): + to_drop = [] + if rename: + renamings = {} + for column in df.columns: + mapped_column = map_blazepose_keypoint(column) + if mapped_column: + renamings[column] = mapped_column + else: + to_drop.append(column) + df = df.rename(columns=renamings) + for index, row in df.iterrows(): + sequence_size = len(row["leftEar_Y"]) lsx = row["leftShoulder_X"] rsx = row["rightShoulder_X"] lsy = row["leftShoulder_Y"] rsy = row["rightShoulder_Y"] - # convert all to list - lsx = lsx[1:-1].split(",") - rsx = rsx[1:-1].split(",") - lsy = lsy[1:-1].split(",") - rsy = rsy[1:-1].split(",") - sequence_size = len(lsx) - neck_x = [] neck_y = [] # Treat each element of the sequence (analyzed frame) individually @@ -84,4 +89,5 @@ def map_blazepose_df(df): df.loc[index, "neck_X"] = str(neck_x) df.loc[index, "neck_Y"] = str(neck_y) - return df + df.drop(columns=to_drop, inplace=True) + return df \ No newline at end of file diff --git a/normalization/main.py b/normalization/main.py index 9f4231f..2ed4891 100644 --- a/normalization/main.py +++ b/normalization/main.py @@ -5,30 +5,30 @@ import pandas as pd from normalization.hand_normalization import normalize_hands_full from normalization.body_normalization import normalize_body_full -DATASET_PATH = './data/wlasl' +DATASET_PATH = './data/processed' # Load the dataset -df = pd.read_csv(os.path.join(DATASET_PATH, "WLASL100_train.csv"), encoding="utf-8") +df = pd.read_csv(os.path.join(DATASET_PATH, "spoter_train.csv"), encoding="utf-8") print(df.head()) print(df.columns) # Retrieve metadata -video_size_heights = df["video_height"].to_list() -video_size_widths = df["video_width"].to_list() +# video_size_heights = df["video_height"].to_list() +# video_size_widths = df["video_width"].to_list() # Delete redundant (non-related) properties -del df["video_height"] -del df["video_width"] +# del df["video_height"] +# del df["video_width"] # Temporarily remove other relevant metadata labels = df["labels"].to_list() -video_fps = df["fps"].to_list() +signs = df["sign"].to_list() + del df["labels"] -del df["fps"] -del df["split"] -del df["video_id"] -del df["label_name"] -del df["length"] +del df["sign"] +del df["path"] +del df["participant_id"] +del df["sequence_id"] # Convert the strings into lists @@ -41,7 +41,7 @@ for column in df.columns: # Perform the normalizations df = normalize_hands_full(df) -df, invalid_row_indexes = normalize_body_full(df) +# df, invalid_row_indexes = normalize_body_full(df) # Clear lists of items from deleted rows # labels = [t for i, t in enumerate(labels) if i not in invalid_row_indexes] @@ -49,6 +49,6 @@ df, invalid_row_indexes = normalize_body_full(df) # Return the metadata back to the dataset df["labels"] = labels -df["fps"] = video_fps +df["sign"] = signs -df.to_csv(os.path.join(DATASET_PATH, "wlasl_train_norm.csv"), encoding="utf-8", index=False) +df.to_csv(os.path.join(DATASET_PATH, "spoter_train_norm.csv"), encoding="utf-8", index=False) diff --git a/notebooks/embeddings_evaluation.ipynb b/notebooks/embeddings_evaluation.ipynb index f04a1c0..d012c83 100644 --- a/notebooks/embeddings_evaluation.ipynb +++ b/notebooks/embeddings_evaluation.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 5, "id": "c20f7fd5", "metadata": {}, "outputs": [], @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 6, "id": "ada032d0", "metadata": {}, "outputs": [], @@ -22,13 +22,12 @@ "import os\n", "import os.path as op\n", "import pandas as pd\n", - "import json\n", - "import base64" + "import json" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 7, "id": "05682e73", "metadata": {}, "outputs": [], @@ -38,7 +37,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 8, "id": "fede7684", "metadata": {}, "outputs": [], @@ -48,7 +47,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 9, "id": "ce531994", "metadata": {}, "outputs": [], @@ -64,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 10, "id": "f4a2d672", "metadata": {}, "outputs": [], @@ -87,17 +86,17 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 11, "id": "1d9db764", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 7, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -119,7 +118,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 12, "id": "71224139", "metadata": {}, "outputs": [], @@ -155,7 +154,7 @@ "# checkpoint = torch.load(model.get_weights())\n", "\n", "## Set your path to checkoint here\n", - "CHECKPOINT_PATH = \"../out-checkpoints/augment_rotate_75_x8/checkpoint_embed_6.pth\"\n", + "CHECKPOINT_PATH = \"../out-checkpoints/augment_rotate_75_x8/checkpoint_embed_1105.pth\"\n", "checkpoint = torch.load(CHECKPOINT_PATH, map_location=device)\n", "\n", "model = SPOTER_EMBEDDINGS(\n", @@ -169,27 +168,28 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 24, "id": "ba6b58f0", "metadata": {}, "outputs": [], "source": [ - "SL_DATASET = 'wlasl' # or 'lsa'\n", - "if SL_DATASET == 'wlasl':\n", + "SL_DATASET = 'basic-signs' # or 'wlasl'\n", + "\n", + "if SL_DATASET == 'fingerspelling':\n", + " dataset_name = \"fingerspelling\"\n", + " split_dataset_path = \"fingerspelling_{}.csv\"\n", + "elif SL_DATASET == 'wlasl':\n", " dataset_name = \"wlasl\"\n", - " num_classes = 100\n", - " split_dataset_path = \"WLASL100_train.csv\"\n", - "else:\n", - " dataset_name = \"lsa64_mapped_mediapipe_only_landmarks_25fps\"\n", - " num_classes = 64\n", - " split_dataset_path = \"LSA64_{}.csv\"\n", - " \n", + " split_dataset_path = \"WLASL100_{}.csv\"\n", + "elif SL_DATASET == 'basic-signs':\n", + " dataset_name = \"basic-signs\"\n", + " split_dataset_path = \"basic-signs_{}.csv\"\n", " " ] }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 25, "id": "5643a72c", "metadata": {}, "outputs": [], @@ -228,7 +228,7 @@ "outputs": [], "source": [ "dataloaders = {}\n", - "splits = ['train', 'val']\n", + "splits = ['train', 'val']\n", "dfs = {}\n", "for split in splits:\n", " split_set_path = op.join(dataset_folder, split_dataset_path.format(split))\n", @@ -269,6 +269,8 @@ " for i, (inputs, labels, masks) in enumerate(dataloader):\n", " k += 1\n", " inputs = inputs.to(device)\n", + " \n", + "\n", " masks = masks.to(device)\n", " outputs = model(inputs, masks)\n", " for n in range(outputs.shape[0]):\n", @@ -285,7 +287,7 @@ { "data": { "text/plain": [ - "(810, 810)" + "(164, 164)" ] }, "execution_count": 19, @@ -299,7 +301,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "ab83c6e2", "metadata": { "lines_to_next_cell": 2 @@ -311,6 +313,70 @@ " df['embeddings'] = embeddings_split[split]" ] }, + { + "cell_type": "code", + "execution_count": 26, + "id": "0b9fb9c2", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 [1.7327625, -3.015248, -1.4775522, -0.7505071,...\n", + "1 [2.0936582, -0.596195, -0.7918601, -0.15896143...\n", + "2 [-1.4007742, -0.9608915, 1.3294879, -0.5185398...\n", + "3 [1.3280737, -3.299126, -1.0110444, -1.2528414,...\n", + "4 [-0.071124956, -0.79259753, 0.7182858, 0.38130...\n", + " ... \n", + "159 [-1.5968355, 1.9617733, 0.28859574, 1.256657, ...\n", + "160 [0.44801116, -1.8377966, 1.1004394, -1.195648,...\n", + "161 [2.0584257, 1.6986116, 0.5129896, 0.27279535, ...\n", + "162 [1.6695516, -2.967027, -1.5715427, -0.77170163...\n", + "163 [1.4977738, -2.6278958, -1.6123883, -0.8420623...\n", + "Name: embeddings, Length: 164, dtype: object\n", + "0 TOT-ZIENS\n", + "1 GOED\n", + "2 GOEDENACHT\n", + "3 NEE\n", + "4 SLECHT\n", + " ... \n", + "159 SORRY\n", + "160 GOEDEMORGEN\n", + "161 LINKS\n", + "162 TOT-ZIENS\n", + "163 GOED\n", + "Name: label_name, Length: 164, dtype: object\n", + "0 0\n", + "1 1\n", + "2 2\n", + "3 3\n", + "4 4\n", + " ..\n", + "159 7\n", + "160 5\n", + "161 13\n", + "162 0\n", + "163 1\n", + "Name: labels, Length: 164, dtype: int64\n" + ] + } + ], + "source": [ + "print(dfs['train'][\"embeddings\"])\n", + "print(dfs['train'][\"label_name\"])\n", + "print(dfs['train'][\"labels\"])\n", + "\n", + "# only keep these columns\n", + "dfs['train'] = dfs['train'][['embeddings', 'label_name', 'labels']]\n", + "\n", + "# convert embeddings to string\n", + "dfs['train']['embeddings2'] = dfs['train']['embeddings'].apply(lambda x: x.tolist())\n", + "\n", + "# save the dfs['train']\n", + "dfs['train'].to_csv(f'../data/{dataset_name}/embeddings.csv', index=False)" + ] + }, { "cell_type": "markdown", "id": "2951638d", @@ -322,7 +388,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "id": "7399b8ae", "metadata": {}, "outputs": [ @@ -331,16 +397,16 @@ "output_type": "stream", "text": [ "Using centroids only\n", - "Top-1 accuracy: 5.19 %\n", - "Top-5 embeddings class match: 17.65 % (Picks any class in the 5 closest embeddings)\n", + "Top-1 accuracy: 80.00 %\n", + "Top-5 embeddings class match: 93.33 % (Picks any class in the 5 closest embeddings)\n", "\n", "################################\n", "\n", "Using all embeddings\n", - "Top-1 accuracy: 5.31 %\n", - "5-nn accuracy: 5.56 % (Picks the class that appears most often in the 5 closest embeddings)\n", - "Top-5 embeddings class match: 15.43 % (Picks any class in the 5 closest embeddings)\n", - "Top-5 unique class match: 15.56 % (Picks the 5 closest distinct classes)\n", + "Top-1 accuracy: 80.00 %\n", + "5-nn accuracy: 80.00 % (Picks the class that appears most often in the 5 closest embeddings)\n", + "Top-5 embeddings class match: 86.67 % (Picks any class in the 5 closest embeddings)\n", + "Top-5 unique class match: 93.33 % (Picks the 5 closest distinct classes)\n", "\n", "################################\n", "\n" @@ -375,13 +441,13 @@ " sorted_labels = labels[argsort]\n", " if sorted_labels[0] == true_label:\n", " top1 += 1\n", - " if use_centroids:\n", - " good_samples.append(df_val.loc[i, 'video_id'])\n", - " else:\n", - " good_samples.append((df_val.loc[i, 'video_id'],\n", - " df_train.loc[argsort[0], 'video_id'],\n", - " i,\n", - " argsort[0]))\n", + " # if use_centroids:\n", + " # good_samples.append(df_val.loc[i, 'video_id'])\n", + " # else:\n", + " # good_samples.append((df_val.loc[i, 'video_id'],\n", + " # df_train.loc[argsort[0], 'video_id'],\n", + " # i,\n", + " # argsort[0]))\n", "\n", "\n", " if true_label == Counter(sorted_labels[:5]).most_common()[0][0]:\n", diff --git a/notebooks/visualize_embeddings.ipynb b/notebooks/visualize_embeddings.ipynb index 4268c5a..e32d95e 100644 --- a/notebooks/visualize_embeddings.ipynb +++ b/notebooks/visualize_embeddings.ipynb @@ -91,7 +91,7 @@ { "data": { "text/plain": [ - "" + "" ] }, "execution_count": 7, @@ -129,7 +129,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 18, "id": "ead15a36", "metadata": {}, "outputs": [ @@ -139,7 +139,7 @@ "" ] }, - "execution_count": 9, + "execution_count": 18, "metadata": {}, "output_type": "execute_result" } @@ -151,7 +151,7 @@ "# checkpoint = torch.load(model.get_weights())\n", "\n", "\n", - "CHECKPOINT_PATH = \"../out-checkpoints/augment_rotate_75_x8/checkpoint_embed_18.pth\"\n", + "CHECKPOINT_PATH = \"../checkpoints/checkpoint_embed_411.pth\"\n", "checkpoint = torch.load(CHECKPOINT_PATH, map_location=device)\n", "\n", "\n", @@ -166,26 +166,28 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "id": "20f8036d", "metadata": {}, "outputs": [], "source": [ - "SL_DATASET = 'wlasl' # or 'lsa'\n", - "if SL_DATASET == 'wlasl':\n", - " dataset_name = \"processed\"\n", - " num_classes = 15\n", - " split_dataset_path = \"spoter_test.csv\"\n", - "else:\n", - " dataset_name = \"lsa64_mapped_mediapipe_only_landmarks_25fps\"\n", - " num_classes = 64\n", - " split_dataset_path = \"LSA64_{}.csv\"\n", + "SL_DATASET = 'fingerspelling' # or 'wlasl'\n", + "\n", + "if SL_DATASET == 'fingerspelling':\n", + " dataset_name = \"fingerspelling\"\n", + " split_dataset_path = \"{}.csv\"\n", + "elif SL_DATASET == 'wlasl':\n", + " dataset_name = \"wlasl\"\n", + " split_dataset_path = \"WLASL100_{}.csv\"\n", + "elif SL_DATASET == 'basic-signs':\n", + " dataset_name = \"basic-signs\"\n", + " split_dataset_path = \"{}.csv\"\n", " " ] }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 20, "id": "758716b6", "metadata": {}, "outputs": [], @@ -205,7 +207,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 21, "id": "f1527959", "metadata": {}, "outputs": [ @@ -255,7 +257,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 22, "id": "3c3af5bf", "metadata": { "lines_to_next_cell": 0 @@ -264,10 +266,10 @@ { "data": { "text/plain": [ - "1220" + "743" ] }, - "execution_count": 24, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -286,13 +288,13 @@ " # df['video_fn'] = df['video_id'].apply(lambda video_id: os.path.join(BASE_DATA_FOLDER, f'lsa/videos/{video_id}.mp4'))\n", " dfs[split] = df\n", "\n", - "df = pd.concat([dfs['train'].sample(20), dfs['val']]).reset_index(drop=True)\n", + "df = pd.concat([dfs['train'], dfs['val']]).reset_index(drop=True)\n", "len(df)" ] }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 23, "id": "dccbe1b9", "metadata": {}, "outputs": [], @@ -315,7 +317,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 24, "id": "904298f0", "metadata": {}, "outputs": [], @@ -329,7 +331,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 25, "id": "42832f7c", "metadata": { "scrolled": false @@ -340,7 +342,7 @@ "text/html": [ "
\n", " \n", - " Loading BokehJS ...\n", + " Loading BokehJS ...\n", "
\n" ] }, @@ -349,7 +351,7 @@ }, { "data": { - "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\nconst JS_MIME_TYPE = 'application/javascript';\n const HTML_MIME_TYPE = 'text/html';\n const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n const CLASS_NAME = 'output_bokeh rendered_html';\n\n /**\n * Render data to the DOM node\n */\n function render(props, node) {\n const script = document.createElement(\"script\");\n node.appendChild(script);\n }\n\n /**\n * Handle when an output is cleared or removed\n */\n function handleClearOutput(event, handle) {\n const cell = handle.cell;\n\n const id = cell.output_area._bokeh_element_id;\n const server_id = cell.output_area._bokeh_server_id;\n // Clean up Bokeh references\n if (id != null && id in Bokeh.index) {\n Bokeh.index[id].model.document.clear();\n delete Bokeh.index[id];\n }\n\n if (server_id !== undefined) {\n // Clean up Bokeh references\n const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n cell.notebook.kernel.execute(cmd_clean, {\n iopub: {\n output: function(msg) {\n const id = msg.content.text.trim();\n if (id in Bokeh.index) {\n Bokeh.index[id].model.document.clear();\n delete Bokeh.index[id];\n }\n }\n }\n });\n // Destroy server and session\n const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n cell.notebook.kernel.execute(cmd_destroy);\n }\n }\n\n /**\n * Handle when a new output is added\n */\n function handleAddOutput(event, handle) {\n const output_area = handle.output_area;\n const output = handle.output;\n\n // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n return\n }\n\n const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n\n if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n // store reference to embed id on output_area\n output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n }\n if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n const bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n const script_attrs = bk_div.children[0].attributes;\n for (let i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n }\n\n function register_renderer(events, OutputArea) {\n\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n const toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[toinsert.length - 1]);\n element.append(toinsert);\n return toinsert\n }\n\n /* Handle when an output is cleared or removed */\n events.on('clear_output.CodeCell', handleClearOutput);\n events.on('delete.Cell', handleClearOutput);\n\n /* Handle when a new output is added */\n events.on('output_added.OutputArea', handleAddOutput);\n\n /**\n * Register the mime type and append_mime function with output_area\n */\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n /* Is output safe? */\n safe: true,\n /* Index of renderer in `output_area.display_order` */\n index: 0\n });\n }\n\n // register the mime type if in Jupyter Notebook environment and previously unregistered\n if (root.Jupyter !== undefined) {\n const events = require('base/js/events');\n const OutputArea = require('notebook/js/outputarea').OutputArea;\n\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n }\n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n const NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n const el = document.getElementById(\"1144\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error(url) {\n console.error(\"failed to load \" + url);\n }\n\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.3.min.js\"];\n const css_urls = [];\n\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {\n }\n ];\n\n function run_inline_js() {\n if (root.Bokeh !== undefined || force === true) {\n for (let i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\nif (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n const cell = $(document.getElementById(\"1144\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));", + "application/javascript": "(function(root) {\n function now() {\n return new Date();\n }\n\n const force = true;\n\n if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n root._bokeh_onload_callbacks = [];\n root._bokeh_is_loading = undefined;\n }\n\nconst JS_MIME_TYPE = 'application/javascript';\n const HTML_MIME_TYPE = 'text/html';\n const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n const CLASS_NAME = 'output_bokeh rendered_html';\n\n /**\n * Render data to the DOM node\n */\n function render(props, node) {\n const script = document.createElement(\"script\");\n node.appendChild(script);\n }\n\n /**\n * Handle when an output is cleared or removed\n */\n function handleClearOutput(event, handle) {\n const cell = handle.cell;\n\n const id = cell.output_area._bokeh_element_id;\n const server_id = cell.output_area._bokeh_server_id;\n // Clean up Bokeh references\n if (id != null && id in Bokeh.index) {\n Bokeh.index[id].model.document.clear();\n delete Bokeh.index[id];\n }\n\n if (server_id !== undefined) {\n // Clean up Bokeh references\n const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n cell.notebook.kernel.execute(cmd_clean, {\n iopub: {\n output: function(msg) {\n const id = msg.content.text.trim();\n if (id in Bokeh.index) {\n Bokeh.index[id].model.document.clear();\n delete Bokeh.index[id];\n }\n }\n }\n });\n // Destroy server and session\n const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n cell.notebook.kernel.execute(cmd_destroy);\n }\n }\n\n /**\n * Handle when a new output is added\n */\n function handleAddOutput(event, handle) {\n const output_area = handle.output_area;\n const output = handle.output;\n\n // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n return\n }\n\n const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n\n if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n // store reference to embed id on output_area\n output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n }\n if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n const bk_div = document.createElement(\"div\");\n bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n const script_attrs = bk_div.children[0].attributes;\n for (let i = 0; i < script_attrs.length; i++) {\n toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n }\n // store reference to server id on output_area\n output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n }\n }\n\n function register_renderer(events, OutputArea) {\n\n function append_mime(data, metadata, element) {\n // create a DOM node to render to\n const toinsert = this.create_output_subarea(\n metadata,\n CLASS_NAME,\n EXEC_MIME_TYPE\n );\n this.keyboard_manager.register_events(toinsert);\n // Render to node\n const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n render(props, toinsert[toinsert.length - 1]);\n element.append(toinsert);\n return toinsert\n }\n\n /* Handle when an output is cleared or removed */\n events.on('clear_output.CodeCell', handleClearOutput);\n events.on('delete.Cell', handleClearOutput);\n\n /* Handle when a new output is added */\n events.on('output_added.OutputArea', handleAddOutput);\n\n /**\n * Register the mime type and append_mime function with output_area\n */\n OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n /* Is output safe? */\n safe: true,\n /* Index of renderer in `output_area.display_order` */\n index: 0\n });\n }\n\n // register the mime type if in Jupyter Notebook environment and previously unregistered\n if (root.Jupyter !== undefined) {\n const events = require('base/js/events');\n const OutputArea = require('notebook/js/outputarea').OutputArea;\n\n if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n register_renderer(events, OutputArea);\n }\n }\n if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n root._bokeh_timeout = Date.now() + 5000;\n root._bokeh_failed_load = false;\n }\n\n const NB_LOAD_WARNING = {'data': {'text/html':\n \"
\\n\"+\n \"

\\n\"+\n \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n \"

\\n\"+\n \"
    \\n\"+\n \"
  • re-rerun `output_notebook()` to attempt to load from CDN again, or
  • \\n\"+\n \"
  • use INLINE resources instead, as so:
  • \\n\"+\n \"
\\n\"+\n \"\\n\"+\n \"from bokeh.resources import INLINE\\n\"+\n \"output_notebook(resources=INLINE)\\n\"+\n \"\\n\"+\n \"
\"}};\n\n function display_loaded() {\n const el = document.getElementById(\"1119\");\n if (el != null) {\n el.textContent = \"BokehJS is loading...\";\n }\n if (root.Bokeh !== undefined) {\n if (el != null) {\n el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n }\n } else if (Date.now() < root._bokeh_timeout) {\n setTimeout(display_loaded, 100)\n }\n }\n\n function run_callbacks() {\n try {\n root._bokeh_onload_callbacks.forEach(function(callback) {\n if (callback != null)\n callback();\n });\n } finally {\n delete root._bokeh_onload_callbacks\n }\n console.debug(\"Bokeh: all callbacks have finished\");\n }\n\n function load_libs(css_urls, js_urls, callback) {\n if (css_urls == null) css_urls = [];\n if (js_urls == null) js_urls = [];\n\n root._bokeh_onload_callbacks.push(callback);\n if (root._bokeh_is_loading > 0) {\n console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n return null;\n }\n if (js_urls == null || js_urls.length === 0) {\n run_callbacks();\n return null;\n }\n console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n function on_load() {\n root._bokeh_is_loading--;\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n run_callbacks()\n }\n }\n\n function on_error(url) {\n console.error(\"failed to load \" + url);\n }\n\n for (let i = 0; i < css_urls.length; i++) {\n const url = css_urls[i];\n const element = document.createElement(\"link\");\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.rel = \"stylesheet\";\n element.type = \"text/css\";\n element.href = url;\n console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n document.body.appendChild(element);\n }\n\n for (let i = 0; i < js_urls.length; i++) {\n const url = js_urls[i];\n const element = document.createElement('script');\n element.onload = on_load;\n element.onerror = on_error.bind(null, url);\n element.async = false;\n element.src = url;\n console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n document.head.appendChild(element);\n }\n };\n\n function inject_raw_css(css) {\n const element = document.createElement(\"style\");\n element.appendChild(document.createTextNode(css));\n document.body.appendChild(element);\n }\n\n const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.3.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.3.min.js\"];\n const css_urls = [];\n\n const inline_js = [ function(Bokeh) {\n Bokeh.set_log_level(\"info\");\n },\nfunction(Bokeh) {\n }\n ];\n\n function run_inline_js() {\n if (root.Bokeh !== undefined || force === true) {\n for (let i = 0; i < inline_js.length; i++) {\n inline_js[i].call(root, root.Bokeh);\n }\nif (force === true) {\n display_loaded();\n }} else if (Date.now() < root._bokeh_timeout) {\n setTimeout(run_inline_js, 100);\n } else if (!root._bokeh_failed_load) {\n console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n root._bokeh_failed_load = true;\n } else if (force !== true) {\n const cell = $(document.getElementById(\"1119\")).parents('.cell').data().cell;\n cell.output_area.append_execute_result(NB_LOAD_WARNING)\n }\n }\n\n if (root._bokeh_is_loading === 0) {\n console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n run_inline_js();\n } else {\n load_libs(css_urls, js_urls, function() {\n console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n run_inline_js();\n });\n }\n}(window));", "application/vnd.bokehjs_load.v0+json": "" }, "metadata": {}, @@ -373,7 +375,12 @@ " \n", " \n", "\"\"\"\n", - "cmap = LinearColorMapper(palette=\"Turbo256\", low=0, high=len(set_labels))\n", + "\n", + "# get labels\n", + "labels = df['label_name'].values\n", + "# get unique labels\n", + "unique_labels = np.unique(labels)\n", + "cmap = LinearColorMapper(palette=\"Turbo256\", low=0, high=len(unique_labels))\n", "\n", "output_notebook()\n", "# or \n", @@ -387,32 +394,33 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 26, "id": "ead4daf7", "metadata": { "scrolled": false }, "outputs": [ { - "ename": "KeyError", - "evalue": "'label_name'", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "File \u001b[0;32m/usr/local/lib/python3.8/dist-packages/pandas/core/indexes/base.py:3802\u001b[0m, in \u001b[0;36mIndex.get_loc\u001b[0;34m(self, key, method, tolerance)\u001b[0m\n\u001b[1;32m 3801\u001b[0m \u001b[39mtry\u001b[39;00m:\n\u001b[0;32m-> 3802\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49m_engine\u001b[39m.\u001b[39;49mget_loc(casted_key)\n\u001b[1;32m 3803\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mKeyError\u001b[39;00m \u001b[39mas\u001b[39;00m err:\n", - "File \u001b[0;32m/usr/local/lib/python3.8/dist-packages/pandas/_libs/index.pyx:138\u001b[0m, in \u001b[0;36mpandas._libs.index.IndexEngine.get_loc\u001b[0;34m()\u001b[0m\n", - "File \u001b[0;32m/usr/local/lib/python3.8/dist-packages/pandas/_libs/index.pyx:165\u001b[0m, in \u001b[0;36mpandas._libs.index.IndexEngine.get_loc\u001b[0;34m()\u001b[0m\n", - "File \u001b[0;32mpandas/_libs/hashtable_class_helper.pxi:5745\u001b[0m, in \u001b[0;36mpandas._libs.hashtable.PyObjectHashTable.get_item\u001b[0;34m()\u001b[0m\n", - "File \u001b[0;32mpandas/_libs/hashtable_class_helper.pxi:5753\u001b[0m, in \u001b[0;36mpandas._libs.hashtable.PyObjectHashTable.get_item\u001b[0;34m()\u001b[0m\n", - "\u001b[0;31mKeyError\u001b[0m: 'label_name'", - "\nThe above exception was the direct cause of the following exception:\n", - "\u001b[0;31mKeyError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[29], line 5\u001b[0m\n\u001b[1;32m 1\u001b[0m column_data \u001b[39m=\u001b[39m \u001b[39mdict\u001b[39m(\n\u001b[1;32m 2\u001b[0m x\u001b[39m=\u001b[39mdf[\u001b[39m'\u001b[39m\u001b[39mtsne_x\u001b[39m\u001b[39m'\u001b[39m],\n\u001b[1;32m 3\u001b[0m y\u001b[39m=\u001b[39mdf[\u001b[39m'\u001b[39m\u001b[39mtsne_y\u001b[39m\u001b[39m'\u001b[39m],\n\u001b[1;32m 4\u001b[0m label\u001b[39m=\u001b[39mdf[\u001b[39m'\u001b[39m\u001b[39msign\u001b[39m\u001b[39m'\u001b[39m],\n\u001b[0;32m----> 5\u001b[0m label_desc\u001b[39m=\u001b[39mdf[\u001b[39m'\u001b[39;49m\u001b[39mlabel_name\u001b[39;49m\u001b[39m'\u001b[39;49m],\n\u001b[1;32m 6\u001b[0m split\u001b[39m=\u001b[39mdf[\u001b[39m'\u001b[39m\u001b[39msplit\u001b[39m\u001b[39m'\u001b[39m],\n\u001b[1;32m 7\u001b[0m video_id\u001b[39m=\u001b[39mdf[\u001b[39m'\u001b[39m\u001b[39mvideo_id\u001b[39m\u001b[39m'\u001b[39m]\n\u001b[1;32m 8\u001b[0m )\n\u001b[1;32m 10\u001b[0m \u001b[39mif\u001b[39;00m use_img_div:\n\u001b[1;32m 11\u001b[0m emb_videos \u001b[39m=\u001b[39m load_videos(df[\u001b[39m'\u001b[39m\u001b[39mvideo_fn\u001b[39m\u001b[39m'\u001b[39m])\n", - "File \u001b[0;32m/usr/local/lib/python3.8/dist-packages/pandas/core/frame.py:3807\u001b[0m, in \u001b[0;36mDataFrame.__getitem__\u001b[0;34m(self, key)\u001b[0m\n\u001b[1;32m 3805\u001b[0m \u001b[39mif\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39mcolumns\u001b[39m.\u001b[39mnlevels \u001b[39m>\u001b[39m \u001b[39m1\u001b[39m:\n\u001b[1;32m 3806\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_getitem_multilevel(key)\n\u001b[0;32m-> 3807\u001b[0m indexer \u001b[39m=\u001b[39m \u001b[39mself\u001b[39;49m\u001b[39m.\u001b[39;49mcolumns\u001b[39m.\u001b[39;49mget_loc(key)\n\u001b[1;32m 3808\u001b[0m \u001b[39mif\u001b[39;00m is_integer(indexer):\n\u001b[1;32m 3809\u001b[0m indexer \u001b[39m=\u001b[39m [indexer]\n", - "File \u001b[0;32m/usr/local/lib/python3.8/dist-packages/pandas/core/indexes/base.py:3804\u001b[0m, in \u001b[0;36mIndex.get_loc\u001b[0;34m(self, key, method, tolerance)\u001b[0m\n\u001b[1;32m 3802\u001b[0m \u001b[39mreturn\u001b[39;00m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_engine\u001b[39m.\u001b[39mget_loc(casted_key)\n\u001b[1;32m 3803\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mKeyError\u001b[39;00m \u001b[39mas\u001b[39;00m err:\n\u001b[0;32m-> 3804\u001b[0m \u001b[39mraise\u001b[39;00m \u001b[39mKeyError\u001b[39;00m(key) \u001b[39mfrom\u001b[39;00m \u001b[39merr\u001b[39;00m\n\u001b[1;32m 3805\u001b[0m \u001b[39mexcept\u001b[39;00m \u001b[39mTypeError\u001b[39;00m:\n\u001b[1;32m 3806\u001b[0m \u001b[39m# If we have a listlike key, _check_indexing_error will raise\u001b[39;00m\n\u001b[1;32m 3807\u001b[0m \u001b[39m# InvalidIndexError. Otherwise we fall through and re-raise\u001b[39;00m\n\u001b[1;32m 3808\u001b[0m \u001b[39m# the TypeError.\u001b[39;00m\n\u001b[1;32m 3809\u001b[0m \u001b[39mself\u001b[39m\u001b[39m.\u001b[39m_check_indexing_error(key)\n", - "\u001b[0;31mKeyError\u001b[0m: 'label_name'" - ] + "data": { + "text/html": [ + "\n", + "
\n" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/javascript": "(function(root) {\n function embed_document(root) {\n const docs_json = {\"27aecfdc-14bf-4fd5-928c-7f15b2ea73c6\":{\"defs\":[],\"roots\":{\"references\":[{\"attributes\":{\"below\":[{\"id\":\"1131\"}],\"center\":[{\"id\":\"1134\"},{\"id\":\"1138\"},{\"id\":\"1172\"}],\"height\":800,\"left\":[{\"id\":\"1135\"}],\"renderers\":[{\"id\":\"1160\"}],\"title\":{\"id\":\"1121\"},\"toolbar\":{\"id\":\"1147\"},\"width\":1000,\"x_range\":{\"id\":\"1123\"},\"x_scale\":{\"id\":\"1127\"},\"y_range\":{\"id\":\"1125\"},\"y_scale\":{\"id\":\"1129\"}},\"id\":\"1120\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1164\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.1},\"fill_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"hatch_alpha\":{\"value\":0.1},\"hatch_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"line_alpha\":{\"value\":0.1},\"line_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"size\":{\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1158\",\"type\":\"Scatter\"},{\"attributes\":{},\"id\":\"1136\",\"type\":\"BasicTicker\"},{\"attributes\":{\"coordinates\":null,\"formatter\":{\"id\":\"1167\"},\"group\":null,\"major_label_policy\":{\"id\":\"1168\"},\"ticker\":{\"id\":\"1132\"}},\"id\":\"1131\",\"type\":\"LinearAxis\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.5},\"fill_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"hatch_alpha\":{\"value\":0.5},\"hatch_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"line_alpha\":{\"value\":0.5},\"line_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"size\":{\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1157\",\"type\":\"Scatter\"},{\"attributes\":{\"source\":{\"id\":\"1155\"}},\"id\":\"1161\",\"type\":\"CDSView\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1155\"},\"glyph\":{\"id\":\"1157\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1159\"},\"nonselection_glyph\":{\"id\":\"1158\"},\"view\":{\"id\":\"1161\"}},\"id\":\"1160\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"items\":[{\"id\":\"1173\"}]},\"id\":\"1172\",\"type\":\"Legend\"},{\"attributes\":{},\"id\":\"1132\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1167\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1145\",\"type\":\"BoxAnnotation\"},{\"attributes\":{\"fill_alpha\":{\"value\":0.2},\"fill_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"hatch_alpha\":{\"value\":0.2},\"hatch_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"line_alpha\":{\"value\":0.2},\"line_color\":{\"field\":\"labels\",\"transform\":{\"id\":\"1118\"}},\"size\":{\"value\":10},\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1159\",\"type\":\"Scatter\"},{\"attributes\":{\"label\":{\"field\":\"label_desc\"},\"renderers\":[{\"id\":\"1160\"}]},\"id\":\"1173\",\"type\":\"LegendItem\"},{\"attributes\":{},\"id\":\"1170\",\"type\":\"Selection\"},{\"attributes\":{},\"id\":\"1125\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1127\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1129\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1169\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1144\",\"type\":\"HelpTool\"},{\"attributes\":{\"axis\":{\"id\":\"1135\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1138\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1168\",\"type\":\"AllLabels\"},{\"attributes\":{},\"id\":\"1165\",\"type\":\"AllLabels\"},{\"attributes\":{\"axis\":{\"id\":\"1131\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1134\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1139\",\"type\":\"PanTool\"},{\"attributes\":{},\"id\":\"1123\",\"type\":\"DataRange1d\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"text\":\"Check label by hovering mouse over the dots\"},\"id\":\"1121\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1140\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"tools\":[{\"id\":\"1139\"},{\"id\":\"1140\"},{\"id\":\"1141\"},{\"id\":\"1142\"},{\"id\":\"1143\"},{\"id\":\"1144\"},{\"id\":\"1146\"}]},\"id\":\"1147\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1142\",\"type\":\"SaveTool\"},{\"attributes\":{\"callback\":null,\"tooltips\":\"\\n
\\n \\n
\\n @label_desc - @split\\n [#@video_id]\\n
\\n
\\n \\n\"},\"id\":\"1146\",\"type\":\"HoverTool\"},{\"attributes\":{\"overlay\":{\"id\":\"1145\"}},\"id\":\"1141\",\"type\":\"BoxZoomTool\"},{\"attributes\":{\"high\":26,\"low\":0,\"palette\":[\"#30123b\",\"#311542\",\"#32184a\",\"#341b51\",\"#351e58\",\"#36215f\",\"#372365\",\"#38266c\",\"#392972\",\"#3a2c79\",\"#3b2f7f\",\"#3c3285\",\"#3c358b\",\"#3d3791\",\"#3e3a96\",\"#3f3d9c\",\"#4040a1\",\"#4043a6\",\"#4145ab\",\"#4148b0\",\"#424bb5\",\"#434eba\",\"#4350be\",\"#4353c2\",\"#4456c7\",\"#4458cb\",\"#455bce\",\"#455ed2\",\"#4560d6\",\"#4563d9\",\"#4666dd\",\"#4668e0\",\"#466be3\",\"#466de6\",\"#4670e8\",\"#4673eb\",\"#4675ed\",\"#4678f0\",\"#467af2\",\"#467df4\",\"#467ff6\",\"#4682f8\",\"#4584f9\",\"#4587fb\",\"#4589fc\",\"#448cfd\",\"#438efd\",\"#4291fe\",\"#4193fe\",\"#4096fe\",\"#3f98fe\",\"#3e9bfe\",\"#3c9dfd\",\"#3ba0fc\",\"#39a2fc\",\"#38a5fb\",\"#36a8f9\",\"#34aaf8\",\"#33acf6\",\"#31aff5\",\"#2fb1f3\",\"#2db4f1\",\"#2bb6ef\",\"#2ab9ed\",\"#28bbeb\",\"#26bde9\",\"#25c0e6\",\"#23c2e4\",\"#21c4e1\",\"#20c6df\",\"#1ec9dc\",\"#1dcbda\",\"#1ccdd7\",\"#1bcfd4\",\"#1ad1d2\",\"#19d3cf\",\"#18d5cc\",\"#18d7ca\",\"#17d9c7\",\"#17dac4\",\"#17dcc2\",\"#17debf\",\"#18e0bd\",\"#18e1ba\",\"#19e3b8\",\"#1ae4b6\",\"#1be5b4\",\"#1de7b1\",\"#1ee8af\",\"#20e9ac\",\"#22eba9\",\"#24eca6\",\"#27eda3\",\"#29eea0\",\"#2cef9d\",\"#2ff09a\",\"#32f197\",\"#35f394\",\"#38f491\",\"#3bf48d\",\"#3ff58a\",\"#42f687\",\"#46f783\",\"#4af880\",\"#4df97c\",\"#51f979\",\"#55fa76\",\"#59fb72\",\"#5dfb6f\",\"#61fc6c\",\"#65fc68\",\"#69fd65\",\"#6dfd62\",\"#71fd5f\",\"#74fe5c\",\"#78fe59\",\"#7cfe56\",\"#80fe53\",\"#84fe50\",\"#87fe4d\",\"#8bfe4b\",\"#8efe48\",\"#92fe46\",\"#95fe44\",\"#98fe42\",\"#9bfd40\",\"#9efd3e\",\"#a1fc3d\",\"#a4fc3b\",\"#a6fb3a\",\"#a9fb39\",\"#acfa37\",\"#aef937\",\"#b1f836\",\"#b3f835\",\"#b6f735\",\"#b9f534\",\"#bbf434\",\"#bef334\",\"#c0f233\",\"#c3f133\",\"#c5ef33\",\"#c8ee33\",\"#caed33\",\"#cdeb34\",\"#cfea34\",\"#d1e834\",\"#d4e735\",\"#d6e535\",\"#d8e335\",\"#dae236\",\"#dde036\",\"#dfde36\",\"#e1dc37\",\"#e3da37\",\"#e5d838\",\"#e7d738\",\"#e8d538\",\"#ead339\",\"#ecd139\",\"#edcf39\",\"#efcd39\",\"#f0cb3a\",\"#f2c83a\",\"#f3c63a\",\"#f4c43a\",\"#f6c23a\",\"#f7c039\",\"#f8be39\",\"#f9bc39\",\"#f9ba38\",\"#fab737\",\"#fbb537\",\"#fbb336\",\"#fcb035\",\"#fcae34\",\"#fdab33\",\"#fda932\",\"#fda631\",\"#fda330\",\"#fea12f\",\"#fe9e2e\",\"#fe9b2d\",\"#fe982c\",\"#fd952b\",\"#fd9229\",\"#fd8f28\",\"#fd8c27\",\"#fc8926\",\"#fc8624\",\"#fb8323\",\"#fb8022\",\"#fa7d20\",\"#fa7a1f\",\"#f9771e\",\"#f8741c\",\"#f7711b\",\"#f76e1a\",\"#f66b18\",\"#f56817\",\"#f46516\",\"#f36315\",\"#f26014\",\"#f15d13\",\"#ef5a11\",\"#ee5810\",\"#ed550f\",\"#ec520e\",\"#ea500d\",\"#e94d0d\",\"#e84b0c\",\"#e6490b\",\"#e5460a\",\"#e3440a\",\"#e24209\",\"#e04008\",\"#de3e08\",\"#dd3c07\",\"#db3a07\",\"#d93806\",\"#d73606\",\"#d63405\",\"#d43205\",\"#d23005\",\"#d02f04\",\"#ce2d04\",\"#cb2b03\",\"#c92903\",\"#c72803\",\"#c52602\",\"#c32402\",\"#c02302\",\"#be2102\",\"#bb1f01\",\"#b91e01\",\"#b61c01\",\"#b41b01\",\"#b11901\",\"#ae1801\",\"#ac1601\",\"#a91501\",\"#a61401\",\"#a31201\",\"#a01101\",\"#9d1001\",\"#9a0e01\",\"#970d01\",\"#940c01\",\"#910b01\",\"#8e0a01\",\"#8b0901\",\"#870801\",\"#840701\",\"#810602\",\"#7d0502\",\"#7a0402\"]},\"id\":\"1118\",\"type\":\"LinearColorMapper\"},{\"attributes\":{\"data\":{\"label\":[8,9,6,23,5,7,3,4,20,24,17,16,21,3,4,18,12,16,16,7,19,22,1,24,14,13,15,13,16,12,16,22,24,25,5,0,1,0,8,2,6,19,17,0,4,21,12,23,6,25,23,4,25,13,3,3,10,3,20,20,11,20,12,2,17,5,18,10,18,1,8,14,11,2,3,0,14,5,2,7,0,14,3,16,0,17,24,15,13,25,6,6,6,2,16,15,9,18,6,12,7,22,2,12,25,4,2,3,23,7,4,10,0,11,16,25,20,9,19,1,19,11,0,22,25,0,19,13,18,20,16,16,1,4,12,23,4,10,12,8,19,7,12,17,22,12,18,18,9,7,9,8,11,6,12,0,21,17,13,4,20,16,11,11,15,12,18,7,5,8,8,15,14,6,5,8,14,8,3,10,16,19,22,17,10,19,8,9,7,14,19,6,19,1,6,14,16,16,15,8,12,5,22,13,18,9,14,22,9,22,20,9,19,18,6,6,25,24,3,21,5,7,1,10,4,15,24,22,21,10,14,3,5,13,16,16,5,24,18,7,9,17,21,21,16,13,24,0,23,19,11,2,17,2,21,19,1,3,13,14,20,22,13,12,14,3,5,7,15,7,24,11,25,7,6,24,5,1,5,3,18,17,5,17,0,10,15,12,13,18,7,19,9,8,2,3,23,5,14,22,9,14,10,1,14,1,1,10,20,5,13,6,19,0,22,2,8,18,6,12,22,14,21,14,20,17,0,4,1,24,22,11,21,18,22,10,16,25,4,18,2,2,24,9,0,24,0,13,23,3,10,23,1,8,23,25,22,0,10,21,12,17,10,19,0,0,13,8,16,23,16,9,18,0,0,17,17,5,22,22,7,11,15,4,19,11,13,0,15,19,1,14,4,15,2,25,9,4,1,19,25,2,13,9,23,20,1,15,0,10,20,23,14,24,0,14,7,3,25,12,18,20,5,2,6,3,6,19,15,8,12,20,21,16,20,0,17,25,23,15,7,6,21,4,16,10,1,10,5,9,25,2,11,23,2,15,12,14,22,7,5,17,12,5,3,23,4,11,1,21,7,11,13,20,23,15,15,12,15,3,23,13,10,11,8,18,4,19,3,2,17,15,10,9,18,13,11,4,18,13,25,9,21,17,10,2,23,12,13,17,11,4,19,11,6,2,3,15,1,22,20,20,2,0,20,3,3,6,3,20,0,19,21,23,18,17,23,4,4,20,9,2,12,7,10,10,6,2,16,4,0,11,10,0,20,11,17,1,7,16,25,13,24,10,16,15,5,16,23,17,12,12,22,8,24,23,1,8,1,6,2,23,3,20,22,25,23,7,11,12,3,0,10,16,17,0,6,11,5,10,18,17,17,14,23,16,22,5,6,24,2,21,20,9,3,7,19,21,8,18,20,22,10,4,18,13,9,25,22,2,19,15,20,11,25,8,25,20,8,23,7,16,13,5,0,9,18,1,19,12,0,3,7,3,14,18,15,10,13,20,17,1,3,4,4,13,5,21,2,9,9,19,11,12,14,20,7,3,0,14,14,2,15,9,13,2,4,0,1,15,15,17,7,23,10,2,14,11,13,6,18,4,0,2,18,15,17,1,19,5,17,0,10,3,4,24,16,14,19,11,19,21,22,9,0,6,18,22,19,5,16,3,10,11,12,4,21,0,12,12,6,8,6],\"label_desc\":[\"W\",\"Y\",\"I\",\"V\",\"G\",\"J\",\"O\",\"S\",\"Z\",\"P\",\"F\",\"M\",\"X\",\"O\",\"S\",\"Q\",\"E\",\"M\",\"M\",\"J\",\"L\",\"U\",\"D\",\"P\",\"K\",\"T\",\"R\",\"T\",\"M\",\"E\",\"M\",\"U\",\"P\",\"A\",\"G\",\"B\",\"D\",\"B\",\"W\",\"N\",\"I\",\"L\",\"F\",\"B\",\"S\",\"X\",\"E\",\"V\",\"I\",\"A\",\"V\",\"S\",\"A\",\"T\",\"O\",\"O\",\"C\",\"O\",\"Z\",\"Z\",\"H\",\"Z\",\"E\",\"N\",\"F\",\"G\",\"Q\",\"C\",\"Q\",\"D\",\"W\",\"K\",\"H\",\"N\",\"O\",\"B\",\"K\",\"G\",\"N\",\"J\",\"B\",\"K\",\"O\",\"M\",\"B\",\"F\",\"P\",\"R\",\"T\",\"A\",\"I\",\"I\",\"I\",\"N\",\"M\",\"R\",\"Y\",\"Q\",\"I\",\"E\",\"J\",\"U\",\"N\",\"E\",\"A\",\"S\",\"N\",\"O\",\"V\",\"J\",\"S\",\"C\",\"B\",\"H\",\"M\",\"A\",\"Z\",\"Y\",\"L\",\"D\",\"L\",\"H\",\"B\",\"U\",\"A\",\"B\",\"L\",\"T\",\"Q\",\"Z\",\"M\",\"M\",\"D\",\"S\",\"E\",\"V\",\"S\",\"C\",\"E\",\"W\",\"L\",\"J\",\"E\",\"F\",\"U\",\"E\",\"Q\",\"Q\",\"Y\",\"J\",\"Y\",\"W\",\"H\",\"I\",\"E\",\"B\",\"X\",\"F\",\"T\",\"S\",\"Z\",\"M\",\"H\",\"H\",\"R\",\"E\",\"Q\",\"J\",\"G\",\"W\",\"W\",\"R\",\"K\",\"I\",\"G\",\"W\",\"K\",\"W\",\"O\",\"C\",\"M\",\"L\",\"U\",\"F\",\"C\",\"L\",\"W\",\"Y\",\"J\",\"K\",\"L\",\"I\",\"L\",\"D\",\"I\",\"K\",\"M\",\"M\",\"R\",\"W\",\"E\",\"G\",\"U\",\"T\",\"Q\",\"Y\",\"K\",\"U\",\"Y\",\"U\",\"Z\",\"Y\",\"L\",\"Q\",\"I\",\"I\",\"A\",\"P\",\"O\",\"X\",\"G\",\"J\",\"D\",\"C\",\"S\",\"R\",\"P\",\"U\",\"X\",\"C\",\"K\",\"O\",\"G\",\"T\",\"M\",\"M\",\"G\",\"P\",\"Q\",\"J\",\"Y\",\"F\",\"X\",\"X\",\"M\",\"T\",\"P\",\"B\",\"V\",\"L\",\"H\",\"N\",\"F\",\"N\",\"X\",\"L\",\"D\",\"O\",\"T\",\"K\",\"Z\",\"U\",\"T\",\"E\",\"K\",\"O\",\"G\",\"J\",\"R\",\"J\",\"P\",\"H\",\"A\",\"J\",\"I\",\"P\",\"G\",\"D\",\"G\",\"O\",\"Q\",\"F\",\"G\",\"F\",\"B\",\"C\",\"R\",\"E\",\"T\",\"Q\",\"J\",\"L\",\"Y\",\"W\",\"N\",\"O\",\"V\",\"G\",\"K\",\"U\",\"Y\",\"K\",\"C\",\"D\",\"K\",\"D\",\"D\",\"C\",\"Z\",\"G\",\"T\",\"I\",\"L\",\"B\",\"U\",\"N\",\"W\",\"Q\",\"I\",\"E\",\"U\",\"K\",\"X\",\"K\",\"Z\",\"F\",\"B\",\"S\",\"D\",\"P\",\"U\",\"H\",\"X\",\"Q\",\"U\",\"C\",\"M\",\"A\",\"S\",\"Q\",\"N\",\"N\",\"P\",\"Y\",\"B\",\"P\",\"B\",\"T\",\"V\",\"O\",\"C\",\"V\",\"D\",\"W\",\"V\",\"A\",\"U\",\"B\",\"C\",\"X\",\"E\",\"F\",\"C\",\"L\",\"B\",\"B\",\"T\",\"W\",\"M\",\"V\",\"M\",\"Y\",\"Q\",\"B\",\"B\",\"F\",\"F\",\"G\",\"U\",\"U\",\"J\",\"H\",\"R\",\"S\",\"L\",\"H\",\"T\",\"B\",\"R\",\"L\",\"D\",\"K\",\"S\",\"R\",\"N\",\"A\",\"Y\",\"S\",\"D\",\"L\",\"A\",\"N\",\"T\",\"Y\",\"V\",\"Z\",\"D\",\"R\",\"B\",\"C\",\"Z\",\"V\",\"K\",\"P\",\"B\",\"K\",\"J\",\"O\",\"A\",\"E\",\"Q\",\"Z\",\"G\",\"N\",\"I\",\"O\",\"I\",\"L\",\"R\",\"W\",\"E\",\"Z\",\"X\",\"M\",\"Z\",\"B\",\"F\",\"A\",\"V\",\"R\",\"J\",\"I\",\"X\",\"S\",\"M\",\"C\",\"D\",\"C\",\"G\",\"Y\",\"A\",\"N\",\"H\",\"V\",\"N\",\"R\",\"E\",\"K\",\"U\",\"J\",\"G\",\"F\",\"E\",\"G\",\"O\",\"V\",\"S\",\"H\",\"D\",\"X\",\"J\",\"H\",\"T\",\"Z\",\"V\",\"R\",\"R\",\"E\",\"R\",\"O\",\"V\",\"T\",\"C\",\"H\",\"W\",\"Q\",\"S\",\"L\",\"O\",\"N\",\"F\",\"R\",\"C\",\"Y\",\"Q\",\"T\",\"H\",\"S\",\"Q\",\"T\",\"A\",\"Y\",\"X\",\"F\",\"C\",\"N\",\"V\",\"E\",\"T\",\"F\",\"H\",\"S\",\"L\",\"H\",\"I\",\"N\",\"O\",\"R\",\"D\",\"U\",\"Z\",\"Z\",\"N\",\"B\",\"Z\",\"O\",\"O\",\"I\",\"O\",\"Z\",\"B\",\"L\",\"X\",\"V\",\"Q\",\"F\",\"V\",\"S\",\"S\",\"Z\",\"Y\",\"N\",\"E\",\"J\",\"C\",\"C\",\"I\",\"N\",\"M\",\"S\",\"B\",\"H\",\"C\",\"B\",\"Z\",\"H\",\"F\",\"D\",\"J\",\"M\",\"A\",\"T\",\"P\",\"C\",\"M\",\"R\",\"G\",\"M\",\"V\",\"F\",\"E\",\"E\",\"U\",\"W\",\"P\",\"V\",\"D\",\"W\",\"D\",\"I\",\"N\",\"V\",\"O\",\"Z\",\"U\",\"A\",\"V\",\"J\",\"H\",\"E\",\"O\",\"B\",\"C\",\"M\",\"F\",\"B\",\"I\",\"H\",\"G\",\"C\",\"Q\",\"F\",\"F\",\"K\",\"V\",\"M\",\"U\",\"G\",\"I\",\"P\",\"N\",\"X\",\"Z\",\"Y\",\"O\",\"J\",\"L\",\"X\",\"W\",\"Q\",\"Z\",\"U\",\"C\",\"S\",\"Q\",\"T\",\"Y\",\"A\",\"U\",\"N\",\"L\",\"R\",\"Z\",\"H\",\"A\",\"W\",\"A\",\"Z\",\"W\",\"V\",\"J\",\"M\",\"T\",\"G\",\"B\",\"Y\",\"Q\",\"D\",\"L\",\"E\",\"B\",\"O\",\"J\",\"O\",\"K\",\"Q\",\"R\",\"C\",\"T\",\"Z\",\"F\",\"D\",\"O\",\"S\",\"S\",\"T\",\"G\",\"X\",\"N\",\"Y\",\"Y\",\"L\",\"H\",\"E\",\"K\",\"Z\",\"J\",\"O\",\"B\",\"K\",\"K\",\"N\",\"R\",\"Y\",\"T\",\"N\",\"S\",\"B\",\"D\",\"R\",\"R\",\"F\",\"J\",\"V\",\"C\",\"N\",\"K\",\"H\",\"T\",\"I\",\"Q\",\"S\",\"B\",\"N\",\"Q\",\"R\",\"F\",\"D\",\"L\",\"G\",\"F\",\"B\",\"C\",\"O\",\"S\",\"P\",\"M\",\"K\",\"L\",\"H\",\"L\",\"X\",\"U\",\"Y\",\"B\",\"I\",\"Q\",\"U\",\"L\",\"G\",\"M\",\"O\",\"C\",\"H\",\"E\",\"S\",\"X\",\"B\",\"E\",\"E\",\"I\",\"W\",\"I\"],\"labels\":[8,9,6,23,5,7,3,4,20,24,17,16,21,3,4,18,12,16,16,7,19,22,1,24,14,13,15,13,16,12,16,22,24,25,5,0,1,0,8,2,6,19,17,0,4,21,12,23,6,25,23,4,25,13,3,3,10,3,20,20,11,20,12,2,17,5,18,10,18,1,8,14,11,2,3,0,14,5,2,7,0,14,3,16,0,17,24,15,13,25,6,6,6,2,16,15,9,18,6,12,7,22,2,12,25,4,2,3,23,7,4,10,0,11,16,25,20,9,19,1,19,11,0,22,25,0,19,13,18,20,16,16,1,4,12,23,4,10,12,8,19,7,12,17,22,12,18,18,9,7,9,8,11,6,12,0,21,17,13,4,20,16,11,11,15,12,18,7,5,8,8,15,14,6,5,8,14,8,3,10,16,19,22,17,10,19,8,9,7,14,19,6,19,1,6,14,16,16,15,8,12,5,22,13,18,9,14,22,9,22,20,9,19,18,6,6,25,24,3,21,5,7,1,10,4,15,24,22,21,10,14,3,5,13,16,16,5,24,18,7,9,17,21,21,16,13,24,0,23,19,11,2,17,2,21,19,1,3,13,14,20,22,13,12,14,3,5,7,15,7,24,11,25,7,6,24,5,1,5,3,18,17,5,17,0,10,15,12,13,18,7,19,9,8,2,3,23,5,14,22,9,14,10,1,14,1,1,10,20,5,13,6,19,0,22,2,8,18,6,12,22,14,21,14,20,17,0,4,1,24,22,11,21,18,22,10,16,25,4,18,2,2,24,9,0,24,0,13,23,3,10,23,1,8,23,25,22,0,10,21,12,17,10,19,0,0,13,8,16,23,16,9,18,0,0,17,17,5,22,22,7,11,15,4,19,11,13,0,15,19,1,14,4,15,2,25,9,4,1,19,25,2,13,9,23,20,1,15,0,10,20,23,14,24,0,14,7,3,25,12,18,20,5,2,6,3,6,19,15,8,12,20,21,16,20,0,17,25,23,15,7,6,21,4,16,10,1,10,5,9,25,2,11,23,2,15,12,14,22,7,5,17,12,5,3,23,4,11,1,21,7,11,13,20,23,15,15,12,15,3,23,13,10,11,8,18,4,19,3,2,17,15,10,9,18,13,11,4,18,13,25,9,21,17,10,2,23,12,13,17,11,4,19,11,6,2,3,15,1,22,20,20,2,0,20,3,3,6,3,20,0,19,21,23,18,17,23,4,4,20,9,2,12,7,10,10,6,2,16,4,0,11,10,0,20,11,17,1,7,16,25,13,24,10,16,15,5,16,23,17,12,12,22,8,24,23,1,8,1,6,2,23,3,20,22,25,23,7,11,12,3,0,10,16,17,0,6,11,5,10,18,17,17,14,23,16,22,5,6,24,2,21,20,9,3,7,19,21,8,18,20,22,10,4,18,13,9,25,22,2,19,15,20,11,25,8,25,20,8,23,7,16,13,5,0,9,18,1,19,12,0,3,7,3,14,18,15,10,13,20,17,1,3,4,4,13,5,21,2,9,9,19,11,12,14,20,7,3,0,14,14,2,15,9,13,2,4,0,1,15,15,17,7,23,10,2,14,11,13,6,18,4,0,2,18,15,17,1,19,5,17,0,10,3,4,24,16,14,19,11,19,21,22,9,0,6,18,22,19,5,16,3,10,11,12,4,21,0,12,12,6,8,6],\"split\":[\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"train\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\",\"val\"],\"x\":{\"__ndarray__\":\"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\",\"dtype\":\"float32\",\"order\":\"little\",\"shape\":[743]},\"y\":{\"__ndarray__\":\"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\",\"dtype\":\"float32\",\"order\":\"little\",\"shape\":[743]}},\"selected\":{\"id\":\"1170\"},\"selection_policy\":{\"id\":\"1169\"}},\"id\":\"1155\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1143\",\"type\":\"ResetTool\"},{\"attributes\":{\"coordinates\":null,\"formatter\":{\"id\":\"1164\"},\"group\":null,\"major_label_policy\":{\"id\":\"1165\"},\"ticker\":{\"id\":\"1136\"}},\"id\":\"1135\",\"type\":\"LinearAxis\"}],\"root_ids\":[\"1120\"]},\"title\":\"Bokeh Application\",\"version\":\"2.4.3\"}};\n const render_items = [{\"docid\":\"27aecfdc-14bf-4fd5-928c-7f15b2ea73c6\",\"root_ids\":[\"1120\"],\"roots\":{\"1120\":\"8aa5a5a3-57de-4695-bcff-8e64eb93f200\"}}];\n root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n }\n if (root.Bokeh !== undefined) {\n embed_document(root);\n } else {\n let attempts = 0;\n const timer = setInterval(function(root) {\n if (root.Bokeh !== undefined) {\n clearInterval(timer);\n embed_document(root);\n } else {\n attempts++;\n if (attempts > 100) {\n clearInterval(timer);\n console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n }\n }\n }, 10, root)\n }\n})(window);", + "application/vnd.bokehjs_exec.v0+json": "" + }, + "metadata": { + "application/vnd.bokehjs_exec.v0+json": { + "id": "1120" + } + }, + "output_type": "display_data" } ], "source": [ @@ -420,38 +428,36 @@ " x=df['tsne_x'],\n", " y=df['tsne_y'],\n", " label=df['labels'],\n", - " label_desc=df['sign'],\n", + " label_desc=df['label_name'],\n", " split=df['split'],\n", - " video_id=df['video_id']\n", + " # video_id=df['video_id']\n", ")\n", "\n", + "# get unique labels\n", + "set_labels = list(set(column_data['label']))\n", + "\n", + "# map labels to 0 to num_classes\n", + "label_to_id = {label: i for i, label in enumerate(set_labels)}\n", + "column_data['labels'] = [label_to_id[label] for label in column_data['label']]\n", + "\n", + "\n", "if use_img_div:\n", " emb_videos = load_videos(df['video_fn'])\n", " column_data[\"videos\"] = emb_videos\n", "source = ColumnDataSource(data=column_data)\n", "\n", - "p.scatter('x', 'y',\n", - " size=10,\n", - " source=source,\n", - " fill_color={\"field\": 'label', \"transform\": cmap},\n", - " line_color={\"field\": 'label', \"transform\": cmap}, \n", - " #legend_label={\"field\": 'split', \"transform\": lambda x: df['split']},\n", - "# marker={\"field\": 'split'}\n", - " )\n", + "# scatter plot with for each label another color\n", + "p.scatter(x='x',\n", + " y='y',\n", + " source=source,\n", + " color={'field': 'labels', 'transform': cmap},\n", + " legend_field='label_desc',\n", + " size=10,\n", + " alpha=0.5)\n", "\n", "show(p)" ] }, - { - "cell_type": "code", - "execution_count": null, - "id": "1d761766", - "metadata": {}, - "outputs": [], - "source": [ - "df" - ] - }, { "cell_type": "code", "execution_count": null, diff --git a/preprocessing.py b/preprocessing.py index 34ac917..ac4b7f5 100644 --- a/preprocessing.py +++ b/preprocessing.py @@ -1,5 +1,5 @@ from argparse import ArgumentParser -from preprocessing.create_wlasl_landmarks_dataset import parse_create_args, create +from preprocessing.create_fingerspelling_dataset import parse_create_args, create from preprocessing.extract_mediapipe_landmarks import parse_extract_args, extract diff --git a/preprocessing/create_fingerspelling_dataset.py b/preprocessing/create_fingerspelling_dataset.py new file mode 100644 index 0000000..c3793cb --- /dev/null +++ b/preprocessing/create_fingerspelling_dataset.py @@ -0,0 +1,174 @@ +import os +import os.path as op +import json +import shutil + +import cv2 +import mediapipe as mp +import numpy as np +import pandas as pd +from utils import get_logger +from tqdm.auto import tqdm +from sklearn.model_selection import train_test_split +from normalization.blazepose_mapping import map_blazepose_df + +BASE_DATA_FOLDER = 'data/' + +mp_drawing = mp.solutions.drawing_utils +mp_drawing_styles = mp.solutions.drawing_styles +mp_hands = mp.solutions.hands +mp_holistic = mp.solutions.holistic +pose_landmarks = mp_holistic.PoseLandmark +hand_landmarks = mp_holistic.HandLandmark + + +def get_landmarks_names(): + ''' + Returns landmark names for mediapipe holistic model + ''' + pose_lmks = ','.join([f'{lmk.name.lower()}_x,{lmk.name.lower()}_y' for lmk in pose_landmarks]) + left_hand_lmks = ','.join([f'left_hand_{lmk.name.lower()}_x,left_hand_{lmk.name.lower()}_y' + for lmk in hand_landmarks]) + right_hand_lmks = ','.join([f'right_hand_{lmk.name.lower()}_x,right_hand_{lmk.name.lower()}_y' + for lmk in hand_landmarks]) + lmks_names = f'{pose_lmks},{left_hand_lmks},{right_hand_lmks}' + return lmks_names + + +def convert_to_str(arr, precision=6): + if isinstance(arr, np.ndarray): + values = [] + for val in arr: + if val == 0: + values.append('0') + else: + values.append(f'{val:.{precision}f}') + return f"[{','.join(values)}]" + else: + return str(arr) + + +def parse_create_args(parser): + parser.add_argument('--landmarks-dataset', '-lmks', required=True, + help='Path to folder with landmarks npy files. \ + You need to run `extract_mediapipe_landmarks.py` script first') + parser.add_argument('--dataset-folder', '-df', default='data/wlasl', + help='Path to folder where original `WLASL_v0.3.json` and `id_to_label.json` are stored. \ + Note that final CSV files will be saved in this folder too.') + parser.add_argument('--videos-folder', '-videos', default=None, + help='Path to folder with videos. If None, then no information of videos (fps, length, \ + width and height) will be stored in final csv file') + parser.add_argument('--num-classes', '-nc', default=100, type=int, help='Number of classes to use in WLASL dataset') + parser.add_argument('--create-new-split', action='store_true') + parser.add_argument('--test-size', '-ts', default=0.25, type=float, + help='Test split percentage size. Only required if --create-new-split is set') + + +# python3 preprocessing.py --landmarks-dataset=data/landmarks -videos data/wlasl/videos +def create(args): + logger = get_logger(__name__) + + landmarks_dataset = args.landmarks_dataset + videos_folder = args.videos_folder + dataset_folder = args.dataset_folder + num_classes = args.num_classes + test_size = args.test_size + + os.makedirs(dataset_folder, exist_ok=True) + + # shutil.copy(os.path.join(BASE_DATA_FOLDER, 'wlasl/id_to_label.json'), dataset_folder) + # shutil.copy(os.path.join(BASE_DATA_FOLDER, 'wlasl/WLASL_v0.3.json'), dataset_folder) + + # get files in landmarks_dataset folder + landmarks_files = os.listdir(landmarks_dataset) + + video_data = [] + for i, file in enumerate(tqdm(landmarks_files)): + + # split by ! + label = file.split('!')[0] + subset = file.split('!')[1].split('.')[0] + + # remove npy and set mp4 + video_id = file.replace('.npy', "") + + + video_dict = {'video_id': video_id, + 'label_name': label, + 'split': subset} + + if videos_folder is not None: + cap = cv2.VideoCapture(op.join(videos_folder, f'{video_id}.mp4')) + if not cap.isOpened(): + logger.warning(f'Video {video_id}.mp4 not found') + continue + width = cap.get(cv2.CAP_PROP_FRAME_WIDTH) + height = cap.get(cv2.CAP_PROP_FRAME_HEIGHT) + fps = cap.get(cv2.CAP_PROP_FPS) + length = cap.get(cv2.CAP_PROP_FRAME_COUNT) / float(cap.get(cv2.CAP_PROP_FPS)) + video_info = {'video_width': width, + 'video_height': height, + 'fps': fps, + 'length': length} + video_dict.update(video_info) + video_data.append(video_dict) + + df_video = pd.DataFrame(video_data) + video_ids = df_video['video_id'].unique() + lmks_data = [] + lmks_names = get_landmarks_names().split(',') + + # get labels from df_video + labels = df_video['label_name'].unique() + # map labels to ids + label_to_id = {label: i for i, label in enumerate(labels)} + + # add label_id column to df_video + df_video['labels'] = df_video['label_name'].map(label_to_id) + + # export to json file as id to label + id_to_label = {i: label for label, i in label_to_id.items()} + with open(op.join(dataset_folder, 'id_to_label.json'), 'w') as f: + json.dump(id_to_label, f, indent=4) + + for video_id in video_ids: + lmk_fn = op.join(landmarks_dataset, f'{video_id}.npy') + if not op.exists(lmk_fn): + logger.warning(f'{lmk_fn} file not found. Skipping') + continue + lmk = np.load(lmk_fn).T + lmks_dict = {'video_id': video_id} + for lmk_, name in zip(lmk, lmks_names): + lmks_dict[name] = lmk_ + lmks_data.append(lmks_dict) + + df_lmks = pd.DataFrame(lmks_data) + df = pd.merge(df_video, df_lmks) + aux_columns = ['split', 'video_id', 'labels', 'label_name'] + if videos_folder is not None: + aux_columns += ['video_width', 'video_height', 'fps', 'length'] + df_aux = df[aux_columns] + df = map_blazepose_df(df) + df = pd.concat([df, df_aux], axis=1) + if args.create_new_split: + df_train, df_test = train_test_split(df, test_size=test_size, stratify=df['labels'], random_state=42) + + + print(f'Num classes: {num_classes}') + print(df_train['labels'].value_counts()) + print(df_test['labels'].value_counts()) + assert set(df_train['labels'].unique()) == set(df_test['labels'].unique( + )), 'The labels for train and test dataframe are different. We recommend to download the dataset again, or to use \ + the --create-new-split flag' + for split, df_split in zip(['train', 'val'], + [df_train, df_test]): + fn_out = op.join(dataset_folder, f'{split}.csv') + (df_split.reset_index(drop=True) + .applymap(convert_to_str) + .to_csv(fn_out, index=False)) + + else: + fn_out = op.join(dataset_folder, 'train.csv') + (df.reset_index(drop=True) + .applymap(convert_to_str) + .to_csv(fn_out, index=False)) \ No newline at end of file diff --git a/preprocessing/create_google_asl_landmarks_dataset.py b/preprocessing/create_google_asl_landmarks_dataset.py index 4a4beda..c128356 100644 --- a/preprocessing/create_google_asl_landmarks_dataset.py +++ b/preprocessing/create_google_asl_landmarks_dataset.py @@ -4,6 +4,8 @@ import pandas as pd from tqdm.auto import tqdm import json +from normalization.blazepose_mapping import map_blazepose_df + def create(train_landmark_files, train_csv, dataset_folder, test_size): os.makedirs(dataset_folder, exist_ok=True) @@ -17,15 +19,15 @@ def create(train_landmark_files, train_csv, dataset_folder, test_size): mapping = { 'pose_0': 'nose', 'pose_1': 'leftEye', - 'pose_2': 'rightEye', - 'pose_3': 'leftEar', - 'pose_4': 'rightEar', - 'pose_5': 'leftShoulder', - 'pose_6': 'rightShoulder', - 'pose_7': 'leftElbow', - 'pose_8': 'rightElbow', - 'pose_9': 'leftWrist', - 'pose_10': 'rightWrist', + 'pose_4': 'rightEye', + 'pose_7': 'leftEar', + 'pose_8': 'rightEar', + 'pose_11': 'leftShoulder', + 'pose_12': 'rightShoulder', + 'pose_13': 'leftElbow', + 'pose_14': 'rightElbow', + 'pose_15': 'leftWrist', + 'pose_16': 'rightWrist', 'left_hand_0': 'wrist_left', 'left_hand_1': 'thumbCMC_left', @@ -77,7 +79,7 @@ def create(train_landmark_files, train_csv, dataset_folder, test_size): columns.append(f'{v}_X') columns.append(f'{v}_Y') - for _, row in tqdm(train_df.head(6000).iterrows(), total=6000): + for _, row in tqdm(train_df.head(10000).iterrows(), total=10000): path, participant_id, sequence_id, sign = row['path'], row['participant_id'], row['sequence_id'], row['sign'] parquet_file = os.path.join(train_landmark_files, str(participant_id), f"{sequence_id}.parquet") @@ -136,6 +138,7 @@ def create(train_landmark_files, train_csv, dataset_folder, test_size): video_data.append(new_landmark_data) video_data = pd.concat(video_data, axis=0, ignore_index=True) + video_data = map_blazepose_df(video_data, rename=False) video_data.to_csv(os.path.join(dataset_folder, 'spoter.csv'), index=False) train_landmark_files = 'data/train_landmark_files' diff --git a/preprocessing/create_wlasl_landmarks_dataset.py b/preprocessing/create_wlasl_landmarks_dataset.py index c1e4d5c..53d5cf4 100644 --- a/preprocessing/create_wlasl_landmarks_dataset.py +++ b/preprocessing/create_wlasl_landmarks_dataset.py @@ -110,6 +110,7 @@ def create(args): 'length': length} video_dict.update(video_info) video_data.append(video_dict) + df_video = pd.DataFrame(video_data) video_ids = df_video['video_id'].unique() lmks_data = [] @@ -126,9 +127,7 @@ def create(args): lmks_data.append(lmks_dict) df_lmks = pd.DataFrame(lmks_data) - print(df_lmks) df = pd.merge(df_video, df_lmks) - print(df) aux_columns = ['split', 'video_id', 'labels', 'label_name'] if videos_folder is not None: aux_columns += ['video_width', 'video_height', 'fps', 'length'] diff --git a/preprocessing/extract_mediapipe_landmarks.py b/preprocessing/extract_mediapipe_landmarks.py index 6d63076..f02421f 100644 --- a/preprocessing/extract_mediapipe_landmarks.py +++ b/preprocessing/extract_mediapipe_landmarks.py @@ -35,7 +35,11 @@ class LandmarksResults: ): self.results = results self.num_landmarks_pose = num_landmarks_pose - self.num_landmarks_hand = num_landmarks_hand + self.num_landmarks_hand = num_landmarks_hand + + @property + def empty(self): + return self.results.pose_landmarks is None or (self.results.left_hand_landmarks is None and self.results.right_hand_landmarks is None) @property def pose_landmarks(self): @@ -67,6 +71,10 @@ def get_landmarks(image_orig, holistic, debug=False): # Convert the BGR image to RGB before processing. image = cv2.cvtColor(image_orig, cv2.COLOR_BGR2RGB) results = LandmarksResults(holistic.process(image)) + + if results.empty: + return None + if debug: lmks_pose = [] for lmk in results.pose_landmarks: @@ -94,6 +102,7 @@ def get_landmarks(image_orig, holistic, debug=False): len(lmks_right_hand) == 2 * LEN_LANDMARKS_HAND ), f"{len(lmks_right_hand)} != {2 * LEN_LANDMARKS_HAND}" landmarks = [] + for lmk in chain( results.pose_landmarks, results.left_hand_landmarks, @@ -128,10 +137,21 @@ def extract(args): videos_folder = args.videos_folder os.makedirs(landmarks_output, exist_ok=True) for fn_video in tqdm(sorted(glob.glob(op.join(videos_folder, "*mp4")))): + + # check if landmarks already exist + if op.exists(op.join(landmarks_output, op.basename(fn_video).split(".")[0] + ".npy")): + continue + cap = cv2.VideoCapture(fn_video) ret, image_orig = cap.read() height, width = image_orig.shape[:2] landmarks_video = [] + + # make sure fps is 20 by determining the number of frames to be skipped + frame_rate = int(cap.get(cv2.CAP_PROP_FPS)) + frame_skip = (frame_rate // 10) - 1 + + with tqdm(total=int(cap.get(cv2.CAP_PROP_FRAME_COUNT))) as pbar: with mp_holistic.Holistic( static_image_mode=False, @@ -145,7 +165,11 @@ def extract(args): print(e) landmarks = get_landmarks(image_orig, holistic, debug=True) ret, image_orig = cap.read() - landmarks_video.append(landmarks) + for _ in range(frame_skip): + ret, image_orig = cap.read() + pbar.update(1) + if landmarks: + landmarks_video.append(landmarks) pbar.update(1) landmarks_video = np.vstack(landmarks_video) np.save( diff --git a/preprocessing/split_dataset.py b/preprocessing/split_dataset.py index 17e1f55..24e3133 100644 --- a/preprocessing/split_dataset.py +++ b/preprocessing/split_dataset.py @@ -8,7 +8,6 @@ dataset = "data/processed/spoter.csv" # read the dataset df = pd.read_csv(dataset) -df = map_blazepose_df(df) with open("data/sign_to_prediction_index_map.json", "r") as f: sign_to_prediction_index_max = json.load(f) @@ -17,6 +16,9 @@ with open("data/sign_to_prediction_index_map.json", "r") as f: # filter df to make sure each sign has at least 4 samples df = df[df["sign"].map(df["sign"].value_counts()) > 4] +# print number of unique signs +print("Number of unique signs: ", len(df["sign"].unique())) + # use the path column to split the dataset paths = df["path"].unique() diff --git a/requirements.txt b/requirements.txt index 2308b4c..4ec4834 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,6 @@ pandas bokeh==2.4.3 boto3>=1.9 -clearml==1.6.4 ipywidgets==8.0.4 matplotlib==3.5.3 mediapipe==0.8.11 @@ -9,6 +8,9 @@ notebook==6.5.2 opencv-python==4.6.0.66 plotly==5.11.0 scikit-learn==1.0.2 -torch -torchvision +clearml==1.10.3 +torch==2.0.0 +torchvision==0.15.1 tqdm==4.54.1 +optuna==3.1.1 +onnx==1.14.0 \ No newline at end of file diff --git a/train.py b/train.py index 76fabfe..7c29d1e 100644 --- a/train.py +++ b/train.py @@ -15,7 +15,7 @@ from torchvision import transforms from torch.utils.data import DataLoader from pathlib import Path import copy - +import numpy as np from datasets import CzechSLRDataset, SLREmbeddingDataset, collate_fn_triplet_padd, collate_fn_padd from models import SPOTER, SPOTER_EMBEDDINGS, train_epoch, evaluate, train_epoch_embedding, \ train_epoch_embedding_online, evaluate_embedding @@ -32,7 +32,7 @@ except ImportError: pass -PROJECT_NAME = "spoter" +PROJECT_NAME = "SpoterEmbedding" CLEARML = "clearml" @@ -75,12 +75,25 @@ def train(args, tracker: Tracker): # Construct the model if not args.classification_model: - slrt_model = SPOTER_EMBEDDINGS( - features=args.vector_length, - hidden_dim=args.hidden_dim, - norm_emb=args.normalize_embeddings, - dropout=args.dropout - ) + # if finetune, load the weights from the classification model + if args.finetune: + checkpoint = torch.load(args.checkpoint_path, map_location=device) + + slrt_model = SPOTER_EMBEDDINGS( + features=checkpoint["config_args"].vector_length, + hidden_dim=checkpoint["config_args"].hidden_dim, + norm_emb=checkpoint["config_args"].normalize_embeddings, + dropout=checkpoint["config_args"].dropout, + ) + else: + slrt_model = SPOTER_EMBEDDINGS( + features=args.vector_length, + hidden_dim=args.hidden_dim, + norm_emb=args.normalize_embeddings, + dropout=args.dropout, + ) + + model_type = 'embed' if args.hard_triplet_mining == "None": cel_criterion = nn.TripletMarginLoss(margin=args.triplet_loss_margin, p=2) @@ -233,6 +246,9 @@ def train(args, tracker: Tracker): val_accs.append(val_acc) tracker.log_scalar_metric("acc", "val", epoch, val_acc) + create_embedding_scatter_plots(tracker, slrt_model, train_loader, val_loader, device, id_to_label, epoch, + top_model_name) + logger.info(f"Epoch time: {datetime.now() - start_time}") logger.info("[" + str(epoch) + "] TRAIN loss: " + str(train_loss) + " acc: " + str(train_accs[-1])) logger.info("[" + str(epoch) + "] VALIDATION acc: " + str(val_accs[-1])) diff --git a/train.sh b/train.sh index fe68689..9b97974 100755 --- a/train.sh +++ b/train.sh @@ -1,22 +1,24 @@ #!/bin/sh -python -m train \ +python3 -m train \ --save_checkpoints_every 10 \ - --experiment_name "augment_rotate_75_x8" \ - --epochs 300 \ + --experiment_name "Finetune Fingerspelling Signs" \ + --epochs 1000 \ --optimizer "ADAM" \ - --lr 0.001 \ - --batch_size 16 \ - --dataset_name "processed" \ - --training_set_path "spoter_train.csv" \ - --validation_set_path "spoter_test.csv" \ + --lr 0.00001 \ + --batch_size 8 \ + --dataset_name "FingerSpelling" \ + --training_set_path "train.csv" \ + --validation_set_path "val.csv" \ --vector_length 32 \ --epoch_iters -1 \ --scheduler_factor 0 \ --hard_triplet_mining "in_batch" \ --filter_easy_triplets \ - --triplet_loss_margin 1 \ + --start_mining_hard 50 \ + --triplet_loss_margin 4 \ --dropout 0.2 \ - --augmentations_prob=0.75 \ - --hard_mining_scheduler_triplets_threshold=0 \ - --normalize_embeddings \ - --num_classes 100 \ \ No newline at end of file + --tracker=clearml \ + --dataset_loader=clearml \ + --dataset_project="SpoterEmbedding" \ + --finetune \ + --checkpoint_path "checkpoints/checkpoint_embed_3835.pth" \ No newline at end of file diff --git a/training/train_arguments.py b/training/train_arguments.py index 5980aff..1a56874 100644 --- a/training/train_arguments.py +++ b/training/train_arguments.py @@ -81,4 +81,7 @@ def get_default_args(): help="Enables batching grouping scheduler if > 0. Defines threshold for when to decay the \ distance threshold of the batch sorter") + parser.add_argument("--finetune", action='store_true', default=False, help="Fintune the model") + parser.add_argument("--checkpoint_path", type=str, default="") + return parser diff --git a/visualize_data.ipynb b/visualize_data.ipynb new file mode 100644 index 0000000..2146c06 --- /dev/null +++ b/visualize_data.ipynb @@ -0,0 +1,1636 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "from augmentations.augment import __preprocess_row_sign" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 path\n", + "1 participant_id\n", + "2 sequence_id\n", + "3 sign\n", + "4 labels\n", + "5 nose_X\n", + "6 nose_Y\n", + "7 leftEye_X\n", + "8 leftEye_Y\n", + "9 rightEye_X\n", + "10 rightEye_Y\n", + "11 leftEar_X\n", + "12 leftEar_Y\n", + "13 rightEar_X\n", + "14 rightEar_Y\n", + "15 leftShoulder_X\n", + "16 leftShoulder_Y\n", + "17 rightShoulder_X\n", + "18 rightShoulder_Y\n", + "19 leftElbow_X\n", + "20 leftElbow_Y\n", + "21 rightElbow_X\n", + "22 rightElbow_Y\n", + "23 leftWrist_X\n", + "24 leftWrist_Y\n", + "25 rightWrist_X\n", + "26 rightWrist_Y\n", + "27 wrist_left_X\n", + "28 wrist_left_Y\n", + "29 thumbCMC_left_X\n", + "30 thumbCMC_left_Y\n", + "31 thumbMP_left_X\n", + "32 thumbMP_left_Y\n", + "33 thumbIP_left_X\n", + "34 thumbIP_left_Y\n", + "35 thumbTip_left_X\n", + "36 thumbTip_left_Y\n", + "37 indexMCP_left_X\n", + "38 indexMCP_left_Y\n", + "39 indexPIP_left_X\n", + "40 indexPIP_left_Y\n", + "41 indexDIP_left_X\n", + "42 indexDIP_left_Y\n", + "43 indexTip_left_X\n", + "44 indexTip_left_Y\n", + "45 middleMCP_left_X\n", + "46 middleMCP_left_Y\n", + "47 middlePIP_left_X\n", + "48 middlePIP_left_Y\n", + "49 middleDIP_left_X\n", + "50 middleDIP_left_Y\n", + "51 middleTip_left_X\n", + "52 middleTip_left_Y\n", + "53 ringMCP_left_X\n", + "54 ringMCP_left_Y\n", + "55 ringPIP_left_X\n", + "56 ringPIP_left_Y\n", + "57 ringDIP_left_X\n", + "58 ringDIP_left_Y\n", + "59 ringTip_left_X\n", + "60 ringTip_left_Y\n", + "61 littleMCP_left_X\n", + "62 littleMCP_left_Y\n", + "63 littlePIP_left_X\n", + "64 littlePIP_left_Y\n", + "65 littleDIP_left_X\n", + "66 littleDIP_left_Y\n", + "67 littleTip_left_X\n", + "68 littleTip_left_Y\n", + "69 wrist_right_X\n", + "70 wrist_right_Y\n", + "71 thumbCMC_right_X\n", + "72 thumbCMC_right_Y\n", + "73 thumbMP_right_X\n", + "74 thumbMP_right_Y\n", + "75 thumbIP_right_X\n", + "76 thumbIP_right_Y\n", + "77 thumbTip_right_X\n", + "78 thumbTip_right_Y\n", + "79 indexMCP_right_X\n", + "80 indexMCP_right_Y\n", + "81 indexPIP_right_X\n", + "82 indexPIP_right_Y\n", + "83 indexDIP_right_X\n", + "84 indexDIP_right_Y\n", + "85 indexTip_right_X\n", + "86 indexTip_right_Y\n", + "87 middleMCP_right_X\n", + "88 middleMCP_right_Y\n", + "89 middlePIP_right_X\n", + "90 middlePIP_right_Y\n", + "91 middleDIP_right_X\n", + "92 middleDIP_right_Y\n", + "93 middleTip_right_X\n", + "94 middleTip_right_Y\n", + "95 ringMCP_right_X\n", + "96 ringMCP_right_Y\n", + "97 ringPIP_right_X\n", + "98 ringPIP_right_Y\n", + "99 ringDIP_right_X\n", + "100 ringDIP_right_Y\n", + "101 ringTip_right_X\n", + "102 ringTip_right_Y\n", + "103 littleMCP_right_X\n", + "104 littleMCP_right_Y\n", + "105 littlePIP_right_X\n", + "106 littlePIP_right_Y\n", + "107 littleDIP_right_X\n", + "108 littleDIP_right_Y\n", + "109 littleTip_right_X\n", + "110 littleTip_right_Y\n", + "111 neck_X\n", + "112 neck_Y\n" + ] + } + ], + "source": [ + "df = pd.read_csv('data/processed/spoter_train.csv')\n", + "for i, e in enumerate(df.columns):\n", + " print(i, e)" + ] + }, + { + "cell_type": "code", + "execution_count": 44, + "metadata": {}, + "outputs": [], + "source": [ + "# dataset = 'data/processed/spoter_train.csv'\n", + "\n", + "def plot(dataset):\n", + " df = pd.read_csv(dataset)\n", + " # get columns 0 - 26\n", + " # df = df.iloc[:, 5:27]\n", + "\n", + "\n", + " # df = df.iloc[:, 27:69]\n", + " # df = df.iloc[:, 69:]\n", + "\n", + " if 'wlasl' in dataset:\n", + " df = df.iloc[:, :108]\n", + "\n", + " else:\n", + " df = df.iloc[:, 5:]\n", + "\n", + "\n", + " # get first row\n", + " row = df.iloc[20]\n", + "\n", + " # use matplotlib to create a scatter plot (the columns are X, Y, X, Y, X, Y, X, Y)\n", + " import matplotlib.pyplot as plt\n", + " import ast\n", + "\n", + " def __process_row(coords):\n", + " coords = coords.tolist()\n", + "\n", + " new_coords = [] # string to list\n", + "\n", + " for x in coords:\n", + " new_coords.append(ast.literal_eval(x))\n", + "\n", + " coords = [x[0] for x in new_coords]\n", + "\n", + " return [float(x) for x in coords]\n", + "\n", + "\n", + " # extract X and Y coordinates from the row\n", + " x_coords = row.iloc[::2] # columns 0, 2, 4, 6, ...\n", + " y_coords = row.iloc[1::2] # columns 1, 3, 5, 7, ...\n", + "\n", + " x_coords = __process_row(x_coords)\n", + " y_coords = __process_row(y_coords)\n", + "\n", + " # create a scatter plot\n", + " plt.scatter(x_coords, y_coords)\n", + "\n", + "\n", + " plt.xlim(0, 1)\n", + " plt.ylim(0, 1)\n", + " \n", + " # add axis labels and a title\n", + " plt.xlabel('X')\n", + " plt.ylabel('Y')\n", + " plt.title('Scatter plot of X and Y coordinates')\n", + "\n", + " # show the plot\n", + " plt.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 45, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAHHCAYAAABTMjf2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA6QklEQVR4nO3de1yUZf7/8TegzHjgoCEHjURNMzyRKCweMg3DdDFrLdPylJtpZim1q6aJh0rXyqU8a6V9bV3dytoOhgc2tzXZxRVtPaQdxLRWUDPBNEWZ6/eHP2YdAQUDBuZ+PR+PeTyaa677vj/3XEPz9r6v+x4vY4wRAACABXm7uwAAAAB3IQgBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBHuC2227Tbbfd5u4yXOTk5Kh///667rrr5OXlpZSUFHeXVC4iIiI0bNgwd5dRJVz+uTt48KC8vLy0YsUKt9UElBVBCFXWrl271L9/fzVu3Fh2u12NGjVSz549NW/evArb5qpVq4r9wv7vf/+radOmaefOnRW2bXc4c+aMpk2bps2bN5f7usePH6/169dr0qRJWrlypXr16lVsv40bN8rLy0vTp08v8lpWVpZq166t/v37l3t9FWX16tXy8vLSkiVLin199OjRqlmzpj7//PNKrsxzPP/883rvvffcXQY8hQGqoM8++8z4+vqaG2+80cycOdMsW7bMTJ061dxxxx2mWbNmFbbdPn36mMaNGxdp37Ztm5Fkli9fXmHb/iW6detmunXrVubljh07ZiSZ5OTkcq8pJCTEPPDAA6XqO2jQIGOz2cz+/ftd2nv16mX8/f3N999/X+71XavGjRuboUOHXrFPr169TGBgoMnOznZp/9e//mW8vb3N7373uwqssPJc/rlzOBzm559/NhcuXKjQ7dapU+eqYwCUVg13BzGgOM8995wCAgK0bds2BQYGurx29OhR9xRVAU6fPq06deq4u4wKcfTo0SJjV5I//vGP+vjjjzVq1Cj97W9/k3TxyEpqaqpeeeUVNWzYsAIrLX+LFi1Sq1atNH78eK1atUqSVFBQoEceeUQ33HCDpk2b5t4CS6msn08vLy/Z7fYKrAioAO5OYkBxbrrpJnPbbbeVuv/KlStNx44dTa1atUxgYKDp2rWrWb9+vfP19957z/Tu3duEhYUZX19f07RpUzNjxgyXf7l269bNSHJ5NG7c2HzyySdF2nXZ0aF//vOfJiEhwfj7+5tatWqZW2+91WzZssWlxuTkZCPJ7NmzxwwcONAEBgaaqKioEvdp+fLlRpL5+9//bkaOHGnq169v/Pz8zODBg82JEydc+hZ3RCgnJ8c89NBDJjg42NhsNtO2bVuzYsUK5+tZWVnF7tfVjg598803pn///qZevXqmVq1aJjY21nz44YdF6r78cTVLly41ksyKFSvMjz/+aEJDQ03Hjh1NQUHBVZc9ePCgGT16tGnRooWx2+2mfv36pn///iYrK8ulX2FtW7ZsMePHjzdBQUGmdu3apl+/fubo0aMufR0Oh5k5c6Zp1KiRqVWrlrntttvM7t27S3VEyBhj5syZYySZDRs2GGOMmTt3rpFkPv7446sua8zFz9Sdd95pAgMDTe3atU2bNm1MSkqKS5+0tDTTpUsXU7t2bRMQEGD69u1r9u7dW2RdmZmZplevXsbPz8/UqVPH9OjRw6Snpxf73mzevNmMHj3aNGjQwAQGBjpfX7JkiWnatKmx2+2mY8eO5tNPPy3yuSv8TF36tzF06FBTp04d891335m77rrL1KlTxwQFBZknn3yyyJGjF154wcTFxZn69esbu91u2rdvb9566y2XPsV9ti4dj++++84MHz7cBAcHG19fXxMZGWlee+21Iu/JK6+8YiIjI53/z4iOjjZ/+tOfShwPeC6CEKqkO+64w/j5+Zldu3Zdte+0adOMJNOpUyfzwgsvmJdfftkMGjTITJgwwdmnX79+5r777jMvvPCCWbRokbn33nuNJPPUU085+2zYsMFERUWZoKAgs3LlSrNy5Urz7rvvmuzsbDNjxgwjyYwcOdL52jfffGOMufhl5Ovra+Li4sxLL71k/vjHP5q2bdsaX19f869//cu5/sIgFBkZae666y6zcOFCs2DBghL3q/CLqU2bNqZr167mlVdeMWPGjDHe3t7m1ltvNQ6Hw9n38i+kM2fOmJtvvtnUrFnTjB8/3rzyyiuma9euRpLzy/Snn34yixYtMpLM3Xff7dyvzz//vMSasrOzTUhIiPHz8zOTJ082c+fONe3atTPe3t5m7dq1xpiLQWnlypVGkunZs6dzvVfjcDhM586dTVBQkBk4cKDx8fExmZmZV13OGGPeeust065dOzN16lSzdOlS8/TTT5t69eqZxo0bm9OnTxd5T2+55RbTo0cPM2/ePPPkk08aHx8fc99997msc8qUKUaS6d27t5k/f7556KGHTMOGDU1QUFCpgtD58+dNu3btTLNmzcxXX31l6tata+6///5S7c+GDRuMr6+vady4sUlOTjaLFi0yjz/+uImPj3f22bhxo6lRo4Zp0aKFmTNnjpk+fboJCgoy9erVcwmAu3fvNnXq1DFhYWFm5syZZvbs2aZJkybGZrOZf/7zn0Xem8jISNOtWzczb948M3v2bGOMMa+++qrzb+yVV14x48aNM4GBgaZp06alCkJ2u920atXKPPTQQ2bRokXmN7/5jZFkFi5c6LLf119/vXn00UfN/Pnzzdy5c01MTIyR5BK0V65caWw2m+natavzs7V161ZjzMXP5/XXX2/Cw8PNjBkzzKJFi0zfvn2NJPPHP/7RuY7C0N2/f3+zZMkS8/LLL5sRI0aYxx9/vFTjA89CEEKVtGHDBuPj42N8fHxMXFyc+f3vf2/Wr19v8vPzXfp99dVXxtvb29x9991FjhxcGhTOnDlTZBuPPPKIqV27tjl79qyzraxzhBwOh2nevLlJSEgosr0mTZqYnj17OtsKg9DAgQNL9R4UfjFFR0e77HfhkYa//vWvzrbLg1BKSoqRZN58801nW35+vomLizN169Y1eXl5xpiyzxEaN26ckWT+8Y9/ONtOnTplmjRpYiIiIlzGQJIZM2ZMqdZbaPfu3aZmzZpGkhk3blyplytufNPT040k83//93/OtsL3ND4+3mW8xo8fb3x8fMzJkyeNMcYcPXrU+Pr6mj59+rj0e/rpp4scgbiSwjlB9evXL3bOUHEuXLhgmjRpYho3bmx+/PFHl9curSUqKsoEBwebH374wdn2+eefG29vbzNkyBBnW79+/Yyvr68zuBtjzH//+1/j5+dnbr31Vmdb4XvTpUsXlyM1+fn5Jjg42ERFRZlz58452wvDRGmCkCQzY8YMl3255ZZbTHR0tEvb5eOYn59vWrdubXr06OHSXtIcoREjRpiwsDBz/Phxl/b777/fBAQEONd/1113mVatWhVZHtbEVWOoknr27Kn09HT17dtXn3/+uebMmaOEhAQ1atRI77//vrPfe++9J4fDoalTp8rb2/Xj7OXl5fzvWrVqOf/71KlTOn78uLp27aozZ85o375911znzp079dVXX2nQoEH64YcfdPz4cR0/flynT5/W7bffrk8//VQOh8NlmVGjRpVpGyNHjlTNmjWdz0ePHq0aNWpo3bp1JS6zbt06hYaGauDAgc62mjVr6vHHH9dPP/2kv//972Wq4dL1xsTEqEuXLs62unXrauTIkTp48KD27t17Test5O/vL19fX0nSHXfcUerlLh3f8+fP64cfftCNN96owMBAZWZmFuk/cuRIl89H165dVVBQoG+//VaStGnTJuXn52vs2LEu/caNG1em/YmJidGoUaN04sQJzZo1SyEhIVddZseOHcrKytK4ceOKzLEqrOXIkSPauXOnhg0bpvr16ztfb9u2rXr27On8bBQUFGjDhg3q16+fmjZt6uwXFhamQYMGacuWLcrLy3PZxsMPPywfHx/n83//+986evSoRo0a5RwbSRo2bJgCAgJK/V5c/rnv2rWrDhw44NJ26Tj++OOPys3NVdeuXYsdw8sZY/TOO+8oMTFRxhjn3+Lx48eVkJCg3Nxc53oCAwP13Xffadu2baWuH56LIIQqq2PHjlq7dq1+/PFHZWRkaNKkSTp16pT69+/v/ML95ptv5O3trcjIyCuua8+ePbr77rsVEBAgf39/NWjQQA8++KAkKTc395pr/OqrryRJQ4cOVYMGDVwer776qs6dO1dk/U2aNCnTNpo3b+7yvG7dugoLC9PBgwdLXObbb79V8+bNi4TDm2++2fn6tfj222910003FWn/pest9Nhjj8nb21uNGzfWk08+qfPnz5dquZ9//llTp05VeHi4bDabgoKC1KBBA508ebLY8b3hhhtcnterV0/SxS/fS/fj8ve+QYMGzr6l1bFjR0lShw4dStX/m2++kSS1bt26xD6F9ZU0FoVh/NixYzpz5kyJ/RwOhw4fPuzSfvnns6T3ombNmi7h6krsdrsaNGjg0lavXj3n+13oww8/1K9+9SvZ7XbVr19fDRo00KJFi0r1N3rs2DGdPHlSS5cuLfK3OHz4cEn/u9BiwoQJqlu3rmJiYtS8eXONGTNGn332Wan2BZ6Hq8ZQ5fn6+qpjx47q2LGjWrRooeHDh+utt95ScnJyqZY/efKkunXrJn9/f82YMUPNmjWT3W5XZmamJkyYUOSITVkULvvCCy8oKiqq2D5169Z1eX7pv3rxP2vXrtX777+vlJQUNW/eXH369NELL7ygp59++qrLjh07VsuXL9e4ceMUFxengIAAeXl56f777y92fC894nEpY8wv3o/qriI+nyW935f6xz/+ob59++rWW2/VwoULFRYWppo1a2r58uXOK++upHCcH3zwQQ0dOrTYPm3btpV0MQTu379fH374oVJTU/XOO+9o4cKFmjp1arH3s4JnIwihWin8V/WRI0ckSc2aNZPD4dDevXtLDCKbN2/WDz/8oLVr1+rWW291tmdlZRXpe+lpkNK0N2vWTNLFUzrx8fGl3o+y+Oqrr9S9e3fn859++klHjhxR7969S1ymcePG+s9//iOHw+FyVKjwNGDjxo0llbxfV1rv/v37i7Rfvt6yOnXqlB5//HG1b99ejz32mHx8fPSb3/xGzz77rAYOHHjVo2hvv/22hg4dqpdeesnZdvbsWZ08efKa6incj6+++srlqMexY8eKHMUob4Wfqd27d5f4mSqsr6SxCAoKUp06dWS321W7du0S+3l7eys8PPyK9Vz6XvTo0cPZfv78eWVlZaldu3al27GreOedd2S327V+/XrZbDZn+/Lly4v0Le5z26BBA/n5+amgoKBUf4t16tTRgAEDNGDAAOXn5+uee+7Rc889p0mTJnELAIvh1BiqpE8++aTYf50Xzn0oPNTfr18/eXt7a8aMGUX+5V+4fOG/Ri9dX35+vhYuXFhk/XXq1Cn2MHzhvVQu/2KNjo5Ws2bN9OKLL+qnn34qstyxY8dK3MfSWrp0qcspokWLFunChQu68847S1ymd+/eys7O1po1a5xtFy5c0Lx581S3bl1169ZNklS7dm1JRffrSuvNyMhQenq6s+306dNaunSpIiIirnqKsiRTpkzRkSNHtGTJEud4vfzyy/Lx8dFjjz121eV9fHyKfF7mzZungoKCa6onPj5eNWvW1Lx581zWWxk/E9K+fXs1adJEKSkpRcalsJawsDBFRUXpjTfecOmze/dubdiwwRmSfXx8dMcdd+ivf/2ry6nUnJwcrVq1Sl26dJG/v/8V6+nQoYMaNGigxYsXKz8/39m+YsWKaw6axfHx8ZGXl5fLmB08eLDYO0jXqVOnyLYLw/M777yj3bt3F1nm0r/FH374weU1X19fRUZGyhhT6tOx8BwcEUKVNHbsWJ05c0Z33323WrZsqfz8fG3dulVr1qxRRESE85z/jTfeqMmTJ2vmzJnq2rWr7rnnHtlsNm3btk0NGzbUrFmz1KlTJ9WrV09Dhw7V448/Li8vL61cubLYoBUdHa01a9YoKSlJHTt2VN26dZWYmKhmzZopMDBQixcvlp+fn+rUqaPY2Fg1adJEr776qu688061atVKw4cPV6NGjfT999/rk08+kb+/vz744INf9F7k5+fr9ttv13333af9+/dr4cKF6tKli/r27VviMiNHjtSSJUs0bNgwbd++XREREXr77bf12WefKSUlRX5+fpIungaJjIzUmjVr1KJFC9WvX1+tW7cucX7KxIkT9ec//1l33nmnHn/8cdWvX19vvPGGsrKy9M477xSZk1Qa27dv14IFCzRmzBiXeTSNGjXSjBkzlJSUpHfeeUe/+c1vSlzHr3/9a61cuVIBAQGKjIxUenq6Nm3apOuuu67M9UgXjy489dRTmjVrln7961+rd+/e2rFjhz7++GMFBQVd0zpLy9vbW4sWLVJiYqKioqI0fPhwhYWFad++fdqzZ4/Wr18v6eLp2DvvvFNxcXEaMWKEfv75Z82bN08BAQEuN2x89tlntXHjRnXp0kWPPvqoatSooSVLlujcuXOaM2fOVeupWbOmnn32WT3yyCPq0aOHBgwYoKysLC1fvrzUc4RKo0+fPpo7d6569eqlQYMG6ejRo1qwYIFuvPFG/ec//3HpGx0drU2bNmnu3Llq2LChmjRpotjYWM2ePVuffPKJYmNj9fDDDysyMlInTpxQZmamNm3apBMnTki6OBE/NDRUnTt3VkhIiL744gvNnz9fffr0cf5twELcdLUacEUff/yxeeihh0zLli1N3bp1nT+3MXbsWJOTk1Ok/+uvv25uueUWY7PZTL169Uy3bt3Mxo0bna9/9tln5le/+pWpVauWadiwofNyfEnmk08+cfb76aefzKBBg0xgYKDzhoqF/vrXv5rIyEhTo0aNIpcI79ixw9xzzz3muuuuMzabzTRu3Njcd999Ji0tzdmn8PL5Y8eOleo9uPyGivXq1TN169Y1DzzwgMsl08aUfEPF4cOHm6CgIOPr62vatGlT7E+EbN261URHRxtfX98y3VAxMDDQ2O12ExMT43Kfl0IqxeXzFy5cMO3btzcNGzY0ubm5xb4eFRVlrr/+enPq1KkS1/Pjjz8697Vu3bomISHB7Nu3r8jNDwvf023btrks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g7Vs2TIFBQWVaplJkyYpNzfX+Th8+HAFVwkAAKqLGu7ceFBQkHx8fJSTk+PSnpOTo9DQ0CL9v/nmGx08eFCJiYnONofDIUmqUaOG9u/fr2bNmrksY7PZZLPZKqB6AABQ3bn1iJCvr6+io6OVlpbmbHM4HEpLS1NcXFyR/i1bttSuXbu0c+dO56Nv377q3r27du7cyWkvAABQJm49IiRJSUlJGjp0qDp06KCYmBilpKTo9OnTGj58uCRpyJAhatSokWbNmiW73a7WrVu7LB8YGChJRdoBAACuxu1BaMCAATp27JimTp2q7OxsRUVFKTU11TmB+tChQ/L2rlZTmQAAQDXhZYwx7i6iMuXl5SkgIEC5ubny9/d3dzkAAKAUKur7m0MtAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsmq4uwAAqAwFDqOMrBM6euqsgv3simlSXz7eXu4uC4CbEYQAeLzU3Uc0/YO9OpJ71tkWFmBXcmKkerUOc2NlANyNU2MAPFrq7iMa/WamSwiSpOzcsxr9ZqZSdx9xU2UAqgKCEACPVeAwmv7BXpliXitsm/7BXhU4iusBwAoIQgA8VkbWiSJHgi5lJB3JPauMrBOVVxSAKoUgBMBjHT1Vcgi6ln4APA+TpQF4lEuvDjt+6lyplgn2s1dwVVfGFW2A+xCEAHiM4q4O8/aSSpoC5CUpNOBi8HAXrmgD3ItTYwA8QklXh10pBElScmKk246+cEUb4H4EIQDV3pWuDit0edYJDbBr0YPt3XbUhSvagKqBU2MAqr2rXR0mXTwy9EyfmxXkZ6sS83DKckVbXLPrKq8wwGIIQgCqvdJe9RXkZ9NdUY0quJrS4Yo2oGrg1BiAaq+0V325++qwS1XHmgFPRBACUO3FNKmvsAC7SjrR5aWLV2K58+qwy1XHmgFPRBAC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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot('data/wlasl/WLASL100_train.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAkIAAAHHCAYAAABTMjf2AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjUuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/NK7nSAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA+8UlEQVR4nO3de1zUVeL/8TcgzHgDNBTQyFuWkRcShaU00zAs17LWMi1vtVmmllK7apakVrpdzPKulfa1bXW738wb5bYWu7iilZlWimmt4C3BNEXh/P7wx6zDRWdwhhnm83o+HjwezeF8Zs5nPkPz9pzzOSfIGGMEAABgQcG+bgAAAICvEIQAAIBlEYQAAIBlEYQAAIBlEYQAAIBlEYQAAIBlEYQAAIBlEYQAAIBlEYQAAIBlEYSAAHDNNdfommuu8XUznOTn56tfv3664IILFBQUpJkzZ/q6SR7RvHlzDR061NfN8AtlP3e7du1SUFCQlixZ4rM2Ae4iCMFvff311+rXr5+aNWsmu92upk2bqmfPnpo1a5bXXvP111+v8Av7v//9rx5//HFt3rzZa6/tC8eOHdPjjz+udevWefy5x44dq1WrVmnChAlaunSpevXqVWG9NWvWKCgoSJMnTy73u9zcXNWpU0f9+vXzePu8ZdmyZQoKCtKCBQsq/P2IESMUGhqqL7/8sppbFjieeuopvfvuu75uBgKFAfzQ559/bsLCwszFF19spk6dahYtWmQmTZpkrrvuOtOqVSuvvW7v3r1Ns2bNypVv2LDBSDKLFy/22mufj27duplu3bq5fdz+/fuNJJORkeHxNkVHR5s77rjDpboDBw40NpvNbN++3am8V69eJjw83Pz8888eb19VNWvWzAwZMuSsdXr16mUiIyNNXl6eU/m///1vExwcbP70pz95sYXVp+znrqSkxPz222/m1KlTXn3dunXrnvMaAK6q5esgBlTkySefVEREhDZs2KDIyEin3+3bt883jfKCo0ePqm7dur5uhlfs27ev3LWrzPPPP6+PP/5Y9913nz755BNJp3tWVq5cqRdffFFNmjTxYks9b968ebr88ss1duxYvf7665Kk4uJi3Xvvvbrooov0+OOP+7aBLnL38xkUFCS73e7FFgFe4OskBlTk0ksvNddcc43L9ZcuXWo6d+5sateubSIjI03Xrl3NqlWrHL9/9913zQ033GBiY2NNWFiYadmypZkyZYrTv1y7detmJDn9NGvWzHz66aflylWmd+hf//qXSUtLM+Hh4aZ27drm6quvNuvXr3dqY0ZGhpFkvvnmGzNgwAATGRlpEhISKj2nxYsXG0nmH//4hxk+fLhp2LChqV+/vhk0aJA5dOiQU92KeoTy8/PNXXfdZRo3bmxsNptp3769WbJkieP3ubm5FZ7XuXqHduzYYfr162caNGhgateubZKTk82HH35Yrt1lf85l4cKFRpJZsmSJ+eWXX0xMTIzp3LmzKS4uPuexu3btMiNGjDCXXHKJsdvtpmHDhqZfv34mNzfXqV5p29avX2/Gjh1roqKiTJ06dUzfvn3Nvn37nOqWlJSYqVOnmqZNm5ratWuba665xmzZssWlHiFjjHn66aeNJLN69WpjjDEzZswwkszHH398zmONOf2Zuv76601kZKSpU6eOadeunZk5c6ZTnczMTNOlSxdTp04dExERYW688UazdevWcs+Vk5NjevXqZerXr2/q1q1revToYbKysip8b9atW2dGjBhhGjVqZCIjIx2/X7BggWnZsqWx2+2mc+fO5rPPPiv3uSv9TJ35tzFkyBBTt25d89NPP5mbbrrJ1K1b10RFRZmHHnqoXM/RM888Y1JSUkzDhg2N3W43HTt2NG+88YZTnYo+W2dej59++skMGzbMNG7c2ISFhZn4+Hjz8ssvl3tPXnzxRRMfH+/4f0ZiYqL561//Wun1QOAiCMEvXXfddaZ+/frm66+/Pmfdxx9/3EgyV155pXnmmWfMCy+8YAYOHGjGjRvnqNO3b19z2223mWeeecbMmzfP3HrrrUaSefjhhx11Vq9ebRISEkxUVJRZunSpWbp0qXnnnXdMXl6emTJlipFkhg8f7vjdjh07jDGnv4zCwsJMSkqKee6558zzzz9v2rdvb8LCwsy///1vx/OXBqH4+Hhz0003mblz55o5c+ZUel6lX0zt2rUzXbt2NS+++KIZOXKkCQ4ONldffbUpKSlx1C37hXTs2DFz2WWXmdDQUDN27Fjz4osvmq5duxpJji/TX3/91cybN89IMjfffLPjvL788stK25SXl2eio6NN/fr1zcSJE82MGTNMhw4dTHBwsHn77beNMaeD0tKlS40k07NnT8fznktJSYm56qqrTFRUlBkwYIAJCQkxOTk55zzOGGPeeOMN06FDBzNp0iSzcOFC88gjj5gGDRqYZs2amaNHj5Z7T6+44grTo0cPM2vWLPPQQw+ZkJAQc9tttzk956OPPmokmRtuuMHMnj3b3HXXXaZJkyYmKirKpSB08uRJ06FDB9OqVSvz/fffm3r16pnbb7/dpfNZvXq1CQsLM82aNTMZGRlm3rx55oEHHjCpqamOOmvWrDG1atUyl1xyiXn66afN5MmTTVRUlGnQoIFTANyyZYupW7euiY2NNVOnTjXTp083LVq0MDabzfzrX/8q997Ex8ebbt26mVmzZpnp06cbY4x56aWXHH9jL774ohkzZoyJjIw0LVu2dCkI2e12c/nll5u77rrLzJs3z/zhD38wkszcuXOdzvvCCy80999/v5k9e7aZMWOGSUpKMpKcgvbSpUuNzWYzXbt2dXy2vvjiC2PM6c/nhRdeaOLi4syUKVPMvHnzzI033mgkmeeff97xHKWhu1+/fmbBggXmhRdeMHfffbd54IEHXLo+CCwEIfil1atXm5CQEBMSEmJSUlLMn//8Z7Nq1SpTVFTkVO/77783wcHB5uabby7Xc3BmUDh27Fi517j33ntNnTp1zPHjxx1l7s4RKikpMa1btzZpaWnlXq9FixamZ8+ejrLSIDRgwACX3oPSL6bExESn8y7taXjvvfccZWWD0MyZM40k89prrznKioqKTEpKiqlXr54pLCw0xrg/R2jMmDFGkvnnP//pKDty5Ihp0aKFad68udM1kGRGjhzp0vOW2rJliwkNDTWSzJgxY1w+rqLrm5WVZSSZ//u//3OUlb6nqampTtdr7NixJiQkxBw+fNgYY8y+fftMWFiY6d27t1O9Rx55pFwPxNmUzglq2LBhhXOGKnLq1CnTokUL06xZM/PLL784/e7MtiQkJJjGjRubgwcPOsq+/PJLExwcbAYPHuwo69u3rwkLC3MEd2OM+e9//2vq169vrr76akdZ6XvTpUsXp56aoqIi07hxY5OQkGBOnDjhKC8NE64EIUlmypQpTudyxRVXmMTERKeystexqKjItG3b1vTo0cOpvLI5QnfffbeJjY01Bw4ccCq//fbbTUREhOP5b7rpJnP55ZeXOx7WxF1j8Es9e/ZUVlaWbrzxRn355Zd6+umnlZaWpqZNm+r999931Hv33XdVUlKiSZMmKTjY+eMcFBTk+O/atWs7/vvIkSM6cOCAunbtqmPHjmnbtm1VbufmzZv1/fffa+DAgTp48KAOHDigAwcO6OjRo7r22mv12WefqaSkxOmY++67z63XGD58uEJDQx2PR4wYoVq1amnFihWVHrNixQrFxMRowIABjrLQ0FA98MAD+vXXX/WPf/zDrTac+bxJSUnq0qWLo6xevXoaPny4du3apa1bt1bpeUuFh4crLCxMknTddde5fNyZ1/fkyZM6ePCgLr74YkVGRionJ6dc/eHDhzt9Prp27ari4mL9+OOPkqS1a9eqqKhIo0ePdqo3ZswYt84nKSlJ9913nw4dOqRp06YpOjr6nMds2rRJubm5GjNmTLk5VqVt2bt3rzZv3qyhQ4eqYcOGjt+3b99ePXv2dHw2iouLtXr1avXt21ctW7Z01IuNjdXAgQO1fv16FRYWOr3GPffco5CQEMfj//znP9q3b5/uu+8+x7WRpKFDhyoiIsLl96Ls575r167auXOnU9mZ1/GXX35RQUGBunbtWuE1LMsYo7feekt9+vSRMcbxt3jgwAGlpaWpoKDA8TyRkZH66aeftGHDBpfbj8BFEILf6ty5s95++2398ssvys7O1oQJE3TkyBH169fP8YW7Y8cOBQcHKz4+/qzP9c033+jmm29WRESEwsPD1ahRI915552SpIKCgiq38fvvv5ckDRkyRI0aNXL6eemll3TixIlyz9+iRQu3XqN169ZOj+vVq6fY2Fjt2rWr0mN+/PFHtW7dulw4vOyyyxy/r4off/xRl156abny833eUqNGjVJwcLCaNWumhx56SCdPnnTpuN9++02TJk1SXFycbDaboqKi1KhRIx0+fLjC63vRRRc5PW7QoIGk01++Z55H2fe+UaNGjrqu6ty5sySpU6dOLtXfsWOHJKlt27aV1iltX2XXojSM79+/X8eOHau0XklJifbs2eNUXvbzWdl7ERoa6hSuzsZut6tRo0ZOZQ0aNHC836U+/PBD/e53v5PdblfDhg3VqFEjzZs3z6W/0f379+vw4cNauHBhub/FYcOGSfrfjRbjxo1TvXr1lJSUpNatW2vkyJH6/PPPXToXBB7uGoPfCwsLU+fOndW5c2ddcsklGjZsmN544w1lZGS4dPzhw4fVrVs3hYeHa8qUKWrVqpXsdrtycnI0bty4cj027ig99plnnlFCQkKFderVq+f0+Mx/9eJ/3n77bb3//vuaOXOmWrdurd69e+uZZ57RI488cs5jR48ercWLF2vMmDFKSUlRRESEgoKCdPvtt1d4fc/s8TiTMea8z6Om88bns7L3+0z//Oc/deONN+rqq6/W3LlzFRsbq9DQUC1evNhx593ZlF7nO++8U0OGDKmwTvv27SWdDoHbt2/Xhx9+qJUrV+qtt97S3LlzNWnSpArXs0JgIwihRin9V/XevXslSa1atVJJSYm2bt1aaRBZt26dDh48qLfffltXX321ozw3N7dc3TOHQVwpb9WqlaTTQzqpqakun4c7vv/+e3Xv3t3x+Ndff9XevXt1ww03VHpMs2bN9NVXX6mkpMSpV6h0GLBZs2aSKj+vsz3v9u3by5WXfV53HTlyRA888IA6duyoUaNGKSQkRH/4wx/0xBNPaMCAAefsRXvzzTc1ZMgQPffcc46y48eP6/Dhw1VqT+l5fP/99069Hvv37y/Xi+FppZ+pLVu2VPqZKm1fZdciKipKdevWld1uV506dSqtFxwcrLi4uLO258z3okePHo7ykydPKjc3Vx06dHDtxM7hrbfekt1u16pVq2Sz2RzlixcvLle3os9to0aNVL9+fRUXF7v0t1i3bl31799f/fv3V1FRkW655RY9+eSTmjBhAksAWAxDY/BLn376aYX/Oi+d+1Da1d+3b18FBwdrypQp5f7lX3p86b9Gz3y+oqIizZ07t9zz161bt8Ju+NK1VMp+sSYmJqpVq1Z69tln9euvv5Y7bv/+/ZWeo6sWLlzoNEQ0b948nTp1Stdff32lx9xwww3Ky8vT8uXLHWWnTp3SrFmzVK9ePXXr1k2SVKdOHUnlz+tsz5udna2srCxH2dGjR7Vw4UI1b978nEOUlXn00Ue1d+9eLViwwHG9XnjhBYWEhGjUqFHnPD4kJKTc52XWrFkqLi6uUntSU1MVGhqqWbNmOT1vdWwT0rFjR7Vo0UIzZ84sd11K2xIbG6uEhAS9+uqrTnW2bNmi1atXO0JySEiIrrvuOr333ntOQ6n5+fl6/fXX1aVLF4WHh5+1PZ06dVKjRo00f/58FRUVOcqXLFlS5aBZkZCQEAUFBTlds127dlW4gnTdunXLvXZpeH7rrbe0ZcuWcsec+bd48OBBp9+FhYUpPj5exhiXh2MROOgRgl8aPXq0jh07pptvvllt2rRRUVGRvvjiCy1fvlzNmzd3jPlffPHFmjhxoqZOnaquXbvqlltukc1m04YNG9SkSRNNmzZNV155pRo0aKAhQ4bogQceUFBQkJYuXVph0EpMTNTy5cuVnp6uzp07q169eurTp49atWqlyMhIzZ8/X/Xr11fdunWVnJysFi1a6KWXXtL111+vyy+/XMOGDVPTpk31888/69NPP1V4eLg++OCD83ovioqKdO211+q2227T9u3bNXfuXHXp0kU33nhjpccMHz5cCxYs0NChQ7Vx40Y1b95cb775pj7//HPNnDlT9evXl3R6GCQ+Pl7Lly/XJZdcooYNG6pt27aVzk8ZP368/va3v+n666/XAw88oIYNG+rVV19Vbm6u3nrrrXJzklyxceNGzZkzRyNHjnSaR9O0aVNNmTJF6enpeuutt/SHP/yh0uf4/e9/r6VLlyoiIkLx8fHKysrS2rVrdcEFF7jdHul078LDDz+sadOm6fe//71uuOEGbdq0SR9//LGioqKq9JyuCg4O1rx589SnTx8lJCRo2LBhio2N1bZt2/TNN99o1apVkk4Px15//fVKSUnR3Xffrd9++02zZs1SRESE04KNTzzxhNasWaMuXbro/vvvV61atbRgwQKdOHFCTz/99DnbExoaqieeeEL33nuvevToof79+ys3N1eLFy92eY6QK3r37q0ZM2aoV69eGjhwoPbt26c5c+bo4osv1ldffeVUNzExUWvXrtWMGTPUpEkTtWjRQsnJyZo+fbo+/fRTJScn65577lF8fLwOHTqknJwcrV27VocOHZJ0eiJ+TEyMrrrqKkVHR+vbb7/V7Nmz1bt3b8ffBizER3erAWf18ccfm7vuusu0adPG1KtXz7HdxujRo01+fn65+q+88oq54oorjM1mMw0aNDDdunUza9ascfz+888/N7/73e9M7dq1TZMmTRy340syn376qaPer7/+agYOHGgiIyMdCyqWeu+990x8fLypVatWuVuEN23aZG655RZzwQUXGJvNZpo1a2Zuu+02k5mZ6ahTevv8/v37XXoPyi6o2KBBA1OvXj1zxx13ON0ybUzlCyoOGzbMREVFmbCwMNOuXbsKtwj54osvTGJiogkLC3NrQcXIyEhjt9tNUlKS0zovpeTC7fOnTp0yHTt2NE2aNDEFBQUV/j4hIcFceOGF5siRI5U+zy+//OI413r16pm0tDSz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6m3lgbwadVmubtxaih41AO4iCAFV4M4t8Gfba8zdvcistnaOu/N8YgL4vQDgHQQhoAp8cQt8ZXNlSofiAnFIzdV5PqO6t9JVFzeq9t4xK/XOAYGKIARUQXXfAu+t1aj9/Yvc1Tvsxva8tNqHFK3WOwcEKoIQUAXVfQu8p4bizlQTvsg9dYedp8/Vir1zQKDirjGgCkq/oKX/fSGX8saEXU8PxdWkLSrO9w47T58rK1sDgYUeIaCKSr+gy/Y0eGPCrieH4mripq/nc4edp8/VG71zqHn8fVgZriMIAefBWzurl+XJobia+kXu7h12knfOlb3iUBOGleE6hsaA8+TpndUrew1PDcVZ6YvcG+fKXnHWVpOGleEaghBQQ3hqNWorfZF741xLe+cqi5ysbB24mB8WmBgaA2oQTwzFWWnTV2+cK3vFWVdNHVbG2dEjBNQw5zsUV913vPmSt86VveKsyUrDylZCjxBgQdV5x5uveetcq2uiPPyHlYaVrSTIGGOpwczCwkJFRESooKBA4eHhvm4O4FNWugXYSucK7yguMeryl0/OOdS6flwPPlte4K3vb3qEAAuryi3pNZWVzhXewfywwMQcIQAAXMT8sMBDjxAAAG5gflhgIQgBAOAmhloDB0NjAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsghCAADAsnwehObMmaPmzZvLbrcrOTlZ2dnZZ61/+PBhjRw5UrGxsbLZbLrkkku0YsWKamotAAAIJLV8+eLLly9Xenq65s+fr+TkZM2cOVNpaWnavn27GjduXK5+UVGRevbsqcaNG+vNN99U06ZN9eOPPyoyMrL6Gw8AAGq8IGOM8dWLJycnq3Pnzpo9e7YkqaSkRHFxcRo9erTGjx9frv78+fP1zDPPaNu2bQoNDa3SaxYWFioiIkIFBQUKDw8/r/YDAIDq4a3vb58NjRUVFWnjxo1KTU39X2OCg5WamqqsrKwKj3n//feVkpKikSNHKjo6Wm3bttVTTz2l4uLiSl/nxIkTKiwsdPoBAACQfBiEDhw4oOLiYkVHRzuVR0dHKy8vr8Jjdu7cqTfffFPFxcVasWKFHnvsMT333HN64oknKn2dadOmKSIiwvETFxfn0fMAAAA1l88nS7ujpKREjRs31sKFC5WYmKj+/ftr4sSJmj9/fqXHTJgwQQUFBY6fPXv2VGOLAQCAP/PZZOmoqCiFhIQoPz/fqTw/P18xMTEVHhMbG6vQ0FCFhIQ4yi677DLl5eWpqKhIYWFh5Y6x2Wyy2WyebTwAAAgIPusRCgsLU2JiojIzMx1lJSUlyszMVEpKSoXHXHXVVfrhhx9UUlLiKPvuu+8UGxtbYQgCAAA4G58OjaWnp2vRokV69dVX9e2332rEiBE6evSohg0bJkkaPHiwJkyY4Kg/YsQIHTp0SA8++KC+++47ffTRR3rqqac0cuRIX50CAACowXy6jlD//v21f/9+TZo0SXl5eUpISNDKlSsdE6h3796t4OD/ZbW4uDitWrVKY8eOVfv27dW0aVM9+OCDGjdunK9OAQAA1GA+XUfIF1hHCACAmifg1hECAADwNYIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLIIQAACwLJeD0H//+19vtgMAAKDauRyELr/8cr3++uvebAsAAEC1cjkIPfnkk7r33nt166236tChQ95sEwAAQLVwOQjdf//9+uqrr3Tw4EHFx8frgw8+8Ga7AAAAvK6WO5VbtGihTz75RLNnz9Ytt9yiyy67TLVqOT9FTk6ORxsIwH8Ulxhl5x7SviPH1bi+XUktGiokOMjXzQKAKnMrCEnSjz/+qLffflsNGjTQTTfdVC4IAQhMK7fs1eQPtmpvwXFHWWyEXRl94tWrbawPWwYAVedWilm0aJEeeughpaam6ptvvlGjRo281S4AfmTllr0a8VqOTJnyvILjGvFajubd2ZEwBKBGcjkI9erVS9nZ2Zo9e7YGDx7szTYB8CPFJUaTP9haLgRJkpEUJGnyB1vVMz6GYTIANY7LQai4uFhfffWVLrzwQm+2B4Cfyc495DQcVpaRtLfguLJzDyml1QXV1zA3Mb8JQEVcDkJr1qzxZjsA+Kl9RyoPQVWp5wuBOr+JcAecP2Y6AzirxvXtHq1X3QJ1flOghjugurHXGICzSmrRULERdlXWzxCk01/ASS0aVmezXHKu+U3S6flNxSUV1fBfpeGu7JBlabhbuWWvj1oG1DwEIQBnFRIcpIw+8ZJULgyVPs7oE++XQzLuzG+qKQI13AG+QhACcE692sZq3p0dFRPhPPwVE2H366GlQJjfVFYghjvAl5gjBMAlvdrGqmd8TI2anFvT5zdVJBDDHeBLBCEALgsJDvLrW+TLKp3flFdwvMKhpCCd7tXyx/lNlQnEcAf4EkNjAAJWTZ7fVJmaPHkd8EcEIQABrabOb6pMIIY7wJeCjDGWurWgsLBQERERKigoUHh4uK+bA8AFnlg4MNAWH2QdIViNt76/CUIA/Bpf+JULtHAHnA1ByEMIQkDNUdmq0KVf9dUxtEXYAPyDt76/uWsMgF/yh13v6Y0CAh+TpQH4JV8vHMg2FoA1EIQA+CVfLhzINhaAdRCEAPglXy4c6OveKADVhyAEwC/5cuFAtrEArIMgBMAv+XLhQLaxAKyDIATAb/lqVWi2sQCsg9vnAfg1X+x6X9obNeK1HAVJTpOm2cYCCCwsqAgAlWAdIcB/sKAiAFQzX/RGAaheBCEAOIuQ4CCltLrA180A4CVMlgYAAJZFEAIAAJZFEAIAAJZFEAIAAJZFEAIAAJZFEAIAAJblF0Fozpw5at68uex2u5KTk5Wdne3SccuWLVNQUJD69u3r3QYCAICA5PMgtHz5cqWnpysjI0M5OTnq0KGD0tLStG/fvrMet2vXLj388MPq2rVrNbUUAAAEGp8HoRkzZuiee+7RsGHDFB8fr/nz56tOnTp65ZVXKj2muLhYd9xxhyZPnqyWLVtWY2sBAEAg8WkQKioq0saNG5WamuooCw4OVmpqqrKysio9bsqUKWrcuLHuvvvuc77GiRMnVFhY6PQDAAAg+TgIHThwQMXFxYqOjnYqj46OVl5eXoXHrF+/Xi+//LIWLVrk0mtMmzZNERERjp+4uLjzbjcAAAgMPh8ac8eRI0c0aNAgLVq0SFFRUS4dM2HCBBUUFDh+9uzZ4+VWAgCAmsKnm65GRUUpJCRE+fn5TuX5+fmKiYkpV3/Hjh3atWuX+vTp4ygrKSmRJNWqVUvbt29Xq1atnI6x2Wyy2WxeaD0AAKjpfNojFBYWpsTERGVmZjrKSkpKlJmZqZSUlHL127Rpo6+//lqbN292/Nx4443q3r27Nm/ezLAXAABwi097hCQpPT1dQ4YMUadOnZSUlKSZM2fq6NGjGjZsmCRp8ODBatq0qaZNmya73a62bds6HR8ZGSlJ5coBAADOxedBqH///tq/f78mTZqkvLw8JSQkaOXKlY4J1Lt371ZwcI2aygQAAGqIIGOM8XUjqlNhYaEiIiJUUFCg8PBwXzcHAAC4wFvf33S1AAAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAyyIIAQAAy/KLIDRnzhw1b95cdrtdycnJys7OrrTuokWL1LVrVzVo0EANGjRQamrqWesDAABUxudBaPny5UpPT1dGRoZycnLUoUMHpaWlad++fRXWX7dunQYMGKBPP/1UWVlZiouL03XXXaeff/65mlsOAABquiBjjPFlA5KTk9W5c2fNnj1bklRSUqK4uDiNHj1a48ePP+fxxcXFatCggWbPnq3Bgwefs35hYaEiIiJUUFCg8PDw824/AADwPm99f/u0R6ioqEgbN25Uamqqoyw4OFipqanKyspy6TmOHTumkydPqmHDhhX+/sSJEyosLHT6AQAAkHwchA4cOKDi4mJFR0c7lUdHRysvL8+l5xg3bpyaNGniFKbONG3aNEVERDh+4uLizrvdAAAgMPh8jtD5mD59upYtW6Z33nlHdru9wjoTJkxQQUGB42fPnj3V3EoAAOCvavnyxaOiohQSEqL8/Hyn8vz8fMXExJz12GeffVbTp0/X2rVr1b59+0rr2Ww22Ww2j7QXAAAEFp/2CIWFhSkxMVGZmZmOspKSEmVmZiolJaXS455++mlNnTpVK1euVKdOnaqjqQAAIAD5tEdIktLT0zVkyBB16tRJSUlJmjlzpo4ePaphw4ZJkgYPHqymTZtq2rRpkqS//OUvmjRpkl5//XU1b97cMZeoXr16qlevns/OAwAA1Dw+D0L9+/fX/v37NWnSJOXl5SkhIUErV650TKDevXu3goP/13E1b948FRUVqV+/fk7Pk5GRoccff7w6mw4AAGo4n68jVN1YRwgAgJonINcRAgAA8CWCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCyCEAAAsCy/CEJz5sxR8+bNZbfblZycrOzs7LPWf+ONN9SmTRvZ7Xa1a9dOK1ascPs122as0vqt+6vaZAAAEAB8HoSWL1+u9PR0ZWRkKCcnRx06dFBaWpr27dtXYf0vvvhCAwYM0N13361Nmzapb9++6tu3r7Zs2eL2a9/5f9lqPv6j8z0FAABQQwUZY4wvG5CcnKzOnTtr9uzZkqSSkhLFxcVp9OjRGj9+fLn6/fv319GjR/Xhhx86yn73u98pISFB8+fPP+frFRYWKiIiQnFj/q5gWx1H+a7pvT1wNgAAwBtKv78LCgoUHh7usef1aY9QUVGRNm7cqNTUVEdZcHCwUlNTlZWVVeExWVlZTvUlKS0trdL6rmKYDAAA66nlyxc/cOCAiouLFR0d7VQeHR2tbdu2VXhMXl5ehfXz8vIqrH/ixAmdOHHC8bigoECSVHLimFO9gYvWacvkNLfPAQAAeF9hYaEkydMDWT4NQtVh2rRpmjx5crnyn+cNLVcWMdP77QEAAFV38OBBRUREeOz5fBqEoqKiFBISovz8fKfy/Px8xcTEVHhMTEyMW/UnTJig9PR0x+PDhw+rWbNm2r17t0ffSLivsLBQcXFx2rNnj0fHe1E1XA//wbXwH1wL/1FQUKCLLrpIDRs29Ojz+jQIhYWFKTExUZmZmerbt6+k05OlMzMzNWrUqAqPSUlJUWZmpsaMGeMoW7NmjVJSUiqsb7PZZLPZypVHRETwofYT4eHhXAs/wvXwH1wL/8G18B/BwZ6d3uzzobH09HQNGTJEnTp1UlJSkmbOnKmjR49q2LBhkqTBgweradOmmjZtmiTpwQcfVLdu3fTcc8+pd+/eWrZsmf7zn/9o4cKFvjwNAABQA/k8CPXv31/79+/XpEmTlJeXp4SEBK1cudIxIXr37t1O6e/KK6/U66+/rkcffVSPPPKIWrdurXfffVdt27b11SkAAIAayudBSJJGjRpV6VDYunXrypXdeuutuvXWW6v0WjabTRkZGRUOl6F6cS38C9fDf3At/AfXwn9461r4fEFFAAAAX/H5FhsAAAC+QhACAACWRRACAACWRRACAACWFZBBaM6cOWrevLnsdruSk5OVnZ191vpvvPGG2rRpI7vdrnbt2mnFihXV1NLA5861WLRokbp27aoGDRqoQYMGSk1NPee1g3vc/dsotWzZMgUFBTkWPsX5c/daHD58WCNHjlRsbKxsNpsuueQS/l/lIe5ei5kzZ+rSSy9V7dq1FRcXp7Fjx+r48ePV1NrA9dlnn6lPnz5q0qSJgoKC9O67757zmHXr1qljx46y2Wy6+OKLtWTJEvdf2ASYZcuWmbCwMPPKK6+Yb775xtxzzz0mMjLS5OfnV1j/888/NyEhIebpp582W7duNY8++qgJDQ01X3/9dTW3PPC4ey0GDhxo5syZYzZt2mS+/fZbM3ToUBMREWF++umnam55YHL3epTKzc01TZs2NV27djU33XRT9TQ2wLl7LU6cOGE6depkbrjhBrN+/XqTm5tr1q1bZzZv3lzNLQ887l6Lv/71r8Zms5m//vWvJjc316xatcrExsaasWPHVnPLA8+KFSvMxIkTzdtvv20kmXfeeees9Xfu3Gnq1Klj0tPTzdatW82sWbNMSEiIWblypVuvG3BBKCkpyYwcOdLxuLi42DRp0sRMmzatwvq33Xab6d27t1NZcnKyuffee73aTitw91qUderUKVO/fn3z6quvequJllKV63Hq1Clz5ZVXmpdeeskMGTKEIOQh7l6LefPmmZYtW5qioqLqaqJluHstRo4caXr06OFUlp6ebq666iqvttNqXAlCf/7zn83ll1/uVNa/f3+Tlpbm1msF1NBYUVGRNm7cqNTUVEdZcHCwUlNTlZWVVeExWVlZTvUlKS0trdL6cE1VrkVZx44d08mTJz2+wZ4VVfV6TJkyRY0bN9bdd99dHc20hKpci/fff18pKSkaOXKkoqOj1bZtWz311FMqLi6urmYHpKpciyuvvFIbN250DJ/t3LlTK1as0A033FAtbcb/eOr72y9WlvaUAwcOqLi42LE9R6no6Ght27atwmPy8vIqrJ+Xl+e1dlpBVa5FWePGjVOTJk3KfdDhvqpcj/Xr1+vll1/W5s2bq6GF1lGVa7Fz50598sknuuOOO7RixQr98MMPuv/++3Xy5EllZGRUR7MDUlWuxcCBA3XgwAF16dJFxhidOnVK9913nx555JHqaDLOUNn3d2FhoX777TfVrl3bpecJqB4hBI7p06dr2bJleuedd2S3233dHMs5cuSIBg0apEWLFikqKsrXzbG8kpISNW7cWAsXLlRiYqL69++viRMnav78+b5umuWsW7dOTz31lObOnaucnBy9/fbb+uijjzR16lRfNw1VFFA9QlFRUQoJCVF+fr5TeX5+vmJiYio8JiYmxq36cE1VrkWpZ599VtOnT9fatWvVvn17bzbTMty9Hjt27NCuXbvUp08fR1lJSYkkqVatWtq+fbtatWrl3UYHqKr8bcTGxio0NFQhISGOsssuu0x5eXkqKipSWFiYV9scqKpyLR577DENGjRIf/zjHyVJ7dq109GjRzV8+HBNnDjRaZNweFdl39/h4eEu9wZJAdYjFBYWpsTERGVmZjrKSkpKlJmZqZSUlAqPSUlJcaovSWvWrKm0PlxTlWshSU8//bSmTp2qlStXqlOnTtXRVEtw93q0adNGX3/9tTZv3uz4ufHGG9W9e3dt3rxZcXFx1dn8gFKVv42rrrpKP/zwgyOMStJ3332n2NhYQtB5qMq1OHbsWLmwUxpQDVt3ViuPfX+7N4/b/y1btszYbDazZMkSs3XrVjN8+HATGRlp8vLyjDHGDBo0yIwfP95R//PPPze1atUyzz77rPn2229NRkYGt897iLvXYvr06SYsLMy8+eabZu/evY6fI0eO+OoUAoq716Ms7hrzHHevxe7du039+vXNqFGjzPbt282HH35oGjdubJ544glfnULAcPdaZGRkmPr165u//e1vZufOnWb16tWmVatW5rbbbvPVKQSMI0eOmE2bNplNmzYZSWbGjBlm06ZN5scffzTGGDN+/HgzaNAgR/3S2+f/9Kc/mW+//dbMmTOH2+dLzZo1y1x00UUmLCzMJCUlmX/961+O33Xr1s0MGTLEqf7f//53c8kll5iwsDBz+eWXm48++qiaWxy43LkWzZo1M5LK/WRkZFR/wwOUu38bZyIIeZa71+KLL74wycnJxmazmZYtW5onn3zSnDp1qppbHZjcuRYnT540jz/+uGnVqpWx2+0mLi7O3H///eaXX36p/oYHmE8//bTC74DS93/IkCGmW7du5Y5JSEgwYWFhpmXLlmbx4sVuv26QMfTlAQAAawqoOUIAAADuIAgBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBAADLIggBqNGKi4t15ZVX6pZbbnEqLygoUFxcnCZOnOijlgGoCVhZGkCN99133ykhIUGLFi3SHXfcIUkaPHiwvvzyS23YsIGNSQFUiiAEICC8+OKLevzxx/XNN98oOztbt956qzZs2KAOHTr4umkA/BhBCEBAMMaoR48eCgkJ0ddff63Ro0fr0Ucf9XWzAPg5ghCAgLFt2zZddtllateunXJyclSrVi1fNwmAn2OyNICA8corr6hOnTrKzc3VTz/95OvmAKgB6BECEBC++OILdevWTatXr9YTTzwhSVq7dq2CgoJ83DIA/oweIQA13rFjxzR06FCNGDFC3bt318svv6zs7GzNnz/f100D4OfoEQJQ4z344INasWKFvvzyS9WpU0eStGDBAj388MP6+uuv1bx5c982EIDfIggBqNH+8Y9/6Nprr9W6devUpUsXp9+lpaXp1KlTDJEBqBRBCAAAWBZzhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGURhAAAgGX9P8sDe9jobEdbAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "plot('data/processed/spoter_train.csv')" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[18 19 20 21 22 23 24 25 26 27 28 29 30]\n", + " frame row_id type landmark_index x y z\n", + "3801 25 25-face-0 face 0 0.498506 0.494856 -0.018860\n", + "3802 25 25-face-1 face 1 0.506121 0.470006 -0.056474\n", + "3803 25 25-face-2 face 2 0.505620 0.475341 -0.025482\n", + "3804 25 25-face-3 face 3 0.497120 0.439673 -0.049148\n", + "3805 25 25-face-4 face 4 0.506552 0.461902 -0.062083\n" + ] + }, + { + "data": { + "application/vnd.plotly.v1+json": { + "config": { + "plotlyServerURL": "https://plot.ly" + }, + "data": [ + { + "customdata": [ + [ + "wrist_left" + ], + [ + "thumbCMC_left" + ], + [ + "thumbMP_left" + ], + [ + "thumbIP_left" + ], + [ + "thumbTip_left" + ], + [ + "indexMCP_left" + ], + [ + "indexPIP_left" + ], + [ + "indexDIP_left" + ], + [ + "indexTip_left" + ], + [ + "middleMCP_left" + ], + [ + "middlePIP_left" + ], + [ + "middleDIP_left" + ], + [ + "middleTip_left" + ], + [ + "ringMCP_left" + ], + [ + "ringPIP_left" + ], + [ + "ringDIP_left" + ], + [ + "ringTip_left" + ], + [ + "littleMCP_left" + ], + [ + "littlePIP_left" + ], + [ + "littleDIP_left" + ], + [ + "littleTip_left" + ] + ], + "hovertemplate": "type=left_hand
x=%{x}
y=%{y}
name=%{customdata[0]}", + "legendgroup": "left_hand", + "marker": { + "color": "#636efa", + "symbol": "circle" + }, + "mode": "markers", + "name": "left_hand", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "xaxis": "x", + "y": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "nose" + ], + [ + "leftEye" + ], + [ + "rightEye" + ], + [ + "leftEar" + ], + [ + "rightEar" + ], + [ + "leftShoulder" + ], + [ + "rightShoulder" + ], + [ + "leftElbow" + ], + [ + "rightElbow" + ], + [ + "leftWrist" + ], + [ + "rightWrist" + ] + ], + "hovertemplate": "type=pose
x=%{x}
y=%{y}
name=%{customdata[0]}", + "legendgroup": "pose", + "marker": { + "color": "#EF553B", + "symbol": "circle" + }, + "mode": "markers", + "name": "pose", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + 0.5037816762924194, + 0.544114351272583, + 0.47258099913597107, + 0.6112894415855408, + 0.42202046513557434, + 0.790356457233429, + 0.30560430884361267, + 0.8586887121200562, + 0.052559275180101395, + 0.8901190161705017, + 0.14568309485912323 + ], + "xaxis": "x", + "y": [ + 0.5385957062244415, + 0.5870676934719086, + 0.5835375487804413, + 0.5667203366756439, + 0.5620827376842499, + 0.3601607084274292, + 0.3561617136001587, + 0.10383915901184082, + 0.12815016508102417, + -0.1593785285949707, + 0.4648846983909607 + ], + "yaxis": "y" + }, + { + "customdata": [ + [ + "wrist_right" + ], + [ + "thumbCMC_right" + ], + [ + "thumbMP_right" + ], + [ + "thumbIP_right" + ], + [ + "thumbTip_right" + ], + [ + "indexMCP_right" + ], + [ + "indexPIP_right" + ], + [ + "indexDIP_right" + ], + [ + "indexTip_right" + ], + [ + "middleMCP_right" + ], + [ + "middlePIP_right" + ], + [ + "middleDIP_right" + ], + [ + "middleTip_right" + ], + [ + "ringMCP_right" + ], + [ + "ringPIP_right" + ], + [ + "ringDIP_right" + ], + [ + "ringTip_right" + ], + [ + "littleMCP_right" + ], + [ + "littlePIP_right" + ], + [ + "littleDIP_right" + ], + [ + "littleTip_right" + ] + ], + "hovertemplate": "type=right_hand
x=%{x}
y=%{y}
name=%{customdata[0]}", + "legendgroup": "right_hand", + "marker": { + "color": "#00cc96", + "symbol": "circle" + }, + "mode": "markers", + "name": "right_hand", + "orientation": "v", + "showlegend": true, + "type": "scatter", + "x": [ + 0.1788196861743927, + 0.23843127489089966, + 0.25467628240585327, + 0.27044928073883057, + 0.2871263027191162, + 0.19056828320026398, + 0.2649158835411072, + 0.3157113492488861, + 0.3494824171066284, + 0.18622460961341858, + 0.2733367681503296, + 0.32277774810791016, + 0.3569425642490387, + 0.19574135541915894, + 0.2806362509727478, + 0.3286181688308716, + 0.36066585779190063, + 0.21657982468605042, + 0.2816353738307953, + 0.31945061683654785, + 0.34714818000793457 + ], + "xaxis": "x", + "y": [ + 0.4631972312927246, + 0.49802637100219727, + 0.5355206429958344, + 0.5633883774280548, + 0.5824070870876312, + 0.5867454409599304, + 0.6103077828884125, + 0.6078206300735474, + 0.6010101139545441, + 0.5849654078483582, + 0.6130928993225098, + 0.6087997257709503, + 0.6008164584636688, + 0.5775772035121918, + 0.6042222082614899, + 0.6019154489040375, + 0.5936585664749146, + 0.564491868019104, + 0.5853257775306702, + 0.5884504020214081, + 0.5863424837589264 + ], + "yaxis": "y" + } + ], + "layout": { + "legend": { + "title": { + "text": "type" + }, + "tracegroupgap": 0 + }, + "margin": { + "t": 60 + }, + "template": { + "data": { + "bar": [ + { + "error_x": { + "color": "#2a3f5f" + }, + "error_y": { + "color": "#2a3f5f" + }, + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "bar" + } + ], + "barpolar": [ + { + "marker": { + "line": { + "color": "#E5ECF6", + "width": 0.5 + }, + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "barpolar" + } + ], + "carpet": [ + { + "aaxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "baxis": { + "endlinecolor": "#2a3f5f", + "gridcolor": "white", + "linecolor": "white", + "minorgridcolor": "white", + "startlinecolor": "#2a3f5f" + }, + "type": "carpet" + } + ], + "choropleth": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "choropleth" + } + ], + "contour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "contour" + } + ], + "contourcarpet": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "contourcarpet" + } + ], + "heatmap": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmap" + } + ], + "heatmapgl": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "heatmapgl" + } + ], + "histogram": [ + { + "marker": { + "pattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + } + }, + "type": "histogram" + } + ], + "histogram2d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2d" + } + ], + "histogram2dcontour": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "histogram2dcontour" + } + ], + "mesh3d": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "type": "mesh3d" + } + ], + "parcoords": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "parcoords" + } + ], + "pie": [ + { + "automargin": true, + "type": "pie" + } + ], + "scatter": [ + { + "fillpattern": { + "fillmode": "overlay", + "size": 10, + "solidity": 0.2 + }, + "type": "scatter" + } + ], + "scatter3d": [ + { + "line": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatter3d" + } + ], + "scattercarpet": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattercarpet" + } + ], + "scattergeo": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergeo" + } + ], + "scattergl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattergl" + } + ], + "scattermapbox": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scattermapbox" + } + ], + "scatterpolar": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolar" + } + ], + "scatterpolargl": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterpolargl" + } + ], + "scatterternary": [ + { + "marker": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "type": "scatterternary" + } + ], + "surface": [ + { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + }, + "colorscale": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "type": "surface" + } + ], + "table": [ + { + "cells": { + "fill": { + "color": "#EBF0F8" + }, + "line": { + "color": "white" + } + }, + "header": { + "fill": { + "color": "#C8D4E3" + }, + "line": { + "color": "white" + } + }, + "type": "table" + } + ] + }, + "layout": { + "annotationdefaults": { + "arrowcolor": "#2a3f5f", + "arrowhead": 0, + "arrowwidth": 1 + }, + "autotypenumbers": "strict", + "coloraxis": { + "colorbar": { + "outlinewidth": 0, + "ticks": "" + } + }, + "colorscale": { + "diverging": [ + [ + 0, + "#8e0152" + ], + [ + 0.1, + "#c51b7d" + ], + [ + 0.2, + "#de77ae" + ], + [ + 0.3, + "#f1b6da" + ], + [ + 0.4, + "#fde0ef" + ], + [ + 0.5, + "#f7f7f7" + ], + [ + 0.6, + "#e6f5d0" + ], + [ + 0.7, + "#b8e186" + ], + [ + 0.8, + "#7fbc41" + ], + [ + 0.9, + "#4d9221" + ], + [ + 1, + "#276419" + ] + ], + "sequential": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ], + "sequentialminus": [ + [ + 0, + "#0d0887" + ], + [ + 0.1111111111111111, + "#46039f" + ], + [ + 0.2222222222222222, + "#7201a8" + ], + [ + 0.3333333333333333, + "#9c179e" + ], + [ + 0.4444444444444444, + "#bd3786" + ], + [ + 0.5555555555555556, + "#d8576b" + ], + [ + 0.6666666666666666, + "#ed7953" + ], + [ + 0.7777777777777778, + "#fb9f3a" + ], + [ + 0.8888888888888888, + "#fdca26" + ], + [ + 1, + "#f0f921" + ] + ] + }, + "colorway": [ + "#636efa", + "#EF553B", + "#00cc96", + "#ab63fa", + "#FFA15A", + "#19d3f3", + "#FF6692", + "#B6E880", + "#FF97FF", + "#FECB52" + ], + "font": { + "color": "#2a3f5f" + }, + "geo": { + "bgcolor": "white", + "lakecolor": "white", + "landcolor": "#E5ECF6", + "showlakes": true, + "showland": true, + "subunitcolor": "white" + }, + "hoverlabel": { + "align": "left" + }, + "hovermode": "closest", + "mapbox": { + "style": "light" + }, + "paper_bgcolor": "white", + "plot_bgcolor": "#E5ECF6", + "polar": { + "angularaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "radialaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "scene": { + "xaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "yaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + }, + "zaxis": { + "backgroundcolor": "#E5ECF6", + "gridcolor": "white", + "gridwidth": 2, + "linecolor": "white", + "showbackground": true, + "ticks": "", + "zerolinecolor": "white" + } + }, + "shapedefaults": { + "line": { + "color": "#2a3f5f" + } + }, + "ternary": { + "aaxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "baxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + }, + "bgcolor": "#E5ECF6", + "caxis": { + "gridcolor": "white", + "linecolor": "white", + "ticks": "" + } + }, + "title": { + "x": 0.05 + }, + "xaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + }, + "yaxis": { + "automargin": true, + "gridcolor": "white", + "linecolor": "white", + "ticks": "", + "title": { + "standoff": 15 + }, + "zerolinecolor": "white", + "zerolinewidth": 2 + } + } + }, + "xaxis": { + "anchor": "y", + "constrain": "domain", + "domain": [ + 0, + 1 + ], + "range": [ + 0, + 1 + ], + "title": { + "text": "x" + } + }, + "yaxis": { + "anchor": "x", + "domain": [ + 0, + 1 + ], + "range": [ + 0, + 1 + ], + "scaleanchor": "x", + "scaleratio": 1, + "title": { + "text": "y" + } + } + } + }, + "text/html": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# read parquet file at data/train_landmark_files/2044/635217.parquet\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n", + "df = pd.read_parquet('data/train_landmark_files/37779/254554888.parquet')\n", + "\n", + "# print all unique frame numbers\n", + "print(df['frame'].unique())\n", + "\n", + "# filter where type is pose\n", + "df = df[df['frame'] == 25]\n", + "\n", + "print(df.head())\n", + "\n", + "mapping = {\n", + " 'pose_0': 'nose',\n", + " 'pose_1': 'leftEye',\n", + " 'pose_4': 'rightEye',\n", + " 'pose_7': 'leftEar',\n", + " 'pose_8': 'rightEar',\n", + " 'pose_11': 'leftShoulder',\n", + " 'pose_12': 'rightShoulder',\n", + " 'pose_13': 'leftElbow',\n", + " 'pose_14': 'rightElbow',\n", + " 'pose_15': 'leftWrist',\n", + " 'pose_16': 'rightWrist',\n", + "\n", + " 'left_hand_0': 'wrist_left',\n", + " 'left_hand_1': 'thumbCMC_left',\n", + " 'left_hand_2': 'thumbMP_left',\n", + " 'left_hand_3': 'thumbIP_left',\n", + " 'left_hand_4': 'thumbTip_left',\n", + " 'left_hand_5': 'indexMCP_left',\n", + " 'left_hand_6': 'indexPIP_left',\n", + " 'left_hand_7': 'indexDIP_left',\n", + " 'left_hand_8': 'indexTip_left',\n", + " 'left_hand_9': 'middleMCP_left',\n", + " 'left_hand_10': 'middlePIP_left',\n", + " 'left_hand_11': 'middleDIP_left',\n", + " 'left_hand_12': 'middleTip_left',\n", + " 'left_hand_13': 'ringMCP_left',\n", + " 'left_hand_14': 'ringPIP_left',\n", + " 'left_hand_15': 'ringDIP_left',\n", + " 'left_hand_16': 'ringTip_left',\n", + " 'left_hand_17': 'littleMCP_left',\n", + " 'left_hand_18': 'littlePIP_left',\n", + " 'left_hand_19': 'littleDIP_left',\n", + " 'left_hand_20': 'littleTip_left',\n", + "\n", + " 'right_hand_0': 'wrist_right',\n", + " 'right_hand_1': 'thumbCMC_right',\n", + " 'right_hand_2': 'thumbMP_right',\n", + " 'right_hand_3': 'thumbIP_right',\n", + " 'right_hand_4': 'thumbTip_right',\n", + " 'right_hand_5': 'indexMCP_right',\n", + " 'right_hand_6': 'indexPIP_right',\n", + " 'right_hand_7': 'indexDIP_right',\n", + " 'right_hand_8': 'indexTip_right',\n", + " 'right_hand_9': 'middleMCP_right',\n", + " 'right_hand_10': 'middlePIP_right',\n", + " 'right_hand_11': 'middleDIP_right',\n", + " 'right_hand_12': 'middleTip_right',\n", + " 'right_hand_13': 'ringMCP_right',\n", + " 'right_hand_14': 'ringPIP_right',\n", + " 'right_hand_15': 'ringDIP_right',\n", + " 'right_hand_16': 'ringTip_right',\n", + " 'right_hand_17': 'littleMCP_right',\n", + " 'right_hand_18': 'littlePIP_right',\n", + " 'right_hand_19': 'littleDIP_right',\n", + " 'right_hand_20': 'littleTip_right',\n", + " }\n", + "\n", + "# scatter plot and when hovering over the point, show the frame number\n", + "import plotly.express as px\n", + "\n", + "# combine type and landmark index\n", + "df['landmark_id'] = df['type'] + '_' + df['landmark_index'].astype(str)\n", + "# only keep rows where landmark_id is in the mapping\n", + "df = df[df['landmark_id'].isin(mapping.keys())]\n", + "df['name'] = df['landmark_id'].apply(lambda x: mapping[x])\n", + "\n", + "# flip vertically\n", + "df['y'] = 1 - df['y']\n", + "\n", + "# keep aspect ratio\n", + "\n", + "# scatter with px and set color of points based on the type\n", + "fig = px.scatter(df, x='x', y='y', color='type', hover_data=['name'])\n", + "\n", + "fig.update_xaxes(range=[0, 1], constrain='domain')\n", + "fig.update_yaxes(scaleanchor='x', scaleratio=1, range=[0, 1])\n", + "\n", + "# show the plot\n", + "fig.show()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# from training dataset\n", + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "\n" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/webcam.py b/webcam.py index d381c71..f832fc5 100644 --- a/webcam.py +++ b/webcam.py @@ -238,6 +238,7 @@ def distance_matrix(keypoints, embeddings, p=2, threshold=1000000): f"{kk}-dimensional vectors") if m*n*k <= threshold: + print("Using minkowski_distance") return minkowski_distance(x[:,np.newaxis,:],y[np.newaxis,:,:],p) else: result = np.empty((m,n),dtype=float) # FIXME: figure out the best dtype