Compare commits

..

No commits in common. "0965cd5d854651c37c8fd3a82fe98a216f95801b" and "ebee258bb3a4e0d4bcf0220e8b753b1589c7ed10" have entirely different histories.

14 changed files with 479 additions and 409 deletions

View File

@ -18,6 +18,4 @@ services:
- ${PWD}/config/local.json:/website/config/local.json - ${PWD}/config/local.json:/website/config/local.json
- /opt/ketrface/models:/root/.deepface - /opt/ketrface/models:/root/.deepface
# - ${PWD}:/website # - ${PWD}:/website
- ${PWD}/ketrface:/website/ketrface
- ${PWD}/frontend:/website/frontend
- ${PWD}/server:/website/server - ${PWD}/server:/website/server

199
frontend/clusters.html Normal file
View File

@ -0,0 +1,199 @@
<html>
<script>'<base href="BASEPATH">';</script>
<script>
function loadMore(index) {
var clusterBlock = document.body.querySelector("[cluster-index='" + index + "']");
if (!clusterBlock) {
return;
}
var faces = clusterBlock.querySelectorAll("div.face").length, i
for (i = faces; i < clusters[index].length; i++) {
if (i - faces > 10) {
return;
}
var tuple = clusters[index][i],
face = createFace(tuple[0], tuple[1]);
clusterBlock.appendChild(face);
}
if (i == clusters[index].length) {
var span = clusterBlock.querySelector("span.more");
if (span) {
span.parentElement.removeChild(span);
}
}
}
function createFace(faceId, photoId) {
var div = document.createElement("div");
div.classList.add("face");
div.setAttribute("photo-id", photoId);
div.style.backgroundImage = "url(face-data/" + (faceId % 100) + "/" + faceId + "-original.png)";
div.addEventListener("click", (event) => {
let photoId = parseInt(event.currentTarget.getAttribute("photo-id"));
if (photoId) {
window.open("face-explorer.html?" + photoId, "photo-" + photoId);
} else {
alert("No photo id mapped to face.");
}
});
return div;
}
function shuffle(array) {
var i = array.length, tmp, random;
while (i > 0) {
random = Math.floor(Math.random() * i);
i--;
tmp = array[i];
array[i] = array[random];
array[random] = tmp;
}
return array;
}
document.addEventListener("DOMContentLoaded", (event) => {
var div = document.createElement("div");
div.textContent = "There are " + clusters.length + " clusters.";
document.body.appendChild(div);
clusters.sort((a, b) => { return b.length - a.length });
clusters.forEach((cluster, clusterIndex) => {
var clusterBlock = document.createElement("div");
clusterBlock.setAttribute("cluster-index", clusterIndex);
document.body.appendChild(clusterBlock);
var div = document.createElement("div");
var html = "Cluster " + (clusterIndex + 1) + " has " + cluster.length + " neighbors.";
if (cluster.length > 10) {
html += " <span class='more' onClick='loadMore(" + clusterIndex + ")'>more</a>";
}
div.innerHTML = html;
clusterBlock.appendChild(div);
shuffle(cluster);
cluster.forEach((tuple, index) => {
if (index >= 10) {
return;
}
var face = createFace(tuple[0], tuple[1]);
clusterBlock.appendChild(face);
});
});
});
</script>
<style>
body {
margin: 0;
padding: 0;
}
.more {
cursor: pointer;
}
.more:hover {
text-decoration: underline;
}
.face {
width: 128px;
height: 128px;
background-size: contain;
background-position: center center;
display: inline-block;
border: 1px solid black;
margin: 0.5em;
cursor: pointer;
}
.face:hover {
box-shadow: 0px 2px 5px rgba(0, 0, 0, 0.5);
}
</style>
<script>
var clusters = [
/* 2. */ [[20848,12112],[27584,19854],[33117,20904],[35066,26340],[35068,26341],[35072,26342],[35079,26343],[35084,26344],[35088,26345],[35172,26370],[35469,15441],[35478,15443],[35480,15444],[35485,15445],[35502,15453],[35524,15472],[35531,15473],[35535,15475],[36181,16039],[36536,16221],[36560,16228],[36857,16454],[36881,16469]],
/* 3. */ [[21721,789],[21725,791],[27501,19778],[27591,19857],[27595,19858],[27597,19859],[27601,19860],[27619,19874],[27631,19893],[27922,30634],[27925,30635],[34667,26670]],
/* 4. */ [[22711,19330],[22715,19331],[22737,19338],[22745,19342],[22774,19375],[23392,12715],[24423,12517],[24424,12518],[24425,12519],[24427,12520],[24438,12569],[24767,12974],[38452,18186]],
/* 5. */ [[23314,13313],[23358,13342],[23363,13347],[32528,22278],[36121,16022],[36133,16023],[36255,16083],[36263,16084],[36279,16094],[36284,16095],[36288,16096],[36346,16118],[36354,16119],[36362,16120],[36488,16199],[36541,16223],[36545,16224]],
/* 6. */ [[22699,19323],[24029,12481],[24079,12483],[24134,12486],[24202,12488],[24248,12489],[24311,12491],[24357,12492],[24430,12521],[24469,13495],[24481,13496],[24536,13498],[24549,13499],[24892,13203],[36958,16718]],
/* 7. */ [[20272,11125],[21046,12267],[25203,13937],[25213,13948],[25419,19439],[26424,14771],[26429,14772],[30962,7891],[30996,9402],[33949,25516],[37631,17198]],
/* 8. */ [[24455,12584],[25340,14452],[25362,14463],[25809,14519],[25894,14612],[26133,14884],[26520,14797],[26606,14827],[27585,19854],[27626,19891],[28646,20649],[28647,20650],[30288,6700],[30555,7072],[30574,7075],[30591,7109],[32572,22296],[32972,21727],[33982,25523],[34113,25731],[34118,25733],[34125,25739],[34220,26115],[34258,26217],[34269,26224],[34304,26278],[34721,26502],[34723,26507],[34733,26518],[35033,26632],[35477,15443],[35487,15445],[35492,15447]],
/* 9. */ [[26004,14683],[26051,14844],[26072,14853],[26103,14872],[26526,14799],[26527,14800],[26528,14801],[26530,14802],[26578,14823],[26594,14825],[26614,14828]],
/* 10. */ [[20136,11732],[22467,2021],[27503,19779],[27550,19816],[27583,19852],[35070,26342],[35078,26343],[35094,26346],[35230,26402],[36462,16182],[36464,16183],[36878,16465]],
/* 11. */ [[21164,30337],[24947,13238],[25199,13926],[26392,14762],[30528,6973],[30536,7003],[30537,7009],[30539,7011],[30546,7010],[30562,7073],[30573,7074],[33952,25516],[33984,25333],[33985,25085],[33989,25090],[33998,25141],[34007,25195],[34011,25182],[34013,25183],[34016,25225],[34017,25228],[34019,25254],[34054,25531],[34064,25533],[34073,25539],[34132,25740],[34147,25690],[34165,25705],[34173,25709],[34182,25638],[34198,25798],[34213,26086],[34216,26087],[34232,26129],[34297,26266],[34313,26281]],
/* 12. */ [[26409,14768],[30559,7072],[30569,7074],[30576,7075],[30590,7109],[30593,7110],[30595,7111],[33946,25515],[33947,25516],[33971,25520],[34126,25739],[34136,25684],[34146,25690],[34207,26067],[34223,26116],[34309,26280]],
/* 13. */ [[30570,7074],[30577,7075],[30584,7094],[33959,25517],[33966,25519],[33973,25520],[34109,25730],[34116,25731],[34127,25739],[34177,25598],[34179,25600],[34183,25638],[34189,25667],[34210,26083],[34221,26115],[34227,26123],[34228,26125],[34243,26161],[34259,26217],[34268,26224],[34303,26278],[34308,26280]],
/* 14. */ [[22396,1981],[31501,10819],[34122,25738],[34544,26307],[34717,26500],[34728,26514]],
/* 15. */ [[31561,10524],[31574,10531],[31576,10532],[31593,10546],[31600,10551],[31603,10552],[31604,10553],[31607,10556],[31608,10557],[31610,10559],[31620,10567],[31621,10568],[31663,10590],[31664,10592],[31666,10596],[31713,10939],[31714,10940],[31906,11053],[31930,11083]],
/* 16. */ [[31567,10527],[31583,10537],[31594,10547],[31596,10548],[31606,10556],[31613,10562],[31616,10565],[31618,10566],[31631,10572],[31634,10575],[31670,10604],[31715,10941]],
/* 17. */ [[20134,11728],[27019,19612],[34589,26327],[35062,26338],[35074,26342],[35083,26344],[35090,26345],[35231,26402],[36037,16437],[36204,16058],[36459,16182],[36466,16183],[36849,16447]],
/* 18. */ [[31472,10059],[34682,26491],[34762,26538],[34768,26540],[34776,26545],[34783,26547],[34790,26549],[34794,26551],[34795,26552],[34821,26568],[34827,26572],[34832,26573],[34837,26574],[34845,26575],[34864,26582],[34869,26583],[34873,26585],[34882,26587],[34894,26589],[34895,26590],[34900,26591],[35041,26634]],
/* 19. */ [[34686,26491],[34697,26493],[34746,26527],[34766,26540],[34772,26544],[34792,26549],[34796,26552],[34833,26573],[34847,26576],[34881,26587],[34947,26611],[34950,26612],[34955,26613],[34958,26614],[34961,26615],[34976,26618],[34984,26619],[34985,26620],[34992,26621],[34995,26622],[35001,26623],[35005,26624],[35007,26625],[35015,26629],[35023,26630],[35031,26632],[35035,26633],[35521,15471]],
/* 20. */ [[35991,16416],[36123,16022],[36137,16023],[36262,16084],[36270,16087],[36314,16114],[36321,16115],[36355,16119],[36515,16211],[36519,16213],[36534,16221],[36559,16228],[36594,16244]],
/* 21. */ [[36120,16022],[36131,16023],[36145,16027],[36149,16028],[36155,16029],[36202,16057],[36348,16118],[36360,16120],[36375,16126],[36433,16174],[36552,16226],[36577,16238]],
/* 22. */ [[30701,7408],[31151,7982],[37528,17176],[37551,17178],[37555,17179],[37569,17180],[37573,17181],[37581,17182],[37600,17184],[37613,17185],[37710,17149],[37719,17150],[38081,17917]],
/* 23. */ [[37207,16838],[37228,16857],[37273,16886],[37274,16888],[37281,16889],[37361,16948],[37524,17175],[37633,17199],[37662,17215],[38258,18027],[38551,18281]],
/* 27. */ [[20174,11844],[20851,12113],[21158,30334],[21276,18847],[21286,18851],[21295,18856],[21299,18857],[21316,18862],[21318,18863],[21330,18869],[21337,18871],[21346,18873],[21353,18876],[21368,18881],[21408,18784],[22697,19321],[22710,19330],[22713,19331],[22717,19332],[22720,19333],[22722,19334],[22725,19335],[22731,19337],[22736,19338],[22743,19341],[22747,19344],[22748,19345],[22751,19348],[22753,19349],[24022,12481],[24052,12482],[24071,12483],[24091,12484],[24113,12485],[24130,12486],[24158,12487],[24193,12488],[24227,12489],[24292,12491],[24347,12492],[24468,13495],[24487,13496],[24539,13498],[24555,13499],[24643,13124],[24683,12970],[24697,12971],[24728,12972],[24747,12973],[24760,12974],[24833,13040],[24897,13203],[24958,13247],[25749,28550],[27110,28775],[27124,28793],[27848,20152],[32967,21726],[33244,23106],[34322,31561],[34324,31562],[34431,29078],[35406,15423],[35413,15424],[35416,15427],[35426,15430],[35434,15432],[35439,15433],[35444,15434],[35447,15435],[35450,15436],[35462,15438],[35465,15439],[36035,16436],[36165,16033],[36335,16116],[36405,16146],[36936,16715],[36955,16718],[37001,16722],[37005,16723],[37021,16724],[38225,17990],[38379,17393],[38382,17395],[38384,17399],[38472,18194]],
/* 28. */ [[20375,11882],[20833,12106],[23303,13309],[23531,31841],[24384,12494],[25529,13592],[25585,13637],[25722,13849],[26820,28599],[27212,28814],[27232,28817],[27239,28821],[27316,28853],[27408,28926],[27412,28928],[27570,19840],[27854,20159],[28816,33628],[29043,33227],[29275,3383],[29296,3392],[30200,6628],[31236,8037],[31413,9860],[31947,20693],[33102,20841],[33355,22907],[33357,22915],[33360,22916],[33527,23017],[36707,16570],[36709,16571],[36715,16578],[36718,16579],[37426,17520],[37510,17117],[37864,17783],[38017,17835],[38521,18256],[39021,30077],[39025,30089]],
/* 29. */ [[20407,11904],[20410,11906],[20674,11364],[21008,11698],[22329,1932],[22341,1938],[22352,1945],[22359,1947],[22361,1948],[22363,1954],[22400,1982],[22825,19215],[22864,19248],[22874,19403],[23247,13400],[23283,13291],[23312,13313],[23414,12725],[23593,13449],[23668,13453],[23755,12774],[23796,12776],[23878,12888],[24060,12482],[24082,12483],[24279,12490],[24397,12495],[24531,13498],[24556,13499],[24689,12970],[24725,12972],[24836,13042],[25319,14441],[25387,14476],[25498,13864],[25518,13574],[25559,13615],[25609,13683],[25628,13708],[27654,19920],[27933,30658],[28487,30670],[28822,33633],[31946,20693],[31951,20694],[32547,22289],[32557,22292],[32573,22296],[32596,22310],[32982,21537],[33435,22953],[33441,22954],[33443,22955],[33448,22956],[33483,22967],[33494,22987],[33517,23012],[35098,26348],[35650,15798],[35968,16406],[35977,16412],[35981,16413],[36004,16424],[36006,16425],[36009,16427],[36045,16270],[36065,16362],[36067,16363],[36299,16104],[36325,16115],[36530,16219],[36752,16604],[36758,16605],[36761,16607],[36813,16632],[36823,16638],[36893,16476],[37011,16723],[37447,17542],[37878,17796],[37879,17797]],
/* 30. */ [[20601,11314],[20603,11315],[20609,11317],[20611,11318],[21477,18957],[23161,12645],[23201,13372],[23510,12672],[23639,13452],[23678,13453],[23714,13467],[23768,12775],[24161,12487],[24188,12488],[24223,12489],[24259,12490],[24293,12491],[24336,12492],[24440,12576],[24485,13496],[24860,13095],[24881,12356],[24980,13261],[25294,14423],[25377,14474],[25756,28555],[25758,28556],[26679,15093],[26815,28596],[27423,19655],[27737,20091],[27745,20092],[27913,20198],[28826,33637],[29324,3412],[29700,4677],[30276,6609],[32005,31937],[32040,20749],[32045,20753],[32310,22767],[32847,21910],[33287,22879],[33557,23022],[33566,23023],[33585,23024],[33605,23025],[33624,23026],[34325,31563],[34328,31564],[34423,29076],[34427,29077],[34434,29079],[34472,29122],[34674,26490],[34688,26492],[34726,26513],[34843,26575],[35039,26634],[35044,26635],[36077,16012],[36084,16013],[36122,16022],[36150,16028],[36152,16029],[36361,16120],[36408,16147],[36409,16148],[36511,16208],[36553,16226],[36556,16227],[36585,16240],[36608,16535],[36612,16536],[36618,16537],[36622,16538],[36628,16539],[36631,16541],[36633,16542],[37012,16723],[37468,17265],[37471,17266],[37819,17743],[37909,17809],[37933,17813],[37958,17814],[38188,17973],[38222,17988],[38226,17993],[38243,18001],[38339,17355],[38377,17393],[38486,18215]],
/* 31. */ [[20604,11315],[20606,11316],[28222,20267],[28645,20649],[29447,3689],[31460,10055],[32546,22288],[33298,22882],[33332,22883],[34095,25560],[34096,25560],[34254,26181],[34264,26223],[34265,26223],[34272,26237],[34301,26268],[34711,26497],[36644,16545],[36648,16546],[36843,16648],[37068,17465],[37895,17804]],
/* 32. */ [[20231,11808],[20766,12092],[20768,12093],[20771,12097],[21085,30302],[27791,20045],[38287,18058],[38612,18348],[38651,29944],[38663,29945],[38740,30013]],
/* 33. */ [[21033,12259],[21336,18871],[21388,18921],[22938,12594],[22959,12595],[22985,12596],[23008,12597],[23035,12599],[23055,12600],[23077,12601],[23114,12603],[23141,12605],[23305,13309],[23352,13334],[23629,13451],[23648,13452],[23792,12776],[24041,12482],[24067,12483],[24114,12485],[24500,13496],[24542,13498],[24853,13091],[25350,14456],[25355,14457],[25370,14473],[25381,14474],[25608,13683],[25665,13847],[25935,14645],[26681,15093],[26763,14998],[26816,28596],[27063,30615],[27068,30618],[27677,19971],[27891,20171],[29293,3391],[30237,6036],[32012,31947],[32015,31948],[32099,22437],[32365,22824],[32891,21457],[33521,23017],[33552,23022],[33639,23027],[33659,23029],[33666,23028],[34381,29046],[34388,29053],[34401,29062],[34403,29063],[34432,29079],[34454,29117],[34464,29119],[34466,29120],[34469,29121],[34503,29149],[35452,15436],[36336,16116],[36969,16719],[37020,16724],[37067,17465],[37770,17576],[37907,17808],[37937,17813],[37957,17814],[38221,17987],[38438,18159],[38527,18264]],
/* 34. */ [[20396,11899],[20431,11312],[21303,18858],[21341,18872],[22305,2012],[22354,1945],[22643,19289],[22757,19354],[22802,19154],[23195,13371],[23509,12671],[23673,13453],[23689,13454],[24026,12481],[24057,12482],[24074,12483],[24092,12484],[24116,12485],[24353,12492],[24406,12500],[24731,12972],[24738,12973],[24922,13222],[25030,14174],[25544,13607],[25549,13610],[25723,13850],[25750,28553],[26671,15078],[26758,14994],[26767,15000],[26775,15010],[26784,15016],[26799,28575],[26879,15295],[27118,28779],[27579,19849],[27800,20053],[28150,20415],[28151,20416],[28153,20417],[28648,20650],[28832,33643],[28912,33445],[28996,33514],[29011,33109],[29194,3335],[29281,3388],[29333,3416],[29340,3432],[29687,4670],[29794,4820],[30413,6820],[30419,6823],[31414,9868],[31436,10002],[32004,31936],[32101,22438],[32170,22532],[32421,21969],[32571,22296],[34341,28986],[36339,16116],[36544,16224],[36714,16574],[37010,16723],[37153,16783],[37858,17780],[37893,17804],[37897,17805],[37929,17812],[38186,17973],[38323,18153],[38364,17367],[38387,17400],[38526,18263],[38623,31624]],
/* 35. */ [[20224,11806],[20228,11807],[21142,30324],[21301,18858],[21326,18869],[21342,18872],[21344,18873],[21411,18785],[21428,18796],[22642,19285],[22940,12594],[22962,12595],[22984,12596],[23006,12597],[23032,12599],[23056,12600],[23078,12601],[23170,12650],[23424,12726],[23596,13450],[23619,13451],[23624,13451],[23642,13452],[23666,13453],[23685,13454],[23881,12893],[24457,13495],[24458,13495],[24491,13496],[24515,13497],[24538,13498],[24553,13499],[24740,12973],[24832,13040],[26273,14720],[26325,14741],[29387,3563],[32468,22181],[32792,21230],[32889,21447],[33523,23017],[33556,23022],[33619,23026],[33637,23027],[33663,23028],[36951,16718],[36954,16718],[36985,16719],[37148,16781],[37403,17500],[37410,17504],[37462,17257],[38473,18195]],
/* 36. */ [[20852,12113],[20856,12116],[20857,12117],[21300,18857],[21313,18861],[21314,18862],[21319,18863],[21322,18866],[21328,18869],[21333,18870],[21338,18871],[21351,18874],[21354,18876],[21356,18877],[21362,18878],[21367,18881],[21373,18883],[24496,13496],[24652,13132],[24676,12970],[24701,12971],[24735,12972],[24751,12973],[24763,12974],[24829,13036],[25748,28550],[25769,28560],[34321,31561],[34323,31562],[34344,28991],[34346,28996],[34349,28999],[34364,29025],[34405,29064],[36032,16435],[36164,16033],[36169,16035],[36407,16146],[36425,16156],[36598,16245],[36971,16719],[36995,16720],[36997,16721],[37019,16724],[37022,16725],[38368,17380],[38372,17389],[38385,17400],[38540,18275]],
/* 37. */ [[21942,1408],[22021,1525],[22023,1526],[22028,1529],[37217,16846],[37269,16882],[37300,16904],[37313,16908],[37670,17223],[37679,17232],[39248,27875]],
/* 38. */ [[20614,11318],[20631,11330],[21334,18870],[21402,18929],[21475,18957],[22701,19325],[22786,19127],[23649,13452],[23672,13453],[23942,12391],[24031,12481],[24055,12482],[24075,12483],[24101,12484],[24120,12485],[24141,12486],[24173,12487],[24198,12488],[24244,12489],[24272,12490],[24304,12491],[24341,12492],[24488,13496],[24562,13499],[25293,14423],[25359,14459],[25378,14474],[25757,28555],[25761,28556],[26290,14725],[26683,15093],[34337,31571],[35407,15423],[35411,15424],[36410,16148],[37004,16723],[37839,17758],[38034,17846],[38037,17852],[38515,18251]],
/* 39. */ [[22932,12594],[22953,12595],[22972,12596],[22997,12597],[23022,12599],[23047,12600],[23068,12601],[23097,12602],[23112,12603],[23132,12604],[30191,6257],[33540,23022],[33569,23023],[33627,23026],[33644,23027],[33670,23028]],
/* 40. */ [[22214,561],[22935,12594],[22954,12595],[22974,12596],[23000,12597],[23023,12599],[23044,12600],[23066,12601],[23189,13371],[23504,12669],[23522,12687],[33543,23022],[33573,23023],[33593,23024],[33613,23025],[33633,23026],[33651,23027],[33672,23028]],
/* 41. */ [[22937,12594],[22958,12595],[22980,12596],[23002,12597],[23021,12599],[23042,12600],[23064,12601],[23124,12604],[23408,12725],[23443,12727],[23626,13451],[23644,13452],[24827,13036],[24890,13203],[24920,13222],[25859,14591],[32103,22439],[33547,23022],[33570,23023],[33588,23024],[33606,23025],[33669,23028],[33958,25517],[35520,15471],[36976,16719]],
/* 42. */ [[22966,12595],[22987,12596],[23013,12597],[23027,12599],[23051,12600],[23075,12601],[23133,12604],[30075,5668],[33561,23023],[33579,23024],[33600,23025],[33665,23028]],
/* 43. */ [[22030,1532],[22983,12596],[23009,12597],[23029,12599],[23052,12600],[23073,12601],[23096,12602],[23113,12603],[23129,12604],[33549,23022],[33567,23023],[33583,23024],[33604,23025],[33620,23026],[33640,23027],[33668,23028]],
/* 44. */ [[23050,12600],[23072,12601],[23091,12602],[23102,12603],[23127,12604],[26472,14780],[32075,22420],[33582,23024],[33623,23026],[33645,23027],[33671,23028]],
/* 45. */ [[21429,18796],[23209,13372],[23756,12774],[23781,12775],[23793,12776],[23890,12901],[24027,12481],[24054,12482],[24084,12483],[24096,12484],[24138,12486],[24162,12487],[24201,12488],[24239,12489],[24269,12490],[24299,12491],[24348,12492],[24446,12580],[24489,13496],[24508,13497],[24541,13498],[24559,13499],[24581,13512],[24672,12970],[24696,12971],[24721,12972],[24755,12974],[24962,13249],[24970,13251],[24984,13263],[35463,15438],[36340,16116],[36427,16156],[36975,16719],[37024,16725]],
/* 46. */ [[21405,18782],[24019,12481],[24046,12482],[24072,12483],[24087,12484],[24111,12485],[24128,12486],[24171,12487],[24210,12488],[24237,12489],[24307,12491],[24350,12492],[24409,12501],[24445,12580],[24480,13496],[24506,13497],[24534,13498],[24557,13499],[24795,12986],[24847,13086]],
/* 47. */ [[23761,12774],[24025,12481],[24050,12482],[24070,12483],[24093,12484],[24133,12486],[24164,12487],[24208,12488],[24247,12489],[24316,12491],[24493,13496],[38445,18164]],
/* 48. */ [[23977,12472],[24030,12481],[24049,12482],[24073,12483],[24099,12484],[24136,12486],[24156,12487],[24199,12488],[24228,12489],[24267,12490],[24300,12491],[24342,12492],[24401,12496],[24460,13495],[24479,13496],[24514,13497],[24532,13498],[24705,12971],[24777,12975]],
/* 49. */ [[21452,18932],[24033,12481],[24044,12482],[24078,12483],[24094,12484],[24118,12485],[24139,12486],[24207,12488],[24314,12491],[24358,12492],[24369,12493],[38218,17985]],
/* 50. */ [[28043,20226],[28056,20227],[28076,20228],[28100,20230],[28102,20230],[32651,22333],[32652,22333],[32664,22335],[32670,22335],[32672,22335],[32685,22336],[32728,22338],[37828,17751]],
/* 51. */ [[26214,14699],[26710,14945],[27736,20091],[27742,20092],[27767,20107],[27796,20050],[35476,15443],[35481,15444],[35486,15445],[35491,15447],[35543,15483]],
/* 52. */ [[21242,18463],[22063,1406],[22131,193],[22248,2116],[25234,13957],[25721,13807],[25823,14563],[25929,14635],[26039,14930],[26697,14940],[27061,30612],[27147,28799],[29220,3361],[34014,25220],[35665,15822]],
/* 53. */ [[35984,16413],[35992,16416],[36095,16016],[36104,16017],[36112,16020],[36302,16110],[36306,16111],[36319,16115],[36371,16125],[36449,16179],[36454,16180],[36457,16181],[36520,16213]],
/* 54. */ [[22325,1930],[36022,16432],[36024,16434],[36030,16435],[36158,16030],[36167,16035],[36312,16114],[36447,16179],[36453,16180],[36456,16181],[38150,17691]],
/* 55. */ [[21952,1410],[37537,17177],[37547,17178],[37571,17180],[37578,17182],[37601,17184],[37615,17185],[37696,17136],[37700,17141],[37715,17149],[37738,17155],[38092,17928],[38119,17950],[39249,27875],[39254,27876],[39282,27777]],
/* 57. */ [[20114,33672],[20116,33674],[20117,33675],[20118,33676],[20120,33678],[20121,33679],[20124,33682],[20125,33683],[20126,33685],[20159,32950],[20161,32951]],
/* 58. */ [[22941,12594],[22963,12595],[22990,12596],[23010,12597],[23100,12603],[23121,12604],[33554,23022],[33572,23023],[33590,23024],[33608,23025],[33641,23027],[33664,23028]],
/* 59. */ [[22942,12594],[23057,12600],[23092,12602],[23107,12603],[23123,12604],[32351,22820],[33565,23023],[33589,23024],[33610,23025],[33628,23026],[33646,23027],[33673,23028]],
/* 60. */ [[22945,12594],[22967,12595],[23031,12599],[23054,12600],[23074,12601],[23095,12602],[23111,12603],[23126,12604],[33563,23023],[33584,23024],[33603,23025]],
/* 61. */ [[22943,12594],[22989,12596],[23011,12597],[23030,12599],[23053,12600],[23076,12601],[23086,12602],[23109,12603],[32275,22738],[33553,23022],[33642,23027]],
/* 62. */ [[22768,19365],[24144,12486],[26694,14939],[27798,20051],[27847,20151],[32840,21907],[32868,21320],[33273,23142],[35290,15702],[37796,17603],[37884,17800],[37901,17807],[37924,17812],[37932,17813],[37954,17814],[37960,17815],[37975,17817],[37984,17818],[38020,17837]],
/* 63. */ [[31085,7974],[31095,7975],[31102,7976],[31115,7977],[31122,7978],[31127,7979],[31139,7980],[31145,7981],[31148,7981],[31169,7984],[31292,8353]],
/* 64. */ [[28794,33584],[29200,3352],[29257,3371],[38835,32359],[38920,32362],[38939,32443],[38940,32444],[38941,32445],[38942,32446],[38943,32447],[38959,32492],[38963,32493]],
/* 68. */ [[22835,19221],[22844,19237],[22921,13432],[22948,12594],[22960,12595],[22979,12596],[23003,12597],[23028,12599],[23049,12600],[23069,12601],[23087,12602],[23104,12603],[23131,12604],[23190,13371],[23373,13352],[23430,12726],[23628,13451],[23721,13470],[23732,13476],[23880,12893],[24686,12970],[24785,12978],[24868,12325],[25607,13683],[25661,13843],[25818,14558],[26663,15064],[26702,14984],[26756,14991],[26824,28603],[27799,20053],[27851,20156],[28497,30676],[28504,30677],[28781,8860],[28994,33514],[29034,33208],[29834,5222],[30407,6819],[30956,7868],[30988,9366],[31446,10022],[32014,31948],[32835,21903],[33169,21036],[33208,21208],[33548,23022],[33568,23023],[33587,23024],[33609,23025],[33625,23026],[33647,23027],[33676,23028],[34456,29117],[34461,29119],[34467,29120],[34470,29121],[34485,29136],[34487,29138],[34495,29144],[35312,15732],[35451,15436],[36087,16013],[36180,16039],[36184,16040],[36259,16083],[36973,16719],[37018,16724],[37026,16725],[37908,17808],[37910,17809],[37940,17813],[37947,17814],[38335,17351]],
/* 69. */ [[29589,3984],[29612,4066],[29656,4548],[29793,4820],[30240,6040],[30243,6041],[30246,6042],[30462,6869],[31974,20715],[31975,20716],[34012,25183],[35893,29739],[38962,32493]],
/* 70. */ [[27568,19952],[29520,3882],[29522,3885],[30278,6616],[30370,6793],[34010,25182],[35885,29735],[35910,29436],[39062,27157],[39069,27146],[39104,27351],[39165,27500]],
/* 71. */ [[20590,12050],[21084,30302],[21280,18849],[21379,18901],[22809,19162],[23665,13453],[23684,13454],[24528,13498],[24716,12972],[24761,12974],[24843,13050],[24969,13251],[24977,13259],[25937,14645],[26802,28582],[26819,28598],[27049,30596],[27052,30603],[27112,28776],[27114,28777],[27115,28778],[27117,28779],[27120,28783],[27224,28816],[27371,28917],[27410,28927],[27415,28929],[27481,19755],[27577,19849],[27787,20041],[28825,33637],[29294,3391],[29339,3432],[29695,4676],[30203,6631],[30245,6041],[30247,6042],[30986,9360],[31027,8290],[32133,22496],[32309,22767],[32447,22046],[32953,21725],[33246,23108],[33271,23141],[35568,15741],[35675,15832],[35982,16413],[36331,16116],[36713,16574],[37007,16723],[37383,17496],[37880,17798],[38027,17840],[38201,17981],[38324,18153],[38485,18214],[38534,18272],[39022,30078]],
/* 72. */ [[20402,11903],[21686,742],[21694,743],[22331,1934],[22336,1936],[22343,1939],[22357,1946],[23383,13366],[23394,12716],[23480,12730],[23986,12476],[24483,13496],[25366,14473],[25376,14474],[25513,13571],[26677,15093],[27034,19637],[27752,20095],[28585,20648],[32965,21726],[32981,21513],[34363,29025],[34366,29026],[35097,26347],[35294,15704],[36025,16434],[36061,16359],[36068,16364],[36070,16365],[36127,16022],[36301,16110],[36304,16111],[36308,16112],[36352,16119],[36429,16163],[36461,16182],[36483,16196],[36517,16212],[36521,16213],[36523,16214],[36524,16215],[36526,16216],[36675,16552],[37440,17541],[37874,17793],[37946,17814],[38174,17730],[38216,17982],[38790,31818]],
/* 73. */ [[22939,12594],[22961,12595],[22978,12596],[23001,12597],[23026,12599],[23048,12600],[23071,12601],[23090,12602],[23106,12603],[23130,12604],[29322,3410],[33544,23022],[33562,23023],[33580,23024],[33601,23025],[33621,23026],[33638,23027],[33667,23028]],
/* 74. */ [[23803,12776],[23806,12777],[24276,12490],[25367,14473],[25374,14474],[28782,8861],[32756,22345],[33229,23083],[35292,15703],[35457,15437],[35464,15438],[36088,16013],[36248,16080],[36513,16208],[37442,17541],[37444,17542],[37987,17818]],
/* 75. */ [[21208,30363],[21422,18790],[26673,15084],[26808,28590],[26981,28747],[27113,28777],[27376,28918],[33216,21219],[33218,21220],[33220,21221],[34396,29060],[36250,16081]],
/* 77. */ [[21031,12255],[22834,19221],[22865,19250],[24017,12481],[24042,12482],[24065,12483],[24088,12484],[24132,12486],[24268,12490],[24315,12491],[24351,12492],[24467,13495],[24498,13496],[24519,13497],[24560,13499],[24614,13556],[26324,14740],[26759,14995],[27060,30611],[27842,20146],[32855,21311],[33157,20994],[33168,21032],[33207,21206],[33213,21210],[34419,29073],[34465,29120],[34486,29137],[34492,29142],[34493,29143],[34512,29154],[34520,29161],[35285,15688],[35454,15436],[35998,16422],[36423,16154],[36927,16714],[36938,16715],[37920,17811],[37926,17812],[37942,17813],[37955,17814],[37979,17817],[37985,17818],[38328,17321],[38716,29990]],
/* 78. */ [[24020,12481],[24045,12482],[24069,12483],[24090,12484],[24121,12485],[24142,12486],[24206,12488],[24238,12489],[24275,12490],[24297,12491],[24334,12492]],
/* 79. */ [[21660,671],[22049,1390],[22077,2675],[22233,531],[22242,2106],[23321,13316],[23544,12741],[26320,14739],[26333,14742],[26336,14743],[26337,14744],[35957,29803],[37203,16837]],
/* 80. */ [[33035,21809],[33038,21810],[33042,21811],[33052,21820],[33054,21821],[33055,21822],[33057,21823],[33060,21824],[33061,21825],[33064,21826],[33066,21827]],
/* 81. */ [[22766,19365],[24495,13496],[24530,13498],[32853,21310],[37761,17568],[37787,17590],[37902,17807],[37917,17811],[37925,17812],[37935,17813],[37948,17814],[37989,17818],[38013,17833],[38341,17361]],
/* 82. */ [[20413,11300],[20448,11943],[20613,11318],[20654,11351],[20805,11499],[20807,11500],[20846,12112],[21022,12240],[21845,1009],[22166,430],[22252,2127],[22308,2013],[22544,2353],[22845,19237],[23782,12775],[23856,12866],[24023,12481],[24048,12482],[24076,12483],[24095,12484],[24112,12485],[24140,12486],[24153,12487],[24191,12488],[24232,12489],[24261,12490],[24296,12491],[24354,12492],[24494,13496],[24842,13046],[26750,14976],[26865,15282],[26944,15368],[27459,19733],[27909,20186],[28529,20617],[28662,20669],[28779,8858],[33172,21045],[33289,22881],[33296,22882],[33300,22884],[33304,22885],[33311,22886],[33318,22890],[33323,22892],[33335,22893],[33481,22967],[34342,28987],[34368,29028],[35085,26344],[35092,26345],[35256,31584],[35437,15432],[35471,15441],[35482,15444],[35488,15445],[35704,15871],[36147,16028],[36363,16120],[36416,16151],[36635,16544],[36642,16545],[36651,16547],[36655,16548],[36671,16551],[36682,16554],[36792,16621],[36796,16622],[37240,16870],[37684,17126],[37748,17164],[37753,17165],[37891,17803],[37956,17814],[38040,17857],[38249,18010],[38338,17354],[38614,18349],[38686,29958],[38687,29959],[38957,32489],[38961,32493],[39173,27517]],
/* 83. */ [[20202,11766],[20449,11943],[20661,11354],[20668,11358],[21409,18784],[22307,2013],[31773,10362],[31936,11086],[34630,26483],[35441,15433],[38693,29962]],
/* 84. */ [[23773,12775],[24021,12481],[24043,12482],[24066,12483],[24102,12484],[24117,12485],[24129,12486],[24172,12487],[24245,12489],[24305,12491],[24335,12492]],
/* 85. */ [[20847,12112],[27453,19725],[35077,26343],[35086,26344],[35091,26345],[35420,15429],[35427,15430],[35468,15441],[35472,15442],[35475,15443],[35484,15445],[38171,17721],[38542,18275]],
/* 86. */ [[33321,22890],[33324,22892],[33338,22893],[33340,22895],[33350,22904],[33352,22902],[33504,22990],[36681,16554],[36689,16558],[36701,16565],[36830,16641]],
/* 87. */ [[33431,22952],[33445,22955],[33449,22956],[33451,22958],[33453,22959],[33456,22961],[33458,22957],[33463,22962],[33465,22965],[33471,22966],[33482,22967],[36748,16603],[36762,16607],[36764,16608],[36768,16609],[36770,16610],[36772,16611],[36777,16612],[36780,16614],[36785,16615],[36805,16625]],
/* 88. */ [[22824,19215],[36059,16357],[36064,16362],[36066,16363],[36078,16012],[36103,16017],[36107,16018],[36140,16025],[36163,16033],[36173,16035],[36175,16036],[36249,16080],[36251,16081],[36253,16082],[36273,16088],[36274,16089],[36275,16090],[36276,16091],[36277,16092],[36278,16094],[36283,16095],[36386,16131],[36434,16174],[36484,16196],[36525,16215],[36996,16721],[37046,16737]],
/* 89. */ [[20593,12052],[20850,12113],[20859,12119],[21023,12240],[21306,18859],[21348,18873],[21361,18878],[21366,18881],[21370,18882],[22339,1937],[22345,1943],[22349,1944],[22355,1945],[23181,12656],[23825,12797],[24056,12482],[24083,12483],[24103,12484],[24465,13495],[24497,13496],[24551,13499],[24909,13209],[24935,13232],[25321,14442],[25332,14449],[25379,14474],[25724,13850],[25751,28553],[26395,14763],[26676,15093],[26696,14940],[26757,14994],[26762,14998],[26800,28579],[27062,30615],[27222,28815],[27374,28918],[27419,28932],[27422,28941],[27482,19756],[27790,20044],[28584,20648],[28612,20497],[32459,22095],[32473,22184],[32873,21329],[33219,21220],[33221,21221],[33228,23083],[33275,23143],[34397,29060],[34444,29094],[35455,15437],[35460,15438],[35699,15856],[35700,15857],[35702,15858],[35980,16412],[36049,15990],[36089,16014],[36092,16015],[36097,16016],[36101,16017],[36252,16081],[36605,16246],[36953,16718],[36970,16719],[37437,17541],[37445,17542],[37794,17602],[37825,17751],[37894,17804],[37896,17805],[37928,17812],[37936,17813],[37949,17814],[37969,17816],[37990,17818],[38014,17833],[38345,17289],[38348,17290],[38365,17367],[38373,17389]],
/* 90. */ [[22933,12594],[22955,12595],[22975,12596],[22998,12597],[23025,12599],[23046,12600],[23065,12601],[33542,23022],[33564,23023],[33581,23024],[33602,23025]],
/* 91. */ [[23722,13472],[27023,19614],[33290,22881],[33295,22882],[33306,22885],[33320,22890],[33328,22892],[33331,22883],[33339,22895],[33341,22896],[33342,22898],[33345,22897],[33347,22900],[36636,16544],[36641,16545],[36647,16546],[36658,16548],[36665,16550],[36669,16551],[36688,16558],[36690,16559],[36692,16560],[36693,16561],[36695,16562],[36698,16563],[37417,17510],[37899,17806],[37934,17813]],
/* 92. */ [[35993,16416],[36060,16358],[36072,16007],[36073,16008],[36076,16011],[36080,16012],[36085,16013],[36100,16017],[36106,16018],[36109,16019],[36115,16021],[36135,16023],[36143,16026],[36146,16027],[36151,16029],[36157,16030],[36161,16031],[36170,16035],[36176,16037],[36178,16038],[36182,16039],[36185,16040],[36291,16098],[36385,16131],[36435,16174],[36450,16179],[36458,16181],[36463,16183],[36566,16233],[38381,17394]]
];
</script>

