Преглед изворни кода

Merge branch '70-fit-with-new-data' into 'master'

Resolve "fit with new data"

Closes #11 and #70

See merge request troebs/pycs!66
Eric Tröbs пре 4 година
родитељ
комит
08b52ca69b

+ 2 - 0
pycs/frontend/WebServer.py

@@ -77,6 +77,8 @@ class WebServer:
 
             if 'delete' in data.keys():
                 app_status['projects'].delete_project(identifier)
+            elif 'fit' in data.keys():
+                app_status['projects'].fit(identifier)
             elif 'predict' in data.keys():
                 app_status['projects'].predict(identifier, data['predict'])
             else:

+ 4 - 0
pycs/pipeline/Job.py

@@ -7,6 +7,10 @@ class Job:
         self.id = uuid1()
         self.type = type
         self.object_id = data['id']
+        self.object_relative_path = path.join('projects',
+                                              project_id,
+                                              'data',
+                                              data['id'] + data['extension'])
         self.object_full_path = path.join(getcwd(),
                                           'projects',
                                           project_id,

+ 4 - 0
pycs/pipeline/Pipeline.py

@@ -29,3 +29,7 @@ class Pipeline:
         :return:
         """
         raise NotImplementedError
+
+    # TODO documentation
+    def fit(self, data):
+        raise NotImplementedError

+ 47 - 66
pycs/pipeline/PipelineManager.py

@@ -19,8 +19,8 @@ class PipelineManager:
         self.pipeline = cl(project['model']['path'], project['model'])
 
     def close(self):
-        self.pipeline.close()
         print('PipelineManager', 'close')
+        self.pipeline.close()
 
     def run(self, media_file):
         # create job list
@@ -35,68 +35,49 @@ class PipelineManager:
         for prediction in result.predictions:
             media_file.add_result(prediction, origin='pipeline')
 
-    '''
-    def __load_pipeline(self, pipeline_identifier):
-        model_distribution = self.project.parent.parent['models'][pipeline_identifier]
-
-        if model_distribution['mode'] == 'tf1':
-            model_root = path.join(getcwd(), 'models', model_distribution['name'])
-
-            #pipeline = TF1Pipeline()
-            #pipeline.load(model_root, model_distribution['pipeline'])
-
-            #return pipeline
-    '''
-
-    '''
-    def __update(self, data):
-        # get current project path
-        opened_projects = list(filter(lambda x: x['status'] == 'open', data))
-        if len(opened_projects) == 0:
-            return
-
-        current_project = opened_projects[0]
-
-        # find images to predict
-        if 'data' not in current_project.keys() or len(current_project['data']) == 0:
-            return
-
-        # load pipeline
-        pipeline = tpool.execute(self.__load_pipeline, current_project['pipeline']['model-distribution'])
-
-        # create job list
-        for d in current_project['data']:
-            print('keys:', d.keys())
-            if 'result' not in d.keys():
-                # TODO update job progress
-                job = Job('detect-faces', current_project['id'], d)
-                result = tpool.execute(lambda p, j: p.execute(j), pipeline, job)
-                d['result'] = result.predictions
-
-        # close pipeline
-        pipeline.close()
-    '''
-
-    '''
-    def __update(self, data):
-        for current_project in data:
-            print('>>>>>>>>>>')
-            # find images to predict
-            if 'data' not in current_project.keys() or len(current_project['data']) == 0:
-                return
-
-            # load pipeline
-            pipeline = tpool.execute(self.__load_pipeline, current_project['pipeline']['model-distribution'])
-
-            # create job list
-            for d in current_project['data']:
-                print('keys:', d.keys())
-                if 'result' not in d.keys():
-                    # TODO update job progress
-                    job = Job('detect-faces', current_project['id'], d)
-                    result = tpool.execute(lambda p, j: p.execute(j), pipeline, job)
-                    d['result'] = result.predictions
-
-            # close pipeline
-            pipeline.close()
-    '''
+    def fit(self):
+        print('PipelineManager', 'fit')
+
+        data = []
+        for identifier in self.project['data']:
+            obj = self.project['data'][identifier]
+            media = {
+                'name': obj['name'],
+                'extension': obj['extension'],
+                'size': obj['size'],
+                'path': path.join('projects', self.project['id'], 'data', identifier + obj['extension']),
+                'predictionResults': []
+            }
+
+            for prediction_identifier in obj['predictionResults']:
+                prediction = obj['predictionResults'][prediction_identifier]
+                if prediction['origin'] != 'user':
+                    continue
+
+                if 'x' not in prediction:
+                    media['predictionResults'].append({
+                        'type': 'labeled-image',
+                        'label': prediction['label']
+                    })
+                else:
+                    if 'label' in prediction:
+                        media['predictionResults'].append({
+                            'type': 'labeled-bounding-box',
+                            'x': prediction['x'],
+                            'y': prediction['y'],
+                            'w': prediction['w'],
+                            'h': prediction['h'],
+                            'label': prediction['label']
+                        })
+                    else:
+                        media['predictionResults'].append({
+                            'type': 'bounding-box',
+                            'x': prediction['x'],
+                            'y': prediction['y'],
+                            'w': prediction['w'],
+                            'h': prediction['h']
+                        })
+
+            data.append(media)
+
+        self.pipeline.fit(data)

+ 27 - 0
pycs/projects/ProjectManager.py

@@ -118,6 +118,33 @@ class ProjectManager(ObservableDict):
         # schedule timeout thread
         self.quit_pipeline_thread = spawn_after(self.DEFAULT_PIPELINE_TIMEOUT, self.__quit_pipeline)
 
+    def fit(self, uuid):
+        # abort if uuid is no valid key
+        if uuid not in self.keys():
+            return
+
+        project = self[uuid]
+
+        # abort pipeline termination
+        if self.quit_pipeline_thread is not None:
+            self.quit_pipeline_thread.cancel()
+            self.quit_pipeline_thread = None
+
+        # create pipeline if it does not exist already
+        if self.pipeline_manager is None:
+            self.pipeline_manager = PipelineManager(project)
+
+        # run fit
+        self.pipeline_manager.fit()
+
+        # quit timeout thread
+        if self.quit_pipeline_thread is not None:
+            self.quit_pipeline_thread.cancel()
+            self.quit_pipeline_thread = None
+
+        # schedule timeout thread
+        self.quit_pipeline_thread = spawn_after(self.DEFAULT_PIPELINE_TIMEOUT, self.__quit_pipeline)
+
     def __quit_pipeline(self):
         if self.pipeline_manager is not None:
             self.pipeline_manager.close()

+ 6 - 0
webui/src/App.vue

@@ -45,6 +45,10 @@
                                   :status="status"
                                   :socket="socket"/>
 
+        <project-model-interaction-window v-if="window.content === 'model_interaction'"
+                                          :current-project="currentProject"
+                                          :socket="socket"/>
+
         <about-window v-if="window.content === 'about'"/>
       </div>
     </div>
@@ -62,10 +66,12 @@ import ProjectDataAddWindow from "@/components/projects/project-data-add-window"
 import ProjectDataViewWindow from "@/components/projects/project-data-view-window";
 import LoadingOverlay from "@/components/other/loading-overlay";
 import ProjectLabelsWindow from "@/components/projects/project-labels-window";
+import ProjectModelInteractionWindow from "@/components/projects/project-model-interaction-window";
 
 export default {
   name: 'App',
   components: {
+    ProjectModelInteractionWindow,
     ProjectLabelsWindow,
     LoadingOverlay,
     ProjectDataAddWindow,

+ 6 - 0
webui/src/assets/icons/cpu.svg

@@ -0,0 +1,6 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
+    <path fill-rule="evenodd"
+          d="M8.75 8a.75.75 0 00-.75.75v6.5c0 .414.336.75.75.75h6.5a.75.75 0 00.75-.75v-6.5a.75.75 0 00-.75-.75h-6.5zm.75 6.5v-5h5v5h-5z"></path>
+    <path fill-rule="evenodd"
+          d="M15.25 1a.75.75 0 01.75.75V4h2.25c.966 0 1.75.784 1.75 1.75V8h2.25a.75.75 0 010 1.5H20v5h2.25a.75.75 0 010 1.5H20v2.25A1.75 1.75 0 0118.25 20H16v2.25a.75.75 0 01-1.5 0V20h-5v2.25a.75.75 0 01-1.5 0V20H5.75A1.75 1.75 0 014 18.25V16H1.75a.75.75 0 010-1.5H4v-5H1.75a.75.75 0 010-1.5H4V5.75C4 4.784 4.784 4 5.75 4H8V1.75a.75.75 0 011.5 0V4h5V1.75a.75.75 0 01.75-.75zm3 17.5a.25.25 0 00.25-.25V5.75a.25.25 0 00-.25-.25H5.75a.25.25 0 00-.25.25v12.5c0 .138.112.25.25.25h12.5z"></path>
+</svg>

+ 1 - 0
webui/src/components/projects/project-creation-window.vue

@@ -72,6 +72,7 @@ export default {
         'bounding-boxes': 'bounding boxes',
         'labeled-bounding-boxes': 'labeled bounding boxes',
         'labeled-images': 'labeled images',
+        'fit': 'fit new data'
       };
       let result = [];
 

+ 28 - 0
webui/src/components/projects/project-model-interaction-window.vue

@@ -0,0 +1,28 @@
+<template>
+  <div class="project-model-interaction-window">
+    <button-input type="primary"
+                  @click="fit">fit model with new data
+    </button-input>
+  </div>
+</template>
+
+<script>
+import ButtonInput from "@/components/base/button-input";
+
+export default {
+  name: "project-model-interaction-window",
+  components: {ButtonInput},
+  props: ['currentProject', 'socket'],
+  methods: {
+    fit: function () {
+      this.socket.post('/projects/' + this.currentProject.id, {'fit': true});
+    }
+  }
+}
+</script>
+
+<style scoped>
+.project-model-interaction-window {
+  padding: 1rem;
+}
+</style>

+ 7 - 0
webui/src/components/window/side-navigation-bar.vue

@@ -42,6 +42,13 @@
         <span>View Data</span>
       </div>
 
+      <div class="item"
+           :class="{active: window.content === 'model_interaction', inactive: !currentProject || !mediaAvailable}"
+           @click="ifProjectIsOpened(show, 'model_interaction')">
+        <img src="@/assets/icons/cpu.svg">
+        <span>Model</span>
+      </div>
+
       <div class="item"
            :class="{active: window.content === 'about'}"
            @click="show('about')">