Bläddra i källkod

Added photo module exclude path and model selector

Toby Chui 3 år sedan
förälder
incheckning
5510a3679b

+ 1 - 1
web/Photo/backend/classify.js

@@ -73,7 +73,7 @@ function main(){
             //Check if this filepath is already been classified.
             if (typeof(filepathMap[thisImageFile]) == "undefined"){
                 //Not found in cache. New photo! 
-                console.log("[Photo] Analysising photo with neuralnet: " + thisImageFile);
+                console.log("[Photo] Analysising photo with " + getNNModel() + ": " + thisImageFile);
                 var thisImageTagRecord = getImageTagsRecord(thisImageFile);
                 if (websocketMode){
                     websocket.send(JSON.stringify(thisImageFile));

+ 31 - 0
web/Photo/backend/exclude.js

@@ -0,0 +1,31 @@
+/*
+    Exclude.js
+
+    Handle read write of the excluded dirs function
+    Excluded dirs shd be start with base folder name, for example
+    for excluding user:/Photo/Manga/, just enter Manga
+
+    Required paramters:
+    set 
+    folders (Required when set=true)
+*/
+
+includes("imagedb.js");
+
+function GetExcludeFolders(){
+    return getExcludeFolders();
+}
+
+function SetExcludeFolders(folders){
+    setExcludeFolders(folders);
+    sendJSONResp(JSON.stringify("OK"));
+}
+
+if (typeof(set) == "undefined"){
+    //Get
+    var excludeFolders = GetExcludeFolders();
+    sendJSONResp(excludeFolders);
+}else{
+    //Set
+    SetExcludeFolders(folders);
+}

+ 34 - 2
web/Photo/backend/imagedb.js

@@ -60,9 +60,41 @@ function inCacheFolder(filename){
     return false;
 }
 
+function getExcludeFolders(){
+    newDBTableIfNotExists("image");
+    var excludeList = readDBItem("image", "excludes");
+    if (excludeList == ""){
+        //Initialize the records
+        writeDBItem("image", "excludes", JSON.stringify(["Manga","thumbnails"]));
+        return ["Manga","thumbnails"];
+    }else{
+       return JSON.parse(excludeList);
+    }
+}
+
+function setExcludeFolders(newFolderList){
+    newDBTableIfNotExists("image");
+    return writeDBItem("image", "excludes", JSON.stringify(newFolderList));
+}
+
+function getNNModel(){
+    newDBTableIfNotExists("image");
+    var nnmodel = readDBItem("image", "nnm");
+    if (nnmodel == ""){
+        return "yolo3";
+    }else{
+        return nnmodel;
+    }
+}
+
+function setNNModel(newNNM){
+    newDBTableIfNotExists("image");
+    return writeDBItem("image", "nnm", newNNM);
+}
+
 //Check if this photo shd be rendered
 function checkIsInExcludeFolders(filename){
-    var excludeRootFolders = ["Manga", "tmp", "thumbnail"];
+    var excludeRootFolders = getExcludeFolders();
     var pathinfo = filename.split("/");
     if (pathinfo.length > 2){
         var basefolder = pathinfo[2];
@@ -120,7 +152,7 @@ function getAllImageFilesInRoot(targetRootID){
 
 //Get the tag of a certain image file given its filepath
 function getImageTags(imagefile){
-    var results = imagelib.classify(imagefile, "yolo3"); 
+    var results = imagelib.classify(imagefile, getNNModel()); 
     var tags = [];
     for (var i = 0; i < results.length; i++){
         console.log(results[i].Name, results[i].Percentage);

+ 27 - 0
web/Photo/backend/modelSelector.js

@@ -0,0 +1,27 @@
+/*
+    modelSelector.js
+
+    Handle neural network selection
+    Require paramters:
+    set
+    model (Require when set = true)
+*/
+includes("imagedb.js");
+
+
+function GetCurrentModel(){
+    return getNNModel();
+}
+
+function SetClassifyModel(newModel){
+    setNNModel(newModel);
+    sendJSONResp(JSON.stringify("OK"));
+}
+
+if (typeof(set) == "undefined"){
+    //Get
+    sendJSONResp(JSON.stringify(GetCurrentModel()));
+}else{
+    //Set
+    SetClassifyModel(model);
+}

+ 1 - 0
web/Photo/img/collection.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 0 24 24" width="48px" fill="#000000"><path d="M0 0h24v24H0V0z" fill="none"/><path d="M20 4v12H8V4h12m0-2H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-8.5 9.67l1.69 2.26 2.48-3.1L19 15H9zM2 6v14c0 1.1.9 2 2 2h14v-2H4V6H2z"/></svg>

+ 67 - 5
web/Photo/index.html

@@ -89,7 +89,7 @@
 </head>
 <body>
     <script src="photo.js"></script>
-    <div id="main" x-data='photoListObject()'>
+    <div id="main" x-data='photoListObject()' x-init="init()">
         <div class="ui left vertical menu" id="menu">
             <div class="item" style="background-color: #f76c5d;">
                 <img class="ui fluid image" src="img/banner.png">
@@ -125,13 +125,15 @@
                             <a class="item" x-on:click="console.log( tags[key][0],tags[key][1]); images = tags[key]; $('#noimg').hide();" :filedata="encodeURIComponent(JSON.stringify(tags[key]));" ><i class="ui hashtag icon"></i> <span x-text="key"></span></a>
                         </template>
                     </div>
-                    <a class="item" onclick="rescan(this);">Rescan <i class="ui search icon"></i></a>
+                    
                 </div>
             </div>
+            <a class="item" onclick="rescan(this);">Rescan <i class="ui search icon"></i></a>
+            <a class="item" onclick="showSetting();">Settings <i class="ui setting icon"></i></a>
 
         </div>
 
-        <div id="display" x-init="init()">
+        <div id="display">
             <div id="noimg" class="ui basic segment" style="display:none;">
                 <h4 class="ui header">
                     <i class="red question icon"></i>
@@ -153,16 +155,76 @@
                 </div>
             </div>
         </div>
+
+        
     </div>
     <button id="showmenuBtn" class="ui basic big icon button" onclick="toggleMenu();"><i class="ui content icon"></i></button>
 
     <!-- Photo Viewing Modal -->
-    <div class="ui basic small modal">
+    <div class="ui basic small modal viewer">
         <i class="close icon" style="margin-top: -2em;"></i>
         <div class="image content">
-            <img id="fullImage" class="ui fluid image" src="" onload="$('.ui.modal').modal('refresh');"/>
+            <img id="fullImage" class="ui fluid image" src="" onload="$('.ui.modal.viewer').modal('refresh');"/>
         </div>
     </div>
+
+    <div class="ui modal setting" x-data="settingObject()" x-init="init();">
+        <i class="close icon"></i>
+        <div class="header">
+            Photo Classification Settings
+        </div>
+        <div class="content">
+            <p><i class="angle right icon"></i> Select Neural Network Model for Classification</p>
+            <div class="ui selection dropdown" id="selectedModel" onchange="updateSelectedModel($(this).dropdown('get value'));">
+                <input type="hidden" name="nnm">
+                <i class="dropdown icon"></i>
+                <div class="default text">default</div>
+                <div class="menu">
+                    <div class="item" data-value="yolo3">YOLO 3</div>
+                    <div class="item" data-value="darknet19">Darknet 19</div>
+                </div>
+            </div>
+            <div class="ui green message" id="modelUpdated" style="display:none;">
+                <div class="content">
+                    <i class="green small checkmark icon"></i>   Model Updated
+                </div>
+            </div>
+            <br>
+            <small><i class="ui info icon"></i> For host with less than 1GB free memory, Darknet 19 is recommend. However, this might result is less accurate classification predictions.</small>
+            <div class="ui divider"></div>
+            <p><i class="angle right icon"></i> Base folder name to exclude when auto-tag generaton</p>
+            <div id="newexc" class="ui action fluid input">
+                <input type="text" placeholder="Private">
+                <button class="ui blue basic button" x-on:click="addDir($el);"><i class="ui add icon"></i> Add</button>
+              </div>
+              <small>Example: To exclude *:/Photo/Private/, just fill in the folder name "Private"</small>
+                <div class="ui middle aligned relaxed divided list">
+                    <div class="item" id="noexcRecords" style="display:none;"><i class="remove icon"></i> No Exclude Directory Record</div>
+
+                    <template x-for="excludedir in excludeDirs">
+                        <div class="item">
+                            <div class="right floated content">
+                                <div class="ui negative small basic button" x-on:click="removeDir(excludedir);"><i class="remove icon"></i> Remove</div>
+                            </div>
+                            <img class="ui avatar image" src="./img/collection.svg">
+                            <div class="content" x-text="excludedir + ' (Exclude *:/Photo/' + excludedir + '/)'">
+                                
+                            </div>
+                        </div>
+                    </template>
+              </div>
+        </div>
+        <div class="actions">
+            <div class="ui deny right button">
+                    Close
+            </div>
+            <div class="ui positive left labeled icon button" x-on:click="save();">
+                    Save
+                <i class="checkmark icon"></i>
+            </div>
+        </div>
+    </div>
+
 </body>
 <script>
 

+ 115 - 2
web/Photo/photo.js

@@ -9,6 +9,7 @@
 let photoList = [];
 let prePhoto = "";
 let nextPhoto = "";
+let currentModel = "";
 
 function scrollbarVisable(){
     return $("body")[0].scrollHeight > $("body").height();
@@ -56,6 +57,115 @@ function extractFolderName(folderpath){
     return folderpath.split("/").pop();
 }
 
+function initSelectedModelValue(){
+    ao_module_agirun("Photo/backend/modelSelector.js",{
+
+    }, function(data){
+        currentModel = data;
+        $("#selectedModel").dropdown("set selected", data);
+    })
+}
+
+function updateSelectedModel(nnnm){
+    if (nnnm != ""){
+        ao_module_agirun("Photo/backend/modelSelector.js",{
+            set: true,
+            model: nnnm
+        }, function(data){
+           console.log("Update model status: ", data);
+           $("#modelUpdated").slideDown("fast").delay(3000).slideUp('fast');
+        })
+    }
+}
+
+function settingObject(){
+    return {
+        excludeDirs: ["Manga","thumbnail"],
+
+        init(){
+            this.loadExcludeList();
+            $('#newexc input').off("keypress").on("keypress",function (e) {
+                if (e.which == 13) {
+                    addDir($('#newexc input'));
+                }
+            });
+            
+            $(".ui.dropdown").dropdown();
+            initSelectedModelValue();
+        },
+
+        addDir(element){
+            var dir = $(element).parent().find("input").val();
+            $("#newexc").removeClass("error");
+            for (var i = 0; i < this.excludeDirs.length; i++){
+                if (this.excludeDirs[i] == dir){
+                    //Already exists
+                    $("#newexc").addClass("error");
+                    return;
+                }
+            }
+
+            this.excludeDirs.push(dir);
+            $('#newexc input').val("");
+        },
+
+        removeDir(filepath){
+            //Pop the given filepath from list
+            var newExcludeDirs = [];
+            for ( var i = 0; i < this.excludeDirs.length; i++){
+                var thisExcludeDir = this.excludeDirs[i];
+                if (thisExcludeDir != filepath){
+                    newExcludeDirs.push(thisExcludeDir);
+                }
+            }
+
+            this.excludeDirs = newExcludeDirs;
+        },
+
+        loadExcludeList(){
+            $("#noexcRecords").hide();
+            fetch(ao_root + "system/ajgi/interface?script=Photo/backend/exclude.js", {
+                method: 'POST',
+                cache: 'no-cache',
+                headers: {
+                  'Content-Type': 'application/json'
+                },
+                body: JSON.stringify({
+                    
+                })
+            }).then(resp => {
+                resp.json().then(data => {
+                    console.log(data);
+                    this.excludeDirs = data;
+                    if (data.length == 0){
+                        $("#noexcRecords").show();
+                    }
+                })
+            });
+        },
+
+
+
+        save(){
+            fetch(ao_root + "system/ajgi/interface?script=Photo/backend/exclude.js", {
+                method: 'POST',
+                cache: 'no-cache',
+                headers: {
+                  'Content-Type': 'application/json'
+                },
+                body: JSON.stringify({
+                    "set":true,
+                    "folders": this.excludeDirs
+                })
+            }).then(resp => {
+                resp.json().then(data => {
+
+                })
+            });
+        }
+    }
+}
+
 function photoListObject() {
     return {
         // data
@@ -73,7 +183,6 @@ function photoListObject() {
             this.getRootInfo();
             this.renderSize = getImageWidth();
             updateImageSizes();
-            
         },
         
         updateRenderingPath(newPath){
@@ -173,7 +282,7 @@ function renderImageList(object){
 }
 
 function ShowModal(){
-    $('.ui.modal').modal({
+    $('.ui.modal.viewer').modal({
         onHide: function(){
             ao_module_setWindowTitle("Photo");
         }
@@ -218,6 +327,10 @@ $(document).on("keydown", function(e){
     }
 })
 
+function showSetting(){
+    $('.ui.modal.setting').modal('show');
+}
+
 function rescan(object){
     var originalContent = $(object).html();
     $(object).addClass("disabled");