View File

@ -18,8 +18,7 @@ function createFace(faceId, photoId, selectable) {
div.classList.add("face"); div.classList.add("face");
div.setAttribute("photo-id", photoId); div.setAttribute("photo-id", photoId);
div.setAttribute("face-id", faceId); div.setAttribute("face-id", faceId);
const dir = String(faceId % 100).padStart(2, '0') div.style.backgroundImage = "url(face-data/" + (faceId % 100) + "/" + faceId + "-original.png)";
div.style.backgroundImage = `url(faces/${dir}/${faceId}.jpg)`;
div.addEventListener("click", (event) => { div.addEventListener("click", (event) => {
if (event.shiftKey) { /* identities */ if (event.shiftKey) { /* identities */
let faceId = parseInt(event.currentTarget.getAttribute("face-id")); let faceId = parseInt(event.currentTarget.getAttribute("face-id"));
@ -165,7 +164,7 @@ function getIdentities(faceId) {
const identitiesBlock = document.getElementById("identities"); const identitiesBlock = document.getElementById("identities");
identitiesBlock.innerHTML = ""; identitiesBlock.innerHTML = "";
const search = ''; //faceId ? "?withScore=" + faceId : ""; const search = faceId ? "?withScore=" + faceId : "";
window.fetch("api/v1/identities" + search).then(res => res.json()).then((identities) => { window.fetch("api/v1/identities" + search).then(res => res.json()).then((identities) => {
identities.sort((a, b) => { identities.sort((a, b) => {
if (a.lastName == b.lastName) { if (a.lastName == b.lastName) {

View File

@ -77,73 +77,39 @@ body {
<div id="loading"></div> <div id="loading"></div>
</div> </div>
<script> <script>
var base, placeholder, info, photos = [], photoIndex;
let base, function shuffle(array) {
placeholder, var index = array.length, tmp, random;
info,
photos = [],
photoIndex = -1;
let mode, filter;
const days = [
"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",
"Saturday"
];
const months = [
"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"
];
let activeFaces = [];
let paused = false,
tap = 0;
let countdown = 15;
let scheduled = false;
const onClick = (e) => {
const now = new Date().getTime();
if (tap && (now - tap < 300)) {
toggleFullscreen();
tap = 0;
} else {
tap = new Date().getTime();
}
};
const shuffle = (arr) => {
var index = arr.length, tmp, random;
while (index) { while (index) {
random = Math.floor(Math.random() * index); random = Math.floor(Math.random() * index);
index--; index--;
tmp = arr[index];
arr[index] = arr[random]; // And swap it with the current element.
arr[random] = tmp; tmp = array[index];
array[index] = array[random];
array[random] = tmp;
} }
return arr;
};
const faceClick = (event, face) => { return array;
console.log(face); }
face.relatedFaces.forEach((photo) => {
window.open(base + photo.path);
});
event.preventDefault = true;
event.stopImmediatePropagation();
event.stopPropagation();
return false;
};
const makeFaceBoxes = () => { var countdown = 15;
const days = [ "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" ],
months = [ "January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December" ];
let activeFaces = [];
function makeFaceBoxes() {
Array.prototype.forEach.call(document.querySelectorAll('.face'), (el) => { Array.prototype.forEach.call(document.querySelectorAll('.face'), (el) => {
el.parentElement.removeChild(el); el.parentElement.removeChild(el);
}); });
if (activeFaces.length === 0) { console.log("Not showing face boxes");
return; return;
}
const el = document.getElementById("photo"), const el = document.getElementById("photo"),
photo = photos[photoIndex]; photo = photos[photoIndex];
@ -163,6 +129,7 @@ const makeFaceBoxes = () => {
offsetTop = 0; offsetTop = 0;
} }
activeFaces.forEach((face) => { activeFaces.forEach((face) => {
const box = document.createElement("div"); const box = document.createElement("div");
box.classList.add("face"); box.classList.add("face");
@ -171,11 +138,20 @@ const makeFaceBoxes = () => {
box.style.top = offsetTop + Math.floor(face.top * height) + "%"; box.style.top = offsetTop + Math.floor(face.top * height) + "%";
box.style.width = Math.floor((face.right - face.left) * width) + "%"; box.style.width = Math.floor((face.right - face.left) * width) + "%";
box.style.height = Math.floor((face.bottom - face.top) * height) + "%"; box.style.height = Math.floor((face.bottom - face.top) * height) + "%";
box.addEventListener("click", (e) => { return faceClick(e, face); }); box.addEventListener("click", (event) => {
console.log(face);
face.relatedPhotos.forEach((photo) => {
window.open(base + photo.path);
});
event.preventDefault = true;
event.stopImmediatePropagation();
event.stopPropagation();
return false;
});
}); });
}; }
const loadPhoto = (index) => { function loadPhoto(index) {
const photo = photos[index], const photo = photos[index],
xml = new XMLHttpRequest(), xml = new XMLHttpRequest(),
url = base + photo.path + "thumbs/scaled/" + photo.filename, url = base + photo.path + "thumbs/scaled/" + photo.filename,
@ -193,29 +169,23 @@ const loadPhoto = (index) => {
} }
}; };
xml.onload = async (event) => { xml.onload = function(event) {
info.textContent = info.textContent =
days[taken.getDay()] + ", " + days[taken.getDay()] + ", " +
months[taken.getMonth()] + " " + months[taken.getMonth()] + " " +
taken.getDate() + " " + taken.getDate() + " " +
taken.getFullYear(); taken.getFullYear();
activeFaces = []; document.getElementById("photo").style.backgroundImage = "url(" + encodeURI(url).replace(/\(/g, '%28').replace(/\)/g, '%29') + ")";
makeFaceBoxes();
document.getElementById("photo").style.backgroundImage =
`url(${encodeURI(url).replace(/\(/g, '%28').replace(/\)/g, '%29')})`;
countdown = 15; countdown = 15;
tick(); tick();
window.fetch("api/v1/photos/faces/" + photo.id).then(res => res.json()).then((faces) => {
try {
const res = await window.fetch("api/v1/photos/faces/" + photo.id);
const faces = await res.json();
activeFaces = faces; activeFaces = faces;
makeFaceBoxes(photo); makeFaceBoxes(photo);
} catch (error) { }).catch(function(error) {
console.error(error); console.error(error);
info.textContent += "Unable to obtain face information :("; info.textContent += "Unable to obtain face information :(";
} });
}; }
xml.onerror = function(event) { xml.onerror = function(event) {
info.textContent = "Error loading photo. Trying next photo."; info.textContent = "Error loading photo. Trying next photo.";
@ -226,77 +196,14 @@ const loadPhoto = (index) => {
xml.send(); xml.send();
} }
const prevPhoto = async () => { function nextPhoto() {
if (photoIndex > 0) { photoIndex = (photoIndex + 1) % photos.length;
photoIndex--; loadPhoto(photoIndex);
loadPhoto(photoIndex);
return;
}
if (mode !== 'random/') {
photoIndex = photos.length;
loadPhoto(photoIndex);
return;
}
try {
const res = await window.fetch(
`api/v1/photos/${mode}${filter.replace(/ +/g, "%20")}`);
const data = await res.json();
if (data && data.items) {
info.textContent = photos.length + " photos found. Shuffling.";
photos = shuffle(data.items);
photoIndex = (photoIndex + 1) % photos.length;
loadPhoto(photoIndex);
} else if (data) {
photos.push(data);
photoIndex = photos.length - 1;
loadPhoto(photoIndex);
} else {
info.textContent = "No photos found for " + filter + ".";
}
} catch(error) {
console.error(error);
info.textContent = "Unable to fetch " + mode + "=" + filter + " :(";
}
} }
const nextPhoto = async () => { var scheduled = false;
if (photoIndex < photos.length - 1) {
photoIndex++;
loadPhoto(photoIndex);
return;
}
if (mode !== 'random/') { function tick() {
photoIndex = 0;
loadPhoto(photoIndex);
return;
}
try {
const res = await window.fetch(
`api/v1/photos/${mode}${filter.replace(/ +/g, "%20")}`);
const data = await res.json();
if (data && data.items) {
info.textContent = photos.length + " photos found. Shuffling.";
photos = shuffle(data.items);
photoIndex = 0;
loadPhoto(photoIndex);
} else if (data) {
photos.push(data);
photoIndex = photos.length - 1;
loadPhoto(photoIndex);
} else {
info.textContent = "No photos found for " + filter + ".";
}
} catch (error) {
console.error(error);
info.textContent = "Unable to fetch " + mode + "=" + filter + " :(";
}
}
const tick = () => {
if (scheduled) { if (scheduled) {
clearTimeout(scheduled); clearTimeout(scheduled);
} }
@ -312,16 +219,16 @@ const tick = () => {
countdown = 15; countdown = 15;
nextPhoto(); nextPhoto();
} }
}; }
const schedule = () => { function schedule() {
if (scheduled) { if (scheduled) {
clearTimeout(scheduled); clearTimeout(scheduled);
} }
tick(); tick();
}; }
const toggleFullscreen = () => { function toggleFullscreen() {
if (!document.fullscreenElement) { if (!document.fullscreenElement) {
document.documentElement.requestFullscreen(); document.documentElement.requestFullscreen();
} else { } else {
@ -329,75 +236,104 @@ const toggleFullscreen = () => {
document.exitFullscreen(); document.exitFullscreen();
} }
} }
}; }
const onKeyDown = (event) => { var paused = false,
switch (event.keyCode) { tap = 0;
case 32: /* space */
paused = !paused;
if (!paused) {
tick();
} else {
document.getElementById("loading").textContent = "||";
clearTimeout(scheduled);
scheduled = null;
}
return;
case 37: /* left */
prevPhoto();
return;
case 39: /* right */
nextPhoto();
return;
case 13: /* enter */
toggleFullscreen();
return;
}
};
document.addEventListener("DOMContentLoaded", function() { document.addEventListener("DOMContentLoaded", function() {
var tmp = document.querySelector("base"); var tmp = document.querySelector("base");
if (tmp) { if (tmp) {
/* Make sure there is a trailing slash */ base = new URL(tmp.href).pathname.replace(/\/$/, "") + "/"; /* Make sure there is a trailing slash */
base = new URL(tmp.href).pathname.replace(/\/$/, "") + "/";
} else {
base = "/";
}
var timeout = 0;
window.addEventListener("resize", (event) => {
if (timeout) {
window.clearTimeout(timeout);
}
timeout = window.setTimeout(makeFaceBoxes, 250);
});
document.addEventListener("click", onClick);
document.addEventListener("keydown", onKeyDown);
info = document.getElementById("info");
/* Trim off everything up to and including the ? (if its there) */
var parts = window.location.href.match(/^.*\?(.*)$/);
if (parts) {
parts = parts[1].split("=");
if (parts.length == 1) {
mode = "holiday/";
filter = parts[0];
} else { } else {
mode = parts[0].replace(/\/*$/, '/'); base = "/";
filter = parts[1];
} }
mode = mode
} else {
mode = "random/"
filter = "";
}
nextPhoto(); var timeout = 0;
window.addEventListener("resize", (event) => {
if (timeout) {
window.clearTimeout(timeout);
}
timeout = window.setTimeout(makeFaceBoxes, 250);
});
document.addEventListener("click", function(event) {
toggleFullscreen();
var now = new Date().getTime();
if (tap && (now - tap < 300)) {
toggleFullscreen();
tap = 0;
} else {
tap = new Date().getTime();
}
});
document.addEventListener("keydown", function(event) {
switch (event.keyCode) {
case 32: /* space */
paused = !paused;
if (!paused) {
tick();
} else {
document.getElementById("loading").textContent = "||";
clearTimeout(scheduled);
scheduled = null;
}
return;
case 37: /* left */
if (photoIndex == 0) {
photoIndex = photos.length;
}
photoIndex--;
loadPhoto(photoIndex);
return;
case 39: /* right */
photoIndex = (photoIndex + 1) % photos.length;
loadPhoto(photoIndex);
return;
case 13: /* enter */
toggleFullscreen();
return;
}
console.log(event.keyCode);
});
info = document.getElementById("info");
/* Trim off everything up to and including the ? (if its there) */
var parts = window.location.href.match(/^.*\?(.*)$/),
mode = "holiday",
filter = "";
if (parts) {
parts = parts[1].split("=");
if (parts.length == 1) {
filter = parts[0];
} else {
mode = parts[0];
filter = parts[1];
}
} else {
filter = "memorial day";
}
window.fetch("api/v1/photos/" + mode + "/" + filter.replace(/ +/g, "%20"))
.then(res => res.json()).then(function(data) {
if (data.items.length) {
info.textContent = photos.length + " photos found. Shuffling.";
photos = shuffle(data.items);
photoIndex = -1;
nextPhoto();
} else {
info.textContent = "No photos found for " + filter + ".";
}
}).catch(function(error) {
console.error(error);
info.textContent = "Unable to fetch " + mode + "=" + filter + " :(";
});
}); });
</script> </script>
</body> </body>

View File

@ -197,19 +197,18 @@ straglers = build_straglers(faces)
reduced = reduced + DBSCAN(straglers) reduced = reduced + DBSCAN(straglers)
# Build a final cluster with all remaining uncategorized faces # Build a final cluster with all remaining uncategorized faces
if False: remaining_cluster = {
remaining_cluster = { 'id': len(reduced) + 1,
'id': len(reduced) + 1, 'distance': 0,
'distance': 0, 'descriptors': [],
'descriptors': [], 'cluster': Undefined,
'cluster': Undefined, 'faces': []
'faces': [] }
} straglers = build_straglers(faces)
straglers = build_straglers(faces) for face in straglers:
for face in straglers: face['cluster'] = remaining_cluster
face['cluster'] = remaining_cluster remaining_cluster['faces'].append(face)
remaining_cluster['faces'].append(face) reduced.append(remaining_cluster)
reduced.append(remaining_cluster)
# Give all merged identity lists a unique ID # Give all merged identity lists a unique ID
for id, identity in enumerate(reduced): for id, identity in enumerate(reduced):
@ -242,46 +241,3 @@ print('Writing to "auto-clusters.html"')
redirect_on(os.path.join(html_path, 'auto-clusters.html')) redirect_on(os.path.join(html_path, 'auto-clusters.html'))
gen_html(reduced) gen_html(reduced)
redirect_off() redirect_off()
def create_identity(conn, identity):
"""
Create a new identity in the identities table
:param conn:
:param identity:
:return: identity id
"""
sql = '''
INSERT INTO identities(descriptors,displayName)
VALUES(?,?)
'''
cur = conn.cursor()
cur.execute(sql, (
np.array(identity['descriptors']),
f'cluster-{identity["id"]}'
))
conn.commit()
return cur.lastrowid
def update_face_identity(conn, faceId, identityId = None):
"""
Update the identity associated with this face
:param conn:
:param faceId:
:param identityId:
:return: None
"""
sql = '''
UPDATE faces SET identityId=? WHERE id=?
'''
cur = conn.cursor()
cur.execute(sql, (identityId, faceId))
conn.commit()
return None
print(f'Connecting to database: {db_path}')
conn = create_connection(db_path)
with conn:
for identity in reduced:
id = create_identity(conn, identity)
for face in identity['faces']:
update_face_identity(conn, face['id'], id)

View File

@ -3,15 +3,12 @@ import json
import os import os
import piexif import piexif
import argparse
from PIL import Image, ImageOps from PIL import Image, ImageOps
from deepface import DeepFace from deepface import DeepFace
from deepface.detectors import FaceDetector from deepface.detectors import FaceDetector
from retinaface import RetinaFace from retinaface import RetinaFace
import numpy as np import numpy as np
import cv2 import cv2
from ketrface.util import * from ketrface.util import *
from ketrface.db import * from ketrface.db import *
from ketrface.config import * from ketrface.config import *
@ -28,8 +25,7 @@ model_name = 'VGG-Face' # 'ArcFace'
detector_backend = 'mtcnn' # 'retinaface' detector_backend = 'mtcnn' # 'retinaface'
model = DeepFace.build_model(model_name) model = DeepFace.build_model(model_name)
# Derived from # Derived from https://github.com/serengil/deepface/blob/master/deepface/detectors/MtcnnWrapper.py
# https://github.com/serengil/deepface/blob/master/deepface/detectors/MtcnnWrapper.py
# Add parameters to MTCNN # Add parameters to MTCNN
from mtcnn import MTCNN from mtcnn import MTCNN
face_detector = MTCNN(min_face_size = 30) face_detector = MTCNN(min_face_size = 30)
@ -67,9 +63,7 @@ def variance_of_laplacian(image):
# measure, which is simply the variance of the Laplacian # measure, which is simply the variance of the Laplacian
return cv2.Laplacian(image, cv2.CV_64F).var() return cv2.Laplacian(image, cv2.CV_64F).var()
def extract_faces( def extract_faces(img, threshold=0.95, allow_upscaling = True, focus_threshold = 100):
img, threshold=0.95, allow_upscaling = True, focus_threshold = 100):
if detector_backend == 'retinaface': if detector_backend == 'retinaface':
faces = RetinaFace.detect_faces( faces = RetinaFace.detect_faces(
img_path = img, img_path = img,
@ -109,6 +103,8 @@ def extract_faces(
} }
to_drop = []
# Re-implementation of 'extract_faces' with the addition of keeping a # Re-implementation of 'extract_faces' with the addition of keeping a
# copy of the face image for caching on disk # copy of the face image for caching on disk
for k, key in enumerate(faces): for k, key in enumerate(faces):
@ -186,15 +182,12 @@ def extract_faces(
identity['image'] = Image.fromarray(resized) identity['image'] = Image.fromarray(resized)
# for key in to_drop:
# faces.pop(key)
return faces return faces
parser = argparse.ArgumentParser(description = 'Detect faces in images.')
parser.add_argument('photos', metavar='PHOTO', type=int, nargs='*',
help='PHOTO ID to scan (default: all unscanned photos)')
args = parser.parse_args()
print(args)
base = '/pictures/' base = '/pictures/'
conn = create_connection('../db/photos.db') conn = create_connection('../db/photos.db')
with conn: with conn:
@ -229,6 +222,11 @@ with conn:
image = face['image'] image = face['image']
print(f'Writing face {j+1}/{len(faces)}') print(f'Writing face {j+1}/{len(faces)}')
#face['analysis'] = DeepFace.analyze(img_path = img, actions = ['age', 'gender', 'race', 'emotion'], enforce_detection = False)
#face['analysis'] = DeepFace.analyze(img, actions = ['emotion'])
# TODO: Add additional meta-data allowing back referencing to original
# photo
face['version'] = 1 # version 1 doesn't add much... face['version'] = 1 # version 1 doesn't add much...
data = {k: face[k] for k in set(list(face.keys())) - set(['image', 'facial_area', 'landmarks'])} data = {k: face[k] for k in set(list(face.keys())) - set(['image', 'facial_area', 'landmarks'])}

View File

@ -1 +0,0 @@
__pycache__

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -45,6 +45,7 @@ def create_face(conn, face):
conn.commit() conn.commit()
return cur.lastrowid return cur.lastrowid
def create_face_descriptor(conn, face): def create_face_descriptor(conn, face):
""" """
Create a new face in the faces table Create a new face in the faces table

View File

@ -26,8 +26,7 @@ router.put("/faces/add/:id", (req, res) => {
return res.status(400).send("No faces supplied."); return res.status(400).send("No faces supplied.");
} }
return photoDB.sequelize.query( return photoDB.sequelize.query("UPDATE faces SET identityId=:identityId,identityDistance=0 " +
"UPDATE faces SET identityId=:identityId " +
"WHERE id IN (:faceIds)", { "WHERE id IN (:faceIds)", {
replacements: { replacements: {
identityId: id, identityId: id,
@ -72,8 +71,7 @@ router.post("/", (req, res) => {
replacements: identity, replacements: identity,
}).then(([ results, metadata ]) => { }).then(([ results, metadata ]) => {
identity.id = metadata.lastID; identity.id = metadata.lastID;
return photoDB.sequelize.query( return photoDB.sequelize.query("UPDATE faces SET identityId=:identityId,identityDistance=0 " +
"UPDATE faces SET identityId=:identityId " +
"WHERE id IN (:faceIds)", { "WHERE id IN (:faceIds)", {
replacements: { replacements: {
identityId: identity.id, identityId: identity.id,
@ -97,14 +95,14 @@ function euclideanDistance(a, b) {
let A = bufferToFloat32Array(a); let A = bufferToFloat32Array(a);
let B = bufferToFloat32Array(b); let B = bufferToFloat32Array(b);
let sum = 0; let sum = 0;
for (let i = 0; i < A.length; i++) { for (let i = 0; i < 128; i++) {
let delta = A[i] - B[i]; let delta = A[i] - B[i];
sum += delta * delta; sum += delta * delta;
} }
return Math.sqrt(sum); return Math.sqrt(sum);
} }
router.get("/:id?", async (req, res) => { router.get("/:id?", (req, res) => {
let id; let id;
if (req.params.id) { if (req.params.id) {
@ -116,101 +114,89 @@ router.get("/:id?", async (req, res) => {
const filter = id ? "WHERE identities.id=:id " : ""; const filter = id ? "WHERE identities.id=:id " : "";
const identities = await photoDB.sequelize.query("SELECT " + return photoDB.sequelize.query("SELECT " +
"identities.*," + "identities.*," +
"GROUP_CONCAT(faces.id) AS relatedFaceIds," + "GROUP_CONCAT(faces.id) AS relatedFaceIds," +
"GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds," + "GROUP_CONCAT(faces.photoId) AS relatedFacePhotoIds," +
"GROUP_CONCAT(facedescriptors.descriptors) AS relatedIdentityDescriptors " + "GROUP_CONCAT(faces.identityDistance) AS relatedIdentityDistances " +
"FROM identities " + "FROM identities " +
"INNER JOIN facedescriptors ON facedescriptors.id=faces.descriptorId " +
"INNER JOIN faces ON identities.id=faces.identityId " + "INNER JOIN faces ON identities.id=faces.identityId " +
filter + filter +
"GROUP BY identities.id", { "GROUP BY identities.id", {
replacements: { id }, replacements: {
id: id
},
type: photoDB.Sequelize.QueryTypes.SELECT, type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true raw: true
}); }).then((identities) => {
identities.forEach((identity) => {
identities.forEach((identity) => { const relatedFaces = identity.relatedFaceIds.split(","),
[ 'firstName', 'middleName', 'lastName' ].forEach(key => { relatedFacePhotos = identity.relatedFacePhotoIds.split(","),
if (!identity[key]) { relatedIdentityDistances = identity.relatedIdentityDistances.split(",");
identity[key] = ''; if (relatedFaces.length != relatedFacePhotos.length) {
console.warn("Face ID to Photo ID mapping doesn't match!");
} }
delete identity.relatedFaceIds;
delete identity.relatedFacePhotoIds;
identity.relatedFaces = relatedFaces.map((faceId, index) => {
return {
faceId: faceId,
photoId: relatedFacePhotos[index],
distance: parseFloat(relatedIdentityDistances[index] !== undefined ? relatedIdentityDistances[index] : -1)
};
});
}); });
const relatedFaces = identity.relatedFaceIds.split(","), if (!req.query.withScore) {
relatedFacePhotos = identity.relatedFacePhotoIds.split(","), console.log("No score request.");
relatedIdentityDescriptors = return identities;
identity.relatedIdentityDescriptors.split(",");
identity.relatedFaces = relatedFaces.map((faceId, index) => {
const distance = euclideanDistance(
relatedIdentityDescriptors[index],
identity.descriptors
);
return {
faceId,
photoId: relatedFacePhotos[index],
distance
};
});
delete identity.relatedFaceIds;
delete identity.relatedFacePhotoIds;
delete identity.relatedIdentityDescriptors;
});
//if (!req.query.withScore) {
console.log("No score request.");
return res.status(200).json(identities);
//}
// THe rest of this routine needs to be reworked -- I don't
// recall what it was doing; maybe getting a list of all identities
// sorted with distance to this faceId?
console.log("Looking up score against: " + req.query.withScore);
await Promise.map(identities, async (identity) => {
const descriptors = photoDB.sequelize.query(
"SELECT id FROM facedescriptors " +
"WHERE descriptorId " +
"IN (:id,:descriptorIds)", {
replacements: {
id: parseInt(req.query.withScore),
descriptorIds: identity.relatedFaces.map(
face => parseInt(face.faceId))
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
});
let target;
for (let i = 0; i < descriptors.length; i++) {
if (descriptors[i].descriptorId == req.query.withScore) {
target = descriptors[i].descriptors;
break;
}
}
if (!target) {
console.warn("Could not find descriptor for requested face: " + req.query.withScore);
return;
} }
console.log("Looking up score against: " + req.query.withScore);
/* For each face's descriptor returned for this identity, compute the distance between the return Promise.map(identities, (identity) => {
* requested photo and that face descriptor */ return photoDB.sequelize.query("SELECT * FROM facedescriptors WHERE faceId IN (:id,:faceIds)", {
descriptors.forEach((descriptor) => { replacements: {
for (let i = 0; i < identity.relatedFaces.length; i++) { id: parseInt(req.query.withScore),
if (identity.relatedFaces[i].faceId == descriptor.faceId) { faceIds: identity.relatedFaces.map(face => face.faceId)
identity.relatedFaces[i].distance = euclideanDistance(target, descriptor.descriptors); },
identity.relatedFaces[i].descriptors = descriptor.descriptors; type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
}).then((descriptors) => {
let target;
for (let i = 0; i < descriptors.length; i++) {
if (descriptors[i].faceId == req.query.withScore) {
target = descriptors[i].descriptors;
break;
}
}
if (!target) {
console.warn("Could not find descriptor for requested face: " + req.query.withScore);
return; return;
} }
}
});
}, {
concurrency: 5
});
return res.status(200).json(identities); /* For each face's descriptor returned for this identity, compute the distance between the
* requested photo and that face descriptor */
descriptors.forEach((descriptor) => {
for (let i = 0; i < identity.relatedFaces.length; i++) {
if (identity.relatedFaces[i].faceId == descriptor.faceId) {
identity.relatedFaces[i].distance = euclideanDistance(target, descriptor.descriptors);
identity.relatedFaces[i].descriptors = descriptor.descriptors;
return;
}
}
});
});
}, {
concurrency: 5
}).then(() => {
return identities;
});
}).then((identities) => {
return res.status(200).json(identities);
}).catch((error) => {
console.error(error);
return res.status(500).send("Error processing request.");
});
}); });
module.exports = router; module.exports = router;

View File

@ -951,7 +951,7 @@ router.get("/faces/:id", (req, res) => {
}); });
}); });
router.get("/random/:id?", async (req, res) => { router.get("/random/:id?", (req, res) => {
let id = parseInt(req.params.id), let id = parseInt(req.params.id),
filter = ""; filter = "";
@ -959,56 +959,54 @@ router.get("/random/:id?", async (req, res) => {
console.log("GET /random/" + id); console.log("GET /random/" + id);
filter = "AND id=:id"; filter = "AND id=:id";
} else { } else {
console.log("GET /random/"); filter = "AND faces>0";
filter = "";//AND faces>0";
id = undefined; id = undefined;
} }
/* If the requested ID is a duplicate, we need to find the original return photoDB.sequelize.query("SELECT id,duplicate FROM photos WHERE deleted=0 " + filter, {
* photo ID */ replacements: {
const results = await photoDB.sequelize.query( id: id
"SELECT id,duplicate FROM photos WHERE deleted=0 " + filter, { },
replacements: { id },
type: photoDB.Sequelize.QueryTypes.SELECT, type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true raw: true
}); }).then((results) => {
if (!results.length) {
if (!results.length) { return [];
return res.status(404).send({ message: id + " not found." });
}
if (id) {
if (results[0].duplicate) {
id = results[0].duplicate;
} }
} else { if (id) {
id = results[Math.floor(Math.random() * results.length)].id; if (results[0].duplicate) {
} id = results[0].duplicate;
}
const photos = await photoDB.sequelize.query( } else {
"SELECT photos.*,albums.path AS path FROM photos " + id = results[Math.floor(Math.random() * results.length)].id;
"INNER JOIN albums ON albums.id=photos.albumId " +
"WHERE photos.duplicate=0 AND photos.deleted=0 AND photos.scanned NOT NULL AND photos.id=:id", {
replacements: { id },
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
});
if (photos.length === 0) {
return res.status(404).send({ message: `Error obtaining ${id}.`});
}
const photo = photos[0];
for (var key in photo) {
if (photo[key] instanceof Date) {
photo[key] = moment(photo[key]);
} }
}
return photoDB.sequelize.query(
const faces = await getFacesForPhoto(photo.id); "SELECT photos.*,albums.path AS path FROM photos " +
photo.faces = faces; "INNER JOIN albums ON albums.id=photos.albumId " +
return res.status(200).json(photo); "WHERE photos.duplicate=0 AND photos.deleted=0 AND photos.scanned NOT NULL AND photos.id=:id", {
}); replacements: {
id: id,
},
type: photoDB.Sequelize.QueryTypes.SELECT,
raw: true
});
}).then(function(photos) {
if (!photos.length) {
return res.status(404).send({ message: id + " not found." });
}
const photo = photos[0];
for (var key in photo) {
if (photo[key] instanceof Date) {
photo[key] = moment(photo[key]);
}
}
return getFacesForPhoto(photo.id).then((faces) => {
photo.faces = faces;
return res.status(200).json(photo);
})
});
})
router.get("/mvimg/*", function(req, res/*, next*/) { router.get("/mvimg/*", function(req, res/*, next*/) {
let limit = parseInt(req.query.limit) || 50, let limit = parseInt(req.query.limit) || 50,