diff --git a/README.md b/README.md index 9966a10..f74e256 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,12 @@ $ ./.venv/Scripts/activate(.bat/.ps1) $ pip install -r ./requirements.txt ``` +### Fix relative imports +1. Install the local package as editable using `pip`: +```sh +$ pip install -e . +``` + ### Create a dataset 1. Rename all images to include a tag and unique id, seperated by an underscore '_' - e.g. `accasia_1210262` @@ -84,6 +90,9 @@ $ python ./src/helpers/test/knn.py -i ./out/result-(date/time).csv -o ./out/mode ``` 4. Edit your `config.json` to include the newly created model +> :memo: **Please note:**
+> The KNN Training script also generates the scaler required to make the decision tree model + --- Arne van Iterson
diff --git a/requirements.txt b/requirements.txt index 56caf5e..3bb7058 100644 Binary files a/requirements.txt and b/requirements.txt differ diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..6f05aa2 --- /dev/null +++ b/setup.py @@ -0,0 +1,2 @@ +from setuptools import setup, find_packages +setup(name='CVSuite', version='1.0', packages=find_packages()) \ No newline at end of file diff --git a/src/helpers/__init__.py b/src/helpers/__init__.py new file mode 100644 index 0000000..8b32d5c --- /dev/null +++ b/src/helpers/__init__.py @@ -0,0 +1,2 @@ +from .logger import * +from .tags import * \ No newline at end of file diff --git a/src/helpers/test/decision_tree.py b/src/helpers/test/decision_tree.py index 4bede1d..05209e9 100644 --- a/src/helpers/test/decision_tree.py +++ b/src/helpers/test/decision_tree.py @@ -7,27 +7,14 @@ import csv import argparse import os +from ..tags import Tree + parser = argparse.ArgumentParser(prog='DecisionTree CLI') parser.add_argument('-i', '--input', help='Input CSV file', required=True) parser.add_argument('-o', '--output', help='Output model folder', required=True) -parser.add_argument( - '-m', - '--model', - help='Chosen model (\'dectree\', \'randforest\' or \'extratree\')', - required=True - ) +parser.add_argument('-m', '--model', help='Chosen model (\'dectree\', \'randforest\' or \'extratree\')', required=True) parser.add_argument('-s', '--scaler', help='Scaler preprocesser', required=True) -class Tree(Enum): - ACCASIA = 0 - BERK = 1 - EIK = 2 - ELS = 3 - ESDOORN = 4 - ES = 5 - LINDE = 6 - PLATAAN = 7 - class CVSuiteTestTree: def __init__(self, model_path = None): self.model_path = model_path @@ -40,12 +27,11 @@ class CVSuiteTestTree: reader = csv.reader(file, delimiter= ',') matrix = list(reader) - i = 0 labels = [] data = [[] for x in range(len(matrix)-1)] # Load all but the headers - for row in matrix[1:]: + for ridx, row in enumerate(matrix[1:]): ## append data to lists label = row.pop(0).upper() labels.append(Tree[label].value) @@ -54,12 +40,12 @@ class CVSuiteTestTree: row.pop(0) # append all but ID and tree - for element in row: - data[i].append(float(element)) - i += 1 + for idx, element in enumerate(row): + value = self.scaler[idx].transform([[float(element)]]) + data[ridx].append(value[0][0]) - # normalize data - #TODO: Arne help + # normalize data has been included in code above :D + #TODO: Check if data is normalized correctly # train model self.train(data, labels, output) diff --git a/src/helpers/test/knn.py b/src/helpers/test/knn.py index c5c1969..6620578 100644 --- a/src/helpers/test/knn.py +++ b/src/helpers/test/knn.py @@ -9,20 +9,13 @@ import joblib import datetime import os +from ..logger import C_DBUG +from ..tags import Tree + parser = argparse.ArgumentParser(prog='KNN Train CLI') parser.add_argument('-i', '--input', help='Input CSV file', required=True) parser.add_argument('-o', '--output', help='Output model file', required=True) -class Tree(Enum): - ACCASIA = 0 - BERK = 1 - EIK = 2 - ELS = 3 - ESDOORN = 4 - ES = 5 - LINDE = 6 - PLATAAN = 7 - class CVSuiteTestKNN: def __init__(self, model = None): self.scale = [] @@ -113,7 +106,12 @@ class CVSuiteTestKNN: self.knn.save(os.path.join(output, F"model_knn_{now.strftime('%Y-%m-%dT%H.%M.%S')}.yaml")) def predict(self, data, nr = 3): - return self.knn.findNearest(data, nr) + ret, results, neighbours ,dist = self.knn.findNearest(data, nr) + print(C_DBUG, "KNN Raw:") + print("\t\tresult: \t{}".format(results) ) + print("\t\tneighbours:\t{}".format(neighbours) ) + print("\t\tdistance:\t{}".format(dist) ) + return neighbours[0] if __name__ == "__main__": args = parser.parse_args() diff --git a/src/suite.py b/src/suite.py index c2b2629..24401ec 100644 --- a/src/suite.py +++ b/src/suite.py @@ -28,14 +28,13 @@ import matplotlib.pyplot as plt # Helpers from helpers.statistics import imgStats -from helpers.logger import CVSuiteLogger, C_DBUG, C_WARN +from helpers.logger import CVSuiteLogger, C_INFO, C_DBUG, C_WARN, C_DONE from helpers.canvas import CVSuiteCanvas from helpers.sift import getSiftData from helpers.tags import Tree # Tests from helpers.test.knn import CVSuiteTestKNN - from helpers.test.decision_tree import CVSuiteTestDecisionTree ## UI config load @@ -48,10 +47,18 @@ CONFIG_PATH = "./src/config/config.json" config_file = open(CONFIG_PATH, encoding="utf-8") config_json = json.load(config_file) +print( + f""" +Welcome to CVSuite aka {TITLE}! +Powered by ARNweb.nl and TomSelier.com +""" +) + ## UI class setup class CVSuite: def __init__(self, master=None): + ### UI setup ### # Pygubu builder self.builder = builder = pygubu.Builder() builder.add_resource_path(PROJECT_PATH) @@ -102,28 +109,45 @@ class CVSuite: ) builder.connect_callbacks(self) + # Load values from config after UI has been initialised + self.img_path.set(config_json["path"]) + self.img_size.set(config_json["size"]) + + ### End of UI setup ### + + ### Model tests setup ### # Attempt to load scaler if config_json["scaler"] != "": self.scaler = joblib.load(config_json["scaler"]) else: self.scaler = None - # Model tests - if self.scaler is not None and config_json["models"]["knn"] != "": - self.test_knn = CVSuiteTestKNN(config_json["models"]["knn"]) - else: - self.test_knn = None + self.models = [] + if self.scaler is not None: + for model in config_json["models"]: + if config_json["models"][model] != "": + print(C_INFO, f"Loading model {model}") + if model == "knn": + self.models.append( + ("KNN", CVSuiteTestKNN(config_json["models"]["knn"])) + ) + elif model == "dectree": + self.models.append( + ( + "Decision Tree", + CVSuiteTestDecisionTree( + config_json["models"]["dectree"] + ), + ) + ) + else: + print(C_WARN, f"Model {model} does not exist!") + else: + print(C_WARN, f"Model {model} not configured!") + print(C_DONE, f"{len(self.models)} models loaded!") - if config_json["models"]["dectree"] != "": - self.test_dectree = CVSuiteTestDecisionTree( - config_json["models"]["dectree"] - ) - else: - self.test_dectree = None - - # Load values from config after UI has been initialised - self.img_path.set(config_json["path"]) - self.img_size.set(config_json["size"]) + print(C_DONE, "CVSuite initialised!\n") + ### End of model tests setup ### def on_quit(self, event=None): """ @@ -311,7 +335,7 @@ class CVSuite: # Don't run the test if there's no scaler if self.scaler is None: return - + output = self.builder.get_object("testdata") output.configure(state="normal") output.delete(1.0, "end") @@ -319,42 +343,26 @@ class CVSuite: # Remove tag and photoId tag = data.pop(0) photoId = data.pop(1) - - # Add actual name - output.insert("end", f"Actual:\n\t{tag.upper()}\n") + + # Add actual name + output.insert("end", f"Actual type:\n\t{tag.upper()}\n") # Normalise data using loaded scalers for idx, value in enumerate(data): d = np.array(value) data[idx] = self.scaler[idx].transform(d.astype(np.float32).reshape(1, -1))[0][0] - + data = np.array([data], dtype=np.float32) - - if self.test_knn is not None: - # Do knn test - output.insert("end", "KNN Result:\n") - ret, results, neighbours ,dist = self.test_knn.predict(data) + for name, ins in self.models: + output.insert("end", f"{name} Result:\n") - for idx, res_id in enumerate(neighbours[0]): - output.insert("end", f" {idx}:\t{Tree(res_id).name}\n") - - print(C_DBUG, "KNN Result:") - print("\t\tresult: \t{}".format(results) ) - print("\t\tneighbours:\t{}".format(neighbours) ) - print("\t\tdistance:\t{}".format(dist) ) - else: - print(C_WARN, "KNN Model not configured!") + # Predict result using model instance + result = ins.predict(data) - if self.test_dectree is not None: - result = self.test_dectree.predict(data) - output.insert("end", "Decision Tree Result:\n") - output.insert("end", f"\t{Tree(result).name}\n") - - print(C_DBUG, "Decision Tree Result:") - print("\t\t result: \t{}".format(result)) - else: - print(C_WARN, "Decison Tree Model not configured!") + # Prediciton result should be an array + for idx, value in enumerate(result): + output.insert("end", f" [{idx + 1}]\t{Tree(value).name}\n") output.configure(state="disabled") @@ -381,9 +389,8 @@ class CVSuite: def update(self, event=None, part_update=None): ## Check if hist and canny hm have to be rerendered - if ( - part_update is None - ): ## If partial update has not been forced, check if full update is required + if part_update is None: + ## If partial update has not been forced, check if full update is required if self.img_current != self.img_old or self.img_size != self.img_size_old: part_update = False self.img_old = self.img_current @@ -397,7 +404,7 @@ class CVSuite: print("Full update forced!") if self.updatePath(): - print(C_DBUG, f"Processing {self.img_name}") + print(C_INFO, f"Processing {self.img_name}") self.mainwindow.title(f"{TITLE} - {self.img_name}") self.log.add("Tree", self.img_name.split("_")[0])