diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e69de29 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3266b95 --- /dev/null +++ b/.gitignore @@ -0,0 +1,10 @@ +.venv/ +.vscode/ +__pycache__/ +dataset/ +gradient_maps/ +models/ +transform/ +weights/ +logs/ +work/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..08bd2c7 --- /dev/null +++ b/README.md @@ -0,0 +1,86 @@ +# DifferNet +This project is used for experiment to train and test models on various datasets. The core function has been packaged as "[differnet-zerobox](https://github.com/zerobox-ai/pydiffernet)". Pleaser refer to the [readme](https://github.com/zerobox-ai/pydiffernet/blob/master/README.md) for how to use the package. + +If you need more information about DifferNet, please reference to the official repository. + +**Differnet Officical repository** +The [official repository](https://github.com/marco-rudolph/differnet) to the WACV 2021 paper "[Same Same But DifferNet: Semi-Supervised Defect Detection with Normalizing Flows]( +https://arxiv.org/abs/2008.12577)" by Marco Rudolph, Bastian Wandt and Bodo Rosenhahn. + +## Getting Started + +The project has been upgraded to python 3.9. Please setup python 3.9 virtual environment then do the following. + +### Install torch and torch vision +In order to have proper torch and torch vision to use either GPU or CPU please follow [pytorch.org](https://pytorch.org/get-started/locally/) to install torch and torch vision + +## Install rest packages with: + +``` +$ pip install -r requirements.txt +``` + +## Configure and Run + +All configuration has default values from package differnet(from package differnet-zerobox). +The project can have dict based configuration to overwrite any default value. + + +Common settings +``` +#conf/settings.conf +"differnet_work_dir": "./work", #work folder +"device": "cuda", # cuda or cpu +"device_id": 0, # the device you want to use. depends on how many GPU or CPU you have. +"verbose": True, # Set to true, when you do experiments. +"meta_epochs": 10, # traing loop +"sub_epochs": 8, # sub-loop of traing +"test_anormaly_target": 10, # threshold when run testing model to identify if a given image is good or bad + +``` + +Traing + +``` +python training.py +``` + +Run test cases +``` +python -m pytest -s +``` + +## Prepare new dataset +The data structure under work folder looks like this. The model folder will save trained model. +For experiment purpose, you would like to give test and validate folder with proper labled data. While, for zerobox +it only requires train folder and data. The minimum images is 16 based on the differnet paper. + +``` +pink1/ +├── model +├── test +│   ├── defect +│   └── good +├─── validate +│ ├── defect +│ └── good +└── train + └── good + ├── 01.jpg + ├── 02.jpg + ├── 03.jpg + ├── 04.jpg + ├── 05.jpg + ├── 06.jpg + ├── 07.jpg + ├── 08.jpg + ├── 09.jpg + ├── 10.jpg + ├── 11.jpg + ├── 12.jpg + ├── 13.jpg + ├── 14.jpg + ├── 15.jpg + └── 16.jpg +``` + diff --git a/Readme.md b/Readme.md deleted file mode 100644 index c614482..0000000 --- a/Readme.md +++ /dev/null @@ -1,92 +0,0 @@ -# DifferNet - -This is the official repository to the WACV 2021 paper "[Same Same But DifferNet: Semi-Supervised Defect Detection with Normalizing Flows]( -https://arxiv.org/abs/2008.12577)" by Marco Rudolph, Bastian Wandt and Bodo Rosenhahn. - -If the only reason you ended up here is because you made a typo on 'different' - what was our intention - here is a shortened summary: We introduce a method that is able to find anomalies like defects on image data without having some of them in the training set. - - -[![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/same-same-but-differnet-semi-supervised/anomaly-detection-on-mvtec-ad)](https://paperswithcode.com/sota/anomaly-detection-on-mvtec-ad?p=same-same-but-differnet-semi-supervised) - -## Getting Started - -You will need [Python 3.6](https://www.python.org/downloads) and the packages specified in _requirements.txt_. -We recommend setting up a [virtual environment with pip](https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/) -and installing the packages there. - -Install packages with: - -``` -$ pip install -r requirements.txt -``` - -## Configure and Run - -All configurations concerning data, model, training, visualization etc. can be made in _config.py_. The default configuration will run a training with paper-given parameters on the provided dummy dataset. This dataset contains images of 4 squares as normal examples and 4 circles as anomaly. - -To start the training, just run _main.py_! If training on the dummy data does not lead to an AUROC of 1.0, something seems to be wrong. -Please report us if you have issues when using the code. - -## Data - -The given dummy dataset shows how the implementation expects the construction of a dataset. Coincidentally, the [MVTec AD dataset](https://www.mvtec.com/de/unternehmen/forschung/datasets/mvtec-ad/) is constructed in this way. - -Set the variables _dataset_path_ and _class_name_ in _config.py_ to run experiments on a dataset of your choice. The expected structure of the data is as follows: - -``` -train data: - - dataset_path/class_name/train/good/any_filename.png - dataset_path/class_name/train/good/another_filename.tif - dataset_path/class_name/train/good/xyz.png - [...] - -test data: - - 'normal data' = non-anomalies - - dataset_path/class_name/test/good/name_the_file_as_you_like_as_long_as_there_is_an_image_extension.webp - dataset_path/class_name/test/good/did_you_know_the_image_extension_webp?.png - dataset_path/class_name/test/good/did_you_know_that_filenames_may_contain_question_marks????.png - dataset_path/class_name/test/good/dont_know_how_it_is_with_windows.png - dataset_path/class_name/test/good/just_dont_use_windows_for_this.png - [...] - - anomalies - assume there are anomaly classes 'crack' and 'curved' - - dataset_path/class_name/test/crack/dat_crack_damn.png - dataset_path/class_name/test/crack/let_it_crack.png - dataset_path/class_name/test/crack/writing_docs_is_fun.png - [...] - - dataset_path/class_name/test/curved/wont_make_a_difference_if_you_put_all_anomalies_in_one_class.png - dataset_path/class_name/test/curved/but_this_code_is_practicable_for_the_mvtec_dataset.png - [...] -``` - -## Credits - -Some code of the [FrEIA framework](https://github.com/VLL-HD/FrEIA) was used for the implementation of Normalizing Flows. Follow [their tutorial](https://github.com/VLL-HD/FrEIA) if you need more documentation about it. - - -## Citation -Please cite our paper in your publications if it helps your research. Even if it does not, you are welcome to cite us. - - @inproceedings { RudWan2021, - author = {Marco Rudolph and Bastian Wandt and Bodo Rosenhahn}, - title = {Same Same But DifferNet: Semi-Supervised Defect Detection with Normalizing Flows}, - booktitle = {Winter Conference on Applications of Computer Vision (WACV)}, - year = {2021}, - month = jan - } - -Another paper link because you missed the first one: - -* [Same Same But DifferNet: Semi-Supervised Defect Detection with Normalizing Flows]( -https://arxiv.org/abs/2008.12577) - -## License - -This project is licensed under the MIT License. - - diff --git a/conf/__init__.py b/conf/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/conf/settings.py b/conf/settings.py new file mode 100644 index 0000000..588fe34 --- /dev/null +++ b/conf/settings.py @@ -0,0 +1,88 @@ +DIFFERNET_CONF = { + # common settings + "differnet_work_dir": "./work", + "device": "cuda", # cuda or cpu + "device_id": 5, + "verbose": True, + "meta_epochs": 5, + "sub_epochs": 8, + "test_anormaly_target": 10, + # other settings + # "img_size": [448, 448], + # "img_dims": [3, 448, 448], + # "add_img_noise": 0.01, + # # transformation settings + # "transf_rotations": False, + # "transf_brightness": 0.5, + # "transf_contrast": 0.5, + # "transf_saturation": 0.5, + # "norm_mean": [0.485, 0.456, 0.406], + # "norm_std": [0.229, 0.224, 0.225], + # "rotation_degree": 0, + # "crop_top": 0.10, + # "crop_left": 0.10, + # "crop_bottom": 0.10, + # "crop_right": 0.10, + # network hyperparameters + # number of scales at which features are extracted, img_size is the highest - others are //2, //4,... + # "n_scales": 3, + # "clamp_alpha": 3, # see paper equation 2 for explanation + # "n_coupling_blocks": 8, + # # fc_internal : 2048 # number of neurons in hidden layers of s-t-networks + # "fc_internal": 1536, # number of neurons in hidden layers of s-t-networks + # "dropout": 0.0, # dropout in s-t-networks + # "lr_init": 2e-4, + # "n_feat": 256 * 3, # do not change except you change the feature extractor + # # dataloader parameters + # "n_transforms": 4, # number of transformations per sample in training + # "n_transforms_test": 1, # number of transformations per sample in testing + # # actual batch size is this value multiplied by n_transforms(_test) + # "batch_size": 4, + # "batch_size_test": 1, + # total epochs : meta_epochs * sub_epochs + # evaluation after epochs + # output settings + # "grad_map_viz": True, + # "hide_tqdm_bar": True, + # "save_model": True, + # "save_transformed_image": True, + # "visualization": False, + # "frame_name_is_given": False, + # "target_tpr": 0.85, +} + + +LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "formatters": { + "verbose": { + "format": "%(levelname)s %(asctime)s %(module)s " + "%(process)d %(thread)d %(message)s" + } + }, + "handlers": { + "console": { + # "level": "DEBUG", + "class": "logging.StreamHandler", + "formatter": "verbose", + }, + # "file": { + # # 'level': 'DEBUG', + # "class": "logging.handlers.RotatingFileHandler", + # "filename": "logs/app.log", + # "formatter": "verbose", + # "maxBytes": 1024 * 1024 * 1, # 10MB + # "backupCount": 5, + # "formatter": "verbose", + # }, + }, + "loggers": { + "differnet": { + "handlers": ["console"], + "level": "DEBUG", + "propagate": False, + }, + }, + "root": {"level": "INFO", "handlers": ["console"]}, +} diff --git a/config.py b/config.py deleted file mode 100644 index a650dba..0000000 --- a/config.py +++ /dev/null @@ -1,49 +0,0 @@ -'''This file configures the training procedure because handling arguments in every single function is so exhaustive for -research purposes. Don't try this code if you are a software engineer.''' - -# device settings -device = 'cuda' # or 'cpu' -import torch -torch.cuda.set_device(0) - -# data settings -dataset_path = "dummy_dataset" -class_name = "dummy_class" -modelname = "dummy_test" - -img_size = (448, 448) -img_dims = [3] + list(img_size) -add_img_noise = 0.01 - -# transformation settings -transf_rotations = True -transf_brightness = 0.0 -transf_contrast = 0.0 -transf_saturation = 0.0 -norm_mean, norm_std = [0.485, 0.456, 0.406], [0.229, 0.224, 0.225] - -# network hyperparameters -n_scales = 3 # number of scales at which features are extracted, img_size is the highest - others are //2, //4,... -clamp_alpha = 3 # see paper equation 2 for explanation -n_coupling_blocks = 8 -fc_internal = 2048 # number of neurons in hidden layers of s-t-networks -dropout = 0.0 # dropout in s-t-networks -lr_init = 2e-4 -n_feat = 256 * n_scales # do not change except you change the feature extractor - -# dataloader parameters -n_transforms = 4 # number of transformations per sample in training -n_transforms_test = 64 # number of transformations per sample in testing -batch_size = 24 # actual batch size is this value multiplied by n_transforms(_test) -batch_size_test = batch_size * n_transforms // n_transforms_test - -# total epochs = meta_epochs * sub_epochs -# evaluation after epochs -meta_epochs = 1 -sub_epochs = 8 - -# output settings -verbose = True -grad_map_viz = True -hide_tqdm_bar = True -save_model = True diff --git a/dummy_dataset/dummy_class/test/anomaly/.directory b/dummy_dataset/dummy_class/test/anomaly/.directory deleted file mode 100644 index f6612a0..0000000 --- a/dummy_dataset/dummy_class/test/anomaly/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2020,8,11,12,21,46 -Version=4 -ViewMode=1 diff --git a/dummy_dataset/dummy_class/test/anomaly/1.png b/dummy_dataset/dummy_class/test/anomaly/1.png deleted file mode 100644 index 6ffcb2e..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/1.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/2.png b/dummy_dataset/dummy_class/test/anomaly/2.png deleted file mode 100644 index 450487a..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/2.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/3png.png b/dummy_dataset/dummy_class/test/anomaly/3png.png deleted file mode 100644 index 2792af3..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/3png.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/4.png b/dummy_dataset/dummy_class/test/anomaly/4.png deleted file mode 100644 index 81a0a57..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/4.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/5.png b/dummy_dataset/dummy_class/test/anomaly/5.png deleted file mode 100644 index 7618a74..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/5.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/6.png b/dummy_dataset/dummy_class/test/anomaly/6.png deleted file mode 100644 index 8102b18..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/6.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/7.png b/dummy_dataset/dummy_class/test/anomaly/7.png deleted file mode 100644 index d96effd..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/7.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/anomaly/8png.png b/dummy_dataset/dummy_class/test/anomaly/8png.png deleted file mode 100644 index eff138a..0000000 Binary files a/dummy_dataset/dummy_class/test/anomaly/8png.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/.directory b/dummy_dataset/dummy_class/test/good/.directory deleted file mode 100644 index 7e2424b..0000000 --- a/dummy_dataset/dummy_class/test/good/.directory +++ /dev/null @@ -1,4 +0,0 @@ -[Dolphin] -Timestamp=2020,8,11,12,21,40 -Version=4 -ViewMode=1 diff --git a/dummy_dataset/dummy_class/test/good/1.png b/dummy_dataset/dummy_class/test/good/1.png deleted file mode 100644 index db14910..0000000 Binary files a/dummy_dataset/dummy_class/test/good/1.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/2.png b/dummy_dataset/dummy_class/test/good/2.png deleted file mode 100644 index 2b1ff9e..0000000 Binary files a/dummy_dataset/dummy_class/test/good/2.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/3.png b/dummy_dataset/dummy_class/test/good/3.png deleted file mode 100644 index bfb748b..0000000 Binary files a/dummy_dataset/dummy_class/test/good/3.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/4.png b/dummy_dataset/dummy_class/test/good/4.png deleted file mode 100644 index 832f728..0000000 Binary files a/dummy_dataset/dummy_class/test/good/4.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/5.png b/dummy_dataset/dummy_class/test/good/5.png deleted file mode 100644 index 827f634..0000000 Binary files a/dummy_dataset/dummy_class/test/good/5.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/6.png b/dummy_dataset/dummy_class/test/good/6.png deleted file mode 100644 index bdb8653..0000000 Binary files a/dummy_dataset/dummy_class/test/good/6.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/7.png b/dummy_dataset/dummy_class/test/good/7.png deleted file mode 100644 index a30dd34..0000000 Binary files a/dummy_dataset/dummy_class/test/good/7.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/test/good/8.png b/dummy_dataset/dummy_class/test/good/8.png deleted file mode 100644 index 81e3d58..0000000 Binary files a/dummy_dataset/dummy_class/test/good/8.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/train/good/1.png b/dummy_dataset/dummy_class/train/good/1.png deleted file mode 100644 index 961c90e..0000000 Binary files a/dummy_dataset/dummy_class/train/good/1.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/train/good/2.png b/dummy_dataset/dummy_class/train/good/2.png deleted file mode 100644 index 90b0a7a..0000000 Binary files a/dummy_dataset/dummy_class/train/good/2.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/train/good/3.png b/dummy_dataset/dummy_class/train/good/3.png deleted file mode 100644 index 65ac750..0000000 Binary files a/dummy_dataset/dummy_class/train/good/3.png and /dev/null differ diff --git a/dummy_dataset/dummy_class/train/good/4.png b/dummy_dataset/dummy_class/train/good/4.png deleted file mode 100644 index 4913141..0000000 Binary files a/dummy_dataset/dummy_class/train/good/4.png and /dev/null differ diff --git a/freia_funcs.py b/freia_funcs.py deleted file mode 100644 index 24947f8..0000000 --- a/freia_funcs.py +++ /dev/null @@ -1,473 +0,0 @@ -'''This Code is based on the FrEIA Framework, source: https://github.com/VLL-HD/FrEIA -It is a assembly of the necessary modules/functions from FrEIA that are needed for our purposes.''' -import torch -import torch.nn as nn -import torch.nn.functional as F -from torch.autograd import Variable -from math import exp -import numpy as np -VERBOSE = False - - -class dummy_data: - def __init__(self, *dims): - self.dims = dims - - @property - def shape(self): - return self.dims - -class F_fully_connected(nn.Module): - '''Fully connected tranformation, not reversible, but used below.''' - - def __init__(self, size_in, size, internal_size=None, dropout=0.0): - super(F_fully_connected, self).__init__() - if not internal_size: - internal_size = 2*size - - self.d1 = nn.Dropout(p=dropout) - self.d2 = nn.Dropout(p=dropout) - self.d2b = nn.Dropout(p=dropout) - - self.fc1 = nn.Linear(size_in, internal_size) - self.fc2 = nn.Linear(internal_size, internal_size) - self.fc2b = nn.Linear(internal_size, internal_size) - self.fc3 = nn.Linear(internal_size, size) - - self.nl1 = nn.ReLU() - self.nl2 = nn.ReLU() - self.nl2b = nn.ReLU() - - self.bn = nn.BatchNorm1d(size_in) - - - def forward(self, x): - out = self.nl1(self.d1(self.fc1(x))) - out = self.nl2(self.d2(self.fc2(out))) - out = self.nl2b(self.d2b(self.fc2b(out))) - out = self.fc3(out) - return out - -class permute_layer(nn.Module): - '''permutes input vector in a random but fixed way''' - - def __init__(self, dims_in, seed): - super(permute_layer, self).__init__() - self.in_channels = dims_in[0][0] - - np.random.seed(seed) - self.perm = np.random.permutation(self.in_channels) - np.random.seed() - - self.perm_inv = np.zeros_like(self.perm) - for i, p in enumerate(self.perm): - self.perm_inv[p] = i - - self.perm = torch.LongTensor(self.perm) - self.perm_inv = torch.LongTensor(self.perm_inv) - - def forward(self, x, rev=False): - if not rev: - return [x[0][:, self.perm]] - else: - return [x[0][:, self.perm_inv]] - - def jacobian(self, x, rev=False): - # TODO: use batch size, set as nn.Parameter so cuda() works - return 0. - - def output_dims(self, input_dims): - assert len(input_dims) == 1, "Can only use 1 input" - return input_dims - - - -class glow_coupling_layer(nn.Module): - def __init__(self, dims_in, F_class=F_fully_connected, F_args={}, - clamp=5.): - super(glow_coupling_layer, self).__init__() - channels = dims_in[0][0] - self.ndims = len(dims_in[0]) - - self.split_len1 = channels // 2 - self.split_len2 = channels - channels // 2 - - self.clamp = clamp - self.max_s = exp(clamp) - self.min_s = exp(-clamp) - - self.s1 = F_class(self.split_len1, self.split_len2*2, **F_args) - self.s2 = F_class(self.split_len2, self.split_len1*2, **F_args) - - def e(self, s): - return torch.exp(self.log_e(s)) - - def log_e(self, s): - return self.clamp * 0.636 * torch.atan(s / self.clamp) - - def forward(self, x, rev=False): - x1, x2 = (x[0].narrow(1, 0, self.split_len1), - x[0].narrow(1, self.split_len1, self.split_len2)) - - if not rev: - r2 = self.s2(x2) - s2, t2 = r2[:, :self.split_len1], r2[:, self.split_len1:] - #print(s2.shape, x1.shape, t2.shape) - y1 = self.e(s2) * x1 + t2 - - r1 = self.s1(y1) - s1, t1 = r1[:, :self.split_len2], r1[:, self.split_len2:] - y2 = self.e(s1) * x2 + t1 - - else: # names of x and y are swapped! - r1 = self.s1(x1) - s1, t1 = r1[:, :self.split_len2], r1[:, self.split_len2:] - y2 = (x2 - t1) / self.e(s1) - - r2 = self.s2(y2) - s2, t2 = r2[:, :self.split_len1], r2[:, self.split_len1:] - y1 = (x1 - t2) / self.e(s2) - y = torch.cat((y1, y2), 1) - y = torch.clamp(y, -1e6, 1e6) - return [y] - - def jacobian(self, x, rev=False): - x1, x2 = (x[0].narrow(1, 0, self.split_len1), - x[0].narrow(1, self.split_len1, self.split_len2)) - - if not rev: - r2 = self.s2(x2) - s2, t2 = r2[:, :self.split_len1], r2[:, self.split_len1:] - y1 = self.e(s2) * x1 + t2 - - r1 = self.s1(y1) - s1, t1 = r1[:, :self.split_len2], r1[:, self.split_len2:] - - else: # names of x and y are swapped! - r1 = self.s1(x1) - s1, t1 = r1[:, :self.split_len2], r1[:, self.split_len2:] - y2 = (x2 - t1) / self.e(s1) - - r2 = self.s2(y2) - s2, t2 = r2[:, :self.split_len1], r2[:, self.split_len1:] - - jac = (torch.sum(self.log_e(s1), dim=1) - + torch.sum(self.log_e(s2), dim=1)) - for i in range(self.ndims-1): - jac = torch.sum(jac, dim=1) - - return jac - - def output_dims(self, input_dims): - assert len(input_dims) == 1, "Can only use 1 input" - return input_dims - -class Node: - '''The Node class represents one transformation in the graph, with an - arbitrary number of in- and outputs.''' - def __init__(self, inputs, module_type, module_args, name=None): - self.inputs = inputs - self.outputs = [] - self.module_type = module_type - self.module_args = module_args - - self.input_dims, self.module = None, None - self.computed = None - self.computed_rev = None - self.id = None - - if name: - self.name = name - else: - self.name = hex(id(self))[-6:] - for i in range(255): - exec('self.out{0} = (self, {0})'.format(i)) - - def build_modules(self, verbose=VERBOSE): - ''' Returns a list with the dimension of each output of this node, - recursively calling build_modules of the nodes connected to the input. - Use this information to initialize the pytorch nn.Module of this node. - ''' - - if not self.input_dims: # Only do it if this hasn't been computed yet - self.input_dims = [n.build_modules(verbose=verbose)[c] - for n, c in self.inputs] - try: - self.module = self.module_type(self.input_dims, - **self.module_args) - except Exception as e: - print('Error in node %s' % (self.name)) - raise e - - if verbose: - print("Node %s has following input dimensions:" % (self.name)) - for d, (n, c) in zip(self.input_dims, self.inputs): - print("\t Output #%i of node %s:" % (c, n.name), d) - print() - - self.output_dims = self.module.output_dims(self.input_dims) - self.n_outputs = len(self.output_dims) - - return self.output_dims - - def run_forward(self, op_list): - '''Determine the order of operations needed to reach this node. Calls - run_forward of parent nodes recursively. Each operation is appended to - the global list op_list, in the form (node ID, input variable IDs, - output variable IDs)''' - - if not self.computed: - - # Compute all nodes which provide inputs, filter out the - # channels you need - self.input_vars = [] - for i, (n, c) in enumerate(self.inputs): - self.input_vars.append(n.run_forward(op_list)[c]) - # Register youself as an output in the input node - n.outputs.append((self, i)) - - # All outputs could now be computed - self.computed = [(self.id, i) for i in range(self.n_outputs)] - op_list.append((self.id, self.input_vars, self.computed)) - - # Return the variables you have computed (this happens mulitple times - # without recomputing if called repeatedly) - return self.computed - - def run_backward(self, op_list): - '''See run_forward, this is the same, only for the reverse computation. - Need to call run_forward first, otherwise this function will not - work''' - - assert len(self.outputs) > 0, "Call run_forward first" - if not self.computed_rev: - - # These are the input variables that must be computed first - output_vars = [(self.id, i) for i in range(self.n_outputs)] - - # Recursively compute these - for n, c in self.outputs: - n.run_backward(op_list) - - # The variables that this node computes are the input variables - # from the forward pass - self.computed_rev = self.input_vars - op_list.append((self.id, output_vars, self.computed_rev)) - - return self.computed_rev - - -class InputNode(Node): - '''Special type of node that represents the input data of the whole net (or - ouput when running reverse)''' - - def __init__(self, *dims, name='node'): - self.name = name - self.data = dummy_data(*dims) - self.outputs = [] - self.module = None - self.computed_rev = None - self.n_outputs = 1 - self.input_vars = [] - self.out0 = (self, 0) - - def build_modules(self, verbose=VERBOSE): - return [self.data.shape] - - def run_forward(self, op_list): - return [(self.id, 0)] - - -class OutputNode(Node): - '''Special type of node that represents the output of the whole net (of the - input when running in reverse)''' - class dummy(nn.Module): - - def __init__(self, *args): - super(OutputNode.dummy, self).__init__() - - def __call__(*args): - return args - - def output_dims(*args): - return args - - def __init__(self, inputs, name='node'): - self.module_type, self.module_args = self.dummy, {} - self.output_dims = [] - self.inputs = inputs - self.input_dims, self.module = None, None - self.computed = None - self.id = None - self.name = name - - for c, inp in enumerate(self.inputs): - inp[0].outputs.append((self, c)) - - def run_backward(self, op_list): - return [(self.id, 0)] - - -class ReversibleGraphNet(nn.Module): - '''This class represents the invertible net itself. It is a subclass of - torch.nn.Module and supports the same methods. The forward method has an - additional option 'rev', whith which the net can be computed in reverse.''' - - def __init__(self, node_list, ind_in=None, ind_out=None, verbose=False): - '''node_list should be a list of all nodes involved, and ind_in, - ind_out are the indexes of the special nodes InputNode and OutputNode - in this list.''' - super(ReversibleGraphNet, self).__init__() - - # Gather lists of input and output nodes - if ind_in is not None: - if isinstance(ind_in, int): - self.ind_in = list([ind_in]) - else: - self.ind_in = ind_in - else: - self.ind_in = [i for i in range(len(node_list)) - if isinstance(node_list[i], InputNode)] - assert len(self.ind_in) > 0, "No input nodes specified." - if ind_out is not None: - if isinstance(ind_out, int): - self.ind_out = list([ind_out]) - else: - self.ind_out = ind_out - else: - self.ind_out = [i for i in range(len(node_list)) - if isinstance(node_list[i], OutputNode)] - assert len(self.ind_out) > 0, "No output nodes specified." - - self.return_vars = [] - self.input_vars = [] - - # Assign each node a unique ID - self.node_list = node_list - for i, n in enumerate(node_list): - n.id = i - - # Recursively build the nodes nn.Modules and determine order of - # operations - ops = [] - for i in self.ind_out: - node_list[i].build_modules(verbose=verbose) - node_list[i].run_forward(ops) - - # create list of Pytorch variables that are used - variables = set() - for o in ops: - variables = variables.union(set(o[1] + o[2])) - self.variables_ind = list(variables) - - self.indexed_ops = self.ops_to_indexed(ops) - - self.module_list = nn.ModuleList([n.module for n in node_list]) - self.variable_list = [Variable(requires_grad=True) for v in variables] - - # Find out the order of operations for reverse calculations - ops_rev = [] - for i in self.ind_in: - node_list[i].run_backward(ops_rev) - self.indexed_ops_rev = self.ops_to_indexed(ops_rev) - - def ops_to_indexed(self, ops): - '''Helper function to translate the list of variables (origin ID, channel), - to variable IDs.''' - result = [] - - for o in ops: - try: - vars_in = [self.variables_ind.index(v) for v in o[1]] - except ValueError: - vars_in = -1 - - vars_out = [self.variables_ind.index(v) for v in o[2]] - - # Collect input/output nodes in separate lists, but don't add to - # indexed ops - if o[0] in self.ind_out: - self.return_vars.append(self.variables_ind.index(o[1][0])) - continue - if o[0] in self.ind_in: - self.input_vars.append(self.variables_ind.index(o[1][0])) - continue - - result.append((o[0], vars_in, vars_out)) - - # Sort input/output variables so they correspond to initial node list - # order - self.return_vars.sort(key=lambda i: self.variables_ind[i][0]) - self.input_vars.sort(key=lambda i: self.variables_ind[i][0]) - - return result - - def forward(self, x, rev=False): - '''Forward or backward computation of the whole net.''' - if rev: - use_list = self.indexed_ops_rev - input_vars, output_vars = self.return_vars, self.input_vars - else: - use_list = self.indexed_ops - input_vars, output_vars = self.input_vars, self.return_vars - - if isinstance(x, (list, tuple)): - assert len(x) == len(input_vars), ( - f"Got list of {len(x)} input tensors for " - f"{'inverse' if rev else 'forward'} pass, but expected " - f"{len(input_vars)}." - ) - for i in range(len(input_vars)): - self.variable_list[input_vars[i]] = x[i] - else: - assert len(input_vars) == 1, (f"Got single input tensor for " - f"{'inverse' if rev else 'forward'} " - f"pass, but expected list of " - f"{len(input_vars)}.") - self.variable_list[input_vars[0]] = x - - for o in use_list: - try: - results = self.module_list[o[0]]([self.variable_list[i] - for i in o[1]], rev=rev) - except TypeError: - raise RuntimeError("Are you sure all used Nodes are in the " - "Node list?") - for i, r in zip(o[2], results): - self.variable_list[i] = r - # self.variable_list[o[2][0]] = self.variable_list[o[1][0]] - - out = [self.variable_list[output_vars[i]] - for i in range(len(output_vars))] - if len(out) == 1: - return out[0] - else: - return out - - def jacobian(self, x=None, rev=False, run_forward=True): - '''Compute the jacobian determinant of the whole net.''' - jacobian = 0 - - if rev: - use_list = self.indexed_ops_rev - else: - use_list = self.indexed_ops - - if run_forward: - if x is None: - raise RuntimeError("You need to provide an input if you want " - "to run a forward pass") - self.forward(x, rev=rev) - jacobian_list = list() - for o in use_list: - try: - node_jac = self.module_list[o[0]].jacobian( - [self.variable_list[i] for i in o[1]], rev=rev - ) - jacobian += node_jac - jacobian_list.append(jacobian) - except TypeError: - raise RuntimeError("Are you sure all used Nodes are in the " - "Node list?") - - return jacobian \ No newline at end of file diff --git a/logs/readme.md b/logs/readme.md new file mode 100644 index 0000000..e332fb6 --- /dev/null +++ b/logs/readme.md @@ -0,0 +1 @@ +place holder \ No newline at end of file diff --git a/main.py b/main.py deleted file mode 100644 index 9fac6ca..0000000 --- a/main.py +++ /dev/null @@ -1,12 +0,0 @@ -'''This is the repo which contains the original code to the WACV 2021 paper -"Same Same But DifferNet: Semi-Supervised Defect Detection with Normalizing Flows" -by Marco Rudolph, Bastian Wandt and Bodo Rosenhahn. -For further information contact Marco Rudolph (rudolph@tnt.uni-hannover.de)''' - -import config as c -from train import train -from utils import load_datasets, make_dataloaders - -train_set, test_set = load_datasets(c.dataset_path, c.class_name) -train_loader, test_loader = make_dataloaders(train_set, test_set) -model = train(train_loader, test_loader) diff --git a/model.py b/model.py deleted file mode 100644 index 89f7594..0000000 --- a/model.py +++ /dev/null @@ -1,70 +0,0 @@ -import numpy as np -import os -import torch -import torch.nn.functional as F -from torch import nn -from torchvision.models import alexnet - -import config as c -from freia_funcs import permute_layer, glow_coupling_layer, F_fully_connected, ReversibleGraphNet, OutputNode, \ - InputNode, Node - -WEIGHT_DIR = './weights' -MODEL_DIR = './models' - - -def nf_head(input_dim=c.n_feat): - nodes = list() - nodes.append(InputNode(input_dim, name='input')) - for k in range(c.n_coupling_blocks): - nodes.append(Node([nodes[-1].out0], permute_layer, {'seed': k}, name=F'permute_{k}')) - nodes.append(Node([nodes[-1].out0], glow_coupling_layer, - {'clamp': c.clamp_alpha, 'F_class': F_fully_connected, - 'F_args': {'internal_size': c.fc_internal, 'dropout': c.dropout}}, - name=F'fc_{k}')) - nodes.append(OutputNode([nodes[-1].out0], name='output')) - coder = ReversibleGraphNet(nodes) - return coder - - -class DifferNet(nn.Module): - def __init__(self): - super(DifferNet, self).__init__() - self.feature_extractor = alexnet(pretrained=True) - self.nf = nf_head() - - def forward(self, x): - y_cat = list() - - for s in range(c.n_scales): - x_scaled = F.interpolate(x, size=c.img_size[0] // (2 ** s)) if s > 0 else x - feat_s = self.feature_extractor.features(x_scaled) - y_cat.append(torch.mean(feat_s, dim=(2, 3))) - - y = torch.cat(y_cat, dim=1) - z = self.nf(y) - return z - - -def save_model(model, filename): - if not os.path.exists(MODEL_DIR): - os.makedirs(MODEL_DIR) - torch.save(model, os.path.join(MODEL_DIR, filename)) - - -def load_model(filename): - path = os.path.join(MODEL_DIR, filename) - model = torch.load(path) - return model - - -def save_weights(model, filename): - if not os.path.exists(WEIGHT_DIR): - os.makedirs(WEIGHT_DIR) - torch.save(model.state_dict(), os.path.join(WEIGHT_DIR, filename)) - - -def load_weights(model, filename): - path = os.path.join(WEIGHT_DIR, filename) - model.load_state_dict(torch.load(path)) - return model diff --git a/multi_transform_loader.py b/multi_transform_loader.py deleted file mode 100644 index edfb5b8..0000000 --- a/multi_transform_loader.py +++ /dev/null @@ -1,71 +0,0 @@ -import torch -import torchvision.transforms as transforms -from torchvision.datasets import DatasetFolder -from torchvision.datasets.folder import make_dataset, pil_loader, default_loader, IMG_EXTENSIONS -from torchvision.transforms.functional import rotate - -import config as c - - -def fixed_rotation(self, sample, degrees): - cust_rot = lambda x: rotate(x, degrees, False, False, None) - augmentative_transforms = [cust_rot] - if c.transf_brightness > 0.0 or c.transf_contrast > 0.0 or c.transf_saturation > 0.0: - augmentative_transforms += [ - transforms.ColorJitter(brightness=c.transf_brightness, contrast=c.transf_contrast, - saturation=c.transf_saturation)] - tfs = [transforms.Resize(c.img_size)] + augmentative_transforms + [transforms.ToTensor(), - transforms.Normalize(c.norm_mean, - c.norm_std)] - return transforms.Compose(tfs)(sample) - - -class DatasetFolderMultiTransform(DatasetFolder): - """Adapts class DatasetFolder of PyTorch in a way that one sample is transformed several times. - Args: - n_transforms (int): number of transformations per sample - all others: see torchvision.datasets.DatasetFolder - """ - - def __init__(self, root, loader, extensions=None, transform=None, - target_transform=None, is_valid_file=None, n_transforms=1): - super(DatasetFolderMultiTransform, self).__init__(root, loader, extensions=extensions, transform=transform, - target_transform=target_transform) - classes, class_to_idx = self._find_classes(self.root) - if is_valid_file is not None: - extensions = None - self.samples = make_dataset(self.root, class_to_idx, extensions) - self.n_transforms = n_transforms - self.get_fixed = False # set to true if the rotations should be fixed and regularly over 360 degrees - self.fixed_degrees = [i * 360.0 / n_transforms for i in range(n_transforms)] - - def __getitem__(self, index): - path, target = self.samples[index] - sample = self.loader(path) - if self.transform is not None: - samples = list() - for i in range(self.n_transforms): - if self.get_fixed: - samples.append(fixed_rotation(self, sample, self.fixed_degrees[i])) - else: - samples.append(self.transform(sample)) - samples = torch.stack(samples, dim=0) - if self.target_transform is not None: - target = self.target_transform(target) - return samples, target - - -class ImageFolderMultiTransform(DatasetFolderMultiTransform): - """Adapts class ImageFolder of PyTorch in a way that one sample can be transformed several times. - Args: - n_transforms (int): number of transformations per sample - all others: see ImageFolder - """ - - def __init__(self, root, transform=None, target_transform=None, - loader=default_loader, is_valid_file=None, n_transforms=c.n_transforms): - super(ImageFolderMultiTransform, self).__init__(root, loader, IMG_EXTENSIONS, - transform=transform, - target_transform=target_transform, - is_valid_file=is_valid_file, n_transforms=n_transforms) - self.imgs = self.samples diff --git a/requirements.txt b/requirements.txt index eb100f1..b0fee8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,9 @@ -scikit-learn>=0.22 -scipy>=1.3.2 -numpy>=1.17.4 -torch>=1.00 -torchvision>=0.2.2 -matplotlib>=3.0.3 -tqdm>=4.40.2 +differnet-zerobox +# scikit-learn>=0.22 +# scipy>=1.3.2 +# numpy>=1.17.4 +# torch==1.8.1 +# torchvision==0.9.1 +# matplotlib>=3.0.3 +# tqdm>=4.59.2 +# opencv-python>=4.5.1 \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..4794e49 --- /dev/null +++ b/test.sh @@ -0,0 +1,10 @@ +# important: +# don't use pytest directly. +# use python -m pytest, otherwise you might run into wrong pytest version + +# run all test case with same method +# python -m pytest -s -k test_model + +# run all test cases +python -m pytest -s + diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.pyc b/tests/__init__.pyc new file mode 100644 index 0000000..a8c0001 Binary files /dev/null and b/tests/__init__.pyc differ diff --git a/tests/test_differnet.py b/tests/test_differnet.py new file mode 100644 index 0000000..f4c0b59 --- /dev/null +++ b/tests/test_differnet.py @@ -0,0 +1,174 @@ +import time +import logging.config +import cv2 +import os +from differnet.differnet_util import DiffernetUtil +import time +from conf.settings import LOGGING, DIFFERNET_CONF + +# import logging +logging.config.dictConfig(LOGGING) +logger = logging.getLogger(__name__) + +# https://www.django-rest-framework.org/api-guide/testing/#apiclient + + +class Test_A1_Training: + """ + BottleCategory test cases, use this one as template + """ + + def test_train(self): + """Test training + use nvidia-smi -i 5 -l 5 to monitor GPU + -i GPU device number -l interval to refresh + """ + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + differnetutil = DiffernetUtil(conf, "black1") + + # train the model + differnetutil.train_model() + + # # test trained model + # differnetutil.test_model() + + def test_model(self): + """Test training""" + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + differnetutil = DiffernetUtil(conf, "black1") + + t1 = time.process_time() + + # test trained model + differnetutil.test_model() + + t2 = time.process_time() + elapsed_time = t2 - t1 + logger.info(f"elapsed time: {elapsed_time}") + + def test_detect(self): + """Test Detection""" + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + t0 = time.process_time() + differnetutil = DiffernetUtil(conf, "black1") + differnetutil.load_model() + t1 = time.process_time() + + elapsed_time = t1 - t0 + logger.info(f"Model load elapsed time: {elapsed_time}") + + img = cv2.imread( + os.path.join( + differnetutil.test_dir, "defect", "Camera0_202009142018586_product.png" + ), + cv2.IMREAD_UNCHANGED, + ) + t1 = time.process_time() + ret = differnetutil.detect(img, 10) + # calculate time + t2 = time.process_time() + elapsed_time = t2 - t1 + logger.info(f"Detection elapsed time: {elapsed_time}") + assert ret == True + + img = cv2.imread( + os.path.join( + differnetutil.test_dir, "good", "Camera0_202009142018133_product.png" + ), + cv2.IMREAD_UNCHANGED, + ) + t2 = time.process_time() + ret = differnetutil.detect(img, 10) + t3 = time.process_time() + elapsed_time = t3 - t2 + logger.info(f"Detection elapsed time: {elapsed_time}") + assert ret == False + + +class Test_B1_Training: + """ + BottleCategory test cases, use this one as template + """ + + def setUp(self): + pass + + def test_train(self): + """Test training + use nvidia-smi -i 5 -l 5 to monitor GPU + -i GPU device number -l interval to refresh + """ + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + differnetutil = DiffernetUtil(conf, "pink1") + + # train the model + differnetutil.train_model(with_validateset=False) + + # # test trained model + # differnetutil.test_model() + + def test_model(self): + """Test training""" + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + differnetutil = DiffernetUtil(conf, "pink1") + + t1 = time.process_time() + + # test trained model + differnetutil.test_model() + + t2 = time.process_time() + elapsed_time = t2 - t1 + logger.info(f"elapsed time: {elapsed_time}") + + def test_detect(self): + """Test Detection""" + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + t0 = time.process_time() + differnetutil = DiffernetUtil(conf, "pink1") + differnetutil.load_model() + t1 = time.process_time() + + elapsed_time = t1 - t0 + logger.info(f"Model load elapsed time: {elapsed_time}") + + img = cv2.imread( + os.path.join(differnetutil.test_dir, "defect", "bad1.jpg"), + cv2.IMREAD_UNCHANGED, + ) + t1 = time.process_time() + ret = differnetutil.detect(img, 10) + # calculate time + t2 = time.process_time() + elapsed_time = t2 - t1 + logger.info(f"Detection elapsed time: {elapsed_time}") + assert ret == True + + img = cv2.imread( + os.path.join(differnetutil.test_dir, "good", "good1.jpg"), + cv2.IMREAD_UNCHANGED, + ) + t2 = time.process_time() + ret = differnetutil.detect(img, 10) + t3 = time.process_time() + elapsed_time = t3 - t2 + logger.info(f"Detection elapsed time: {elapsed_time}") + assert ret == False diff --git a/train.py b/train.py deleted file mode 100644 index f3ebf73..0000000 --- a/train.py +++ /dev/null @@ -1,100 +0,0 @@ -import numpy as np -import torch -from sklearn.metrics import roc_auc_score -from tqdm import tqdm - -import config as c -from localization import export_gradient_maps -from model import DifferNet, save_model, save_weights -from utils import * - - -class Score_Observer: - '''Keeps an eye on the current and highest score so far''' - - def __init__(self, name): - self.name = name - self.max_epoch = 0 - self.max_score = None - self.last = None - - def update(self, score, epoch, print_score=False): - self.last = score - if epoch == 0 or score > self.max_score: - self.max_score = score - self.max_epoch = epoch - if print_score: - self.print_score() - - def print_score(self): - print('{:s}: \t last: {:.4f} \t max: {:.4f} \t epoch_max: {:d}'.format(self.name, self.last, self.max_score, - self.max_epoch)) - - -def train(train_loader, test_loader): - model = DifferNet() - optimizer = torch.optim.Adam(model.nf.parameters(), lr=c.lr_init, betas=(0.8, 0.8), eps=1e-04, weight_decay=1e-5) - model.to(c.device) - - score_obs = Score_Observer('AUROC') - - for epoch in range(c.meta_epochs): - - # train some epochs - model.train() - if c.verbose: - print(F'\nTrain epoch {epoch}') - for sub_epoch in range(c.sub_epochs): - train_loss = list() - for i, data in enumerate(tqdm(train_loader, disable=c.hide_tqdm_bar)): - optimizer.zero_grad() - inputs, labels = preprocess_batch(data) # move to device and reshape - # TODO inspect - # inputs += torch.randn(*inputs.shape).cuda() * c.add_img_noise - - z = model(inputs) - loss = get_loss(z, model.nf.jacobian(run_forward=False)) - train_loss.append(t2np(loss)) - loss.backward() - optimizer.step() - - mean_train_loss = np.mean(train_loss) - if c.verbose: - print('Epoch: {:d}.{:d} \t train loss: {:.4f}'.format(epoch, sub_epoch, mean_train_loss)) - - # evaluate - model.eval() - if c.verbose: - print('\nCompute loss and scores on test set:') - test_loss = list() - test_z = list() - test_labels = list() - with torch.no_grad(): - for i, data in enumerate(tqdm(test_loader, disable=c.hide_tqdm_bar)): - inputs, labels = preprocess_batch(data) - z = model(inputs) - loss = get_loss(z, model.nf.jacobian(run_forward=False)) - test_z.append(z) - test_loss.append(t2np(loss)) - test_labels.append(t2np(labels)) - - test_loss = np.mean(np.array(test_loss)) - if c.verbose: - print('Epoch: {:d} \t test_loss: {:.4f}'.format(epoch, test_loss)) - - test_labels = np.concatenate(test_labels) - is_anomaly = np.array([0 if l == 0 else 1 for l in test_labels]) - - z_grouped = torch.cat(test_z, dim=0).view(-1, c.n_transforms_test, c.n_feat) - anomaly_score = t2np(torch.mean(z_grouped ** 2, dim=(-2, -1))) - score_obs.update(roc_auc_score(is_anomaly, anomaly_score), epoch, - print_score=c.verbose or epoch == c.meta_epochs - 1) - - if c.grad_map_viz: - export_gradient_maps(model, test_loader, optimizer, -1) - - if c.save_model: - model.to('cpu') - save_model(model, c.modelname) - save_weights(model, c.modelname) - return model diff --git a/training.py b/training.py new file mode 100644 index 0000000..3f7e28b --- /dev/null +++ b/training.py @@ -0,0 +1,94 @@ +import time +import logging.config +import cv2 +import os +from differnet.differnet_util import DiffernetUtil +import time +from conf.settings import LOGGING, DIFFERNET_CONF + +# import logging +logging.config.dictConfig(LOGGING) +logger = logging.getLogger(__name__) + +# https://www.django-rest-framework.org/api-guide/testing/#apiclient + + +def test_train(): + """Test training + use nvidia-smi -i 5 -l 5 to monitor GPU + -i GPU device number -l interval to refresh + """ + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + differnetutil = DiffernetUtil(conf, "black1") + + # train the model + differnetutil.train_model() + + +def test_model(): + """Test training""" + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + differnetutil = DiffernetUtil(conf, "black1") + + t1 = time.process_time() + + # test trained model + differnetutil.test_model() + + t2 = time.process_time() + elapsed_time = t2 - t1 + logger.info(f"elapsed time: {elapsed_time}") + + +def test_detect(): + """Test Detection""" + # load cutomized conf + conf = DIFFERNET_CONF + logger.info(f"working folder: {conf.get('differnet_work_dir')}") + + t0 = time.process_time() + differnetutil = DiffernetUtil(conf, "black1") + differnetutil.load_model() + t1 = time.process_time() + + elapsed_time = t1 - t0 + logger.info(f"Model load elapsed time: {elapsed_time}") + + img = cv2.imread( + os.path.join( + differnetutil.test_dir, "defect", "Camera0_202009142018586_product.png" + ), + cv2.IMREAD_UNCHANGED, + ) + t1 = time.process_time() + ret = differnetutil.detect(img, 10) + # calculate time + t2 = time.process_time() + elapsed_time = t2 - t1 + logger.info(f"Detection elapsed time: {elapsed_time}") + assert ret == True + + img = cv2.imread( + os.path.join( + differnetutil.test_dir, "good", "Camera0_202009142018133_product.png" + ), + cv2.IMREAD_UNCHANGED, + ) + t2 = time.process_time() + ret = differnetutil.detect(img, 10) + t3 = time.process_time() + elapsed_time = t3 - t2 + logger.info(f"Detection elapsed time: {elapsed_time}") + assert ret == False + + +if __name__ == "__main__": + test_train() + test_model() + test_detect() \ No newline at end of file diff --git a/unused-scripts/apply_mask.py b/unused-scripts/apply_mask.py new file mode 100644 index 0000000..8a0362d --- /dev/null +++ b/unused-scripts/apply_mask.py @@ -0,0 +1,22 @@ +import cv2 +import os + + +def load_images_from_folder(folder): + images = [] + for filename in os.listdir(folder): + img = cv2.imread(os.path.join(folder, filename)) + if img is not None: + images.append(img) + return images + + +path = 'dataset/Experiment_4.1/validate/good' +mask = cv2.imread(os.path.join('dataset/Mask/', 'Mask_shrink.jpg')) +mask = mask / 255 # make the mask into 0/1 matrix for multiplication +imgs = load_images_from_folder(path) + +for i, img in enumerate(imgs): + img = cv2.resize(img, (400, 700), interpolation=cv2.INTER_AREA) + masked_img = img * mask + cv2.imwrite('dataset/Experiment_5.1/validate/good/good-Masked-' + str(i) + '.jpg', masked_img) diff --git a/unused-scripts/data_extraction.py b/unused-scripts/data_extraction.py new file mode 100644 index 0000000..b0bb99f --- /dev/null +++ b/unused-scripts/data_extraction.py @@ -0,0 +1,74 @@ +import cv2 +from xml.dom import minidom +import config as c + +# Load videos one by one +for i in range(c.num_videos): + print('Data generation on video-' + str(i+1)) + filename = str(i+1) + + # Opens the Video file + cap = cv2.VideoCapture('dataset/data-generation/videos/' + filename + '.avi') + + # Read annotations + annotation = minidom.parse('dataset/data-generation/annotations/' + filename + '.xml') + boxes = annotation.getElementsByTagName('box') + + frameList = [] + labelList = [] + boxesList = [] + + # Store the bounding box info along with frame number info into list + for i in range(boxes.length): + + # make sure not select the bounding box that outside the frame + if (boxes[i].attributes['outside'].value != '1'): + frame = int(boxes[i].attributes['frame'].value) + frameList.append(frame) + + labelList.append(boxes[i].parentNode.attributes['label'].value) + + ytl = int(float(boxes[i].attributes['ytl'].value)) + ybr = int(float(boxes[i].attributes['ybr'].value)) + xtl = int(float(boxes[i].attributes['xtl'].value)) + xbr = int(float(boxes[i].attributes['xbr'].value)) + boxesList.append([ytl, ybr, xtl, xbr]) + + # Set up shrink percentage + shrink_percentage = 0.02 + j = 0 + while(cap.isOpened()): + ret, frame = cap.read() + if(frame is not None and j in frameList): + ytl = boxesList[frameList.index(j)][0] + ybr = boxesList[frameList.index(j)][1] + xtl = boxesList[frameList.index(j)][2] + xbr = boxesList[frameList.index(j)][3] + label = 'good' if labelList[frameList.index(j)] == 'bottle' else 'defect' + + # draw bounding box on original frames + if label != 'defect': + cv2.rectangle(frame, (xtl, ytl), (xbr, ybr), (0, 255, 0), 5) + else: + cv2.rectangle(frame, (xtl, ytl), (xbr, ybr), (0, 0, 255), 5) + #cv2.imshow("Show", frame) + #cv2.waitKey() + #cv2.destroyAllWindows() + # Crop the frames with the bounding box position info + crop_frame = frame[int(ytl*(1+shrink_percentage)):int(ybr*(1-shrink_percentage)), + int(xtl*(1+shrink_percentage)):int(xbr*(1-shrink_percentage))] + + # output file formatting example "video1-frame4-defect.jpg" + print('Successfully generated: ' +c.save_cropped_image_to + label + '/video-' + filename + '-frame' + str(j) + + '-' + label + '.jpg') + cv2.imwrite(c.save_original_image_to + label + '/original-video-' + filename + '-frame' + str(j) + '-' + label + '.jpg', + frame) + cv2.imwrite(c.save_cropped_image_to + label + '/video-' + filename + '-frame' + str(j) + '-' + label + '.jpg', + crop_frame) + + if ret == False: + break + j += 1 + + cap.release() + cv2.destroyAllWindows() diff --git a/unused-scripts/drawMask.py b/unused-scripts/drawMask.py new file mode 100644 index 0000000..fe1f253 --- /dev/null +++ b/unused-scripts/drawMask.py @@ -0,0 +1,62 @@ +import cv2 +import os +import numpy as np + + +def load_images_from_folder(folder): + images = [] + for filename in os.listdir(folder): + image = cv2.imread(os.path.join(folder, filename)) + if image is not None: + images.append(image) + return images + + +imgs = load_images_from_folder('dataset/bgm/') +output_height = 700 +output_width = 400 + +for i, img in enumerate(imgs): + print('Original Dimensions : ', img.shape) + resized = cv2.resize(img, (output_width, output_height), interpolation=cv2.INTER_AREA) + masked = [] + + for r in resized: + new_c = [] + for c in r: + # check if the pixel value is green mask or original image pixels + if True in (abs([120, 255, 155] - c) > [30, 30, 30]): + new_c.append([0, 0, 0]) + else: + # keep the green mask pixels in the img + new_c.append([1, 1, 1]) + masked.append(new_c) + masked = np.array(masked, dtype=np.uint8) + if i == 0: + mask = masked + else: + mask = np.ceil((mask + masked) / 2) + + # output the results of each iteration after mask addition process + #cv2.imwrite('dataset/Mask/Mask-' + str(i) + '.jpg', 255 - (255 * mask)) + #print('Resized Dimensions : ', resized.shape) + +# output final mask addition result +cv2.imwrite('dataset/Mask/Mask.jpg', 255 - (255 * mask)) + +# shrink the mask area +shrink_percentage = 0.1 # shrink the mask by percentage from 4 orientations (top, bottom, left and right) +shrank_height = output_height * (1 - 2 * shrink_percentage) # shrink top and bottom +shrank_width = output_width * (1 - 2 * shrink_percentage) # shrink left and right +shrank_mask = cv2.resize(mask, (int(shrank_width), int(shrank_height)), interpolation=cv2.INTER_AREA) +# extend the border of the shrank_mask +shrank_mask = cv2.copyMakeBorder( + shrank_mask, + top=int(output_height * shrink_percentage), + bottom=int(output_height * shrink_percentage), + left=int(output_width * shrink_percentage), + right=int(output_width * shrink_percentage), + borderType=cv2.BORDER_CONSTANT, + value=[255, 255, 255] +) +cv2.imwrite('dataset/Mask/Mask_shrink.jpg', 255 - (255 * shrank_mask)) diff --git a/unused-scripts/flows.py b/unused-scripts/flows.py new file mode 100644 index 0000000..9af567c --- /dev/null +++ b/unused-scripts/flows.py @@ -0,0 +1,528 @@ +import math +import types + +import numpy as np +import scipy as sp +import scipy.linalg +import torch +import torch.nn as nn +import torch.nn.functional as F + + +def get_mask(in_features, out_features, in_flow_features, mask_type=None): + """ + mask_type: input | None | output + + See Figure 1 for a better illustration: + https://arxiv.org/pdf/1502.03509.pdf + """ + if mask_type == 'input': + in_degrees = torch.arange(in_features) % in_flow_features + else: + in_degrees = torch.arange(in_features) % (in_flow_features - 1) + + if mask_type == 'output': + out_degrees = torch.arange(out_features) % in_flow_features - 1 + else: + out_degrees = torch.arange(out_features) % (in_flow_features - 1) + + return (out_degrees.unsqueeze(-1) >= in_degrees.unsqueeze(0)).float() + + +class MaskedLinear(nn.Module): + def __init__(self, + in_features, + out_features, + mask, + cond_in_features=None, + bias=True): + super(MaskedLinear, self).__init__() + self.linear = nn.Linear(in_features, out_features) + if cond_in_features is not None: + self.cond_linear = nn.Linear( + cond_in_features, out_features, bias=False) + + self.register_buffer('mask', mask) + + def forward(self, inputs, cond_inputs=None): + output = F.linear(inputs, self.linear.weight * self.mask, + self.linear.bias) + if cond_inputs is not None: + output += self.cond_linear(cond_inputs) + return output + + +nn.MaskedLinear = MaskedLinear + + +class MADESplit(nn.Module): + """ An implementation of MADE + (https://arxiv.org/abs/1502.03509). + """ + + def __init__(self, + num_inputs, + num_hidden, + num_cond_inputs=None, + s_act='tanh', + t_act='relu', + pre_exp_tanh=False): + super(MADESplit, self).__init__() + + self.pre_exp_tanh = pre_exp_tanh + + activations = {'relu': nn.ReLU, 'sigmoid': nn.Sigmoid, 'tanh': nn.Tanh} + + input_mask = get_mask(num_inputs, num_hidden, num_inputs, + mask_type='input') + hidden_mask = get_mask(num_hidden, num_hidden, num_inputs) + output_mask = get_mask(num_hidden, num_inputs, num_inputs, + mask_type='output') + + act_func = activations[s_act] + self.s_joiner = nn.MaskedLinear(num_inputs, num_hidden, input_mask, + num_cond_inputs) + + self.s_trunk = nn.Sequential(act_func(), + nn.MaskedLinear(num_hidden, num_hidden, + hidden_mask), act_func(), + nn.MaskedLinear(num_hidden, num_inputs, + output_mask)) + + act_func = activations[t_act] + self.t_joiner = nn.MaskedLinear(num_inputs, num_hidden, input_mask, + num_cond_inputs) + + self.t_trunk = nn.Sequential(act_func(), + nn.MaskedLinear(num_hidden, num_hidden, + hidden_mask), act_func(), + nn.MaskedLinear(num_hidden, num_inputs, + output_mask)) + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + h = self.s_joiner(inputs, cond_inputs) + m = self.s_trunk(h) + + h = self.t_joiner(inputs, cond_inputs) + a = self.t_trunk(h) + + if self.pre_exp_tanh: + a = torch.tanh(a) + + u = (inputs - m) * torch.exp(-a) + return u, -a.sum(-1, keepdim=True) + + else: + x = torch.zeros_like(inputs) + for i_col in range(inputs.shape[1]): + h = self.s_joiner(x, cond_inputs) + m = self.s_trunk(h) + + h = self.t_joiner(x, cond_inputs) + a = self.t_trunk(h) + + if self.pre_exp_tanh: + a = torch.tanh(a) + + x[:, i_col] = inputs[:, i_col] * torch.exp( + a[:, i_col]) + m[:, i_col] + return x, -a.sum(-1, keepdim=True) + +class MADE(nn.Module): + """ An implementation of MADE + (https://arxiv.org/abs/1502.03509). + """ + + def __init__(self, + num_inputs, + num_hidden, + num_cond_inputs=None, + act='relu', + pre_exp_tanh=False): + super(MADE, self).__init__() + + activations = {'relu': nn.ReLU, 'sigmoid': nn.Sigmoid, 'tanh': nn.Tanh} + act_func = activations[act] + + input_mask = get_mask( + num_inputs, num_hidden, num_inputs, mask_type='input') + hidden_mask = get_mask(num_hidden, num_hidden, num_inputs) + output_mask = get_mask( + num_hidden, num_inputs * 2, num_inputs, mask_type='output') + + self.joiner = nn.MaskedLinear(num_inputs, num_hidden, input_mask, + num_cond_inputs) + + self.trunk = nn.Sequential(act_func(), + nn.MaskedLinear(num_hidden, num_hidden, + hidden_mask), act_func(), + nn.MaskedLinear(num_hidden, num_inputs * 2, + output_mask)) + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + h = self.joiner(inputs, cond_inputs) + m, a = self.trunk(h).chunk(2, 1) + u = (inputs - m) * torch.exp(-a) + return u, -a.sum(-1, keepdim=True) + + else: + x = torch.zeros_like(inputs) + for i_col in range(inputs.shape[1]): + h = self.joiner(x, cond_inputs) + m, a = self.trunk(h).chunk(2, 1) + x[:, i_col] = inputs[:, i_col] * torch.exp( + a[:, i_col]) + m[:, i_col] + return x, -a.sum(-1, keepdim=True) + + +class Sigmoid(nn.Module): + def __init__(self): + super(Sigmoid, self).__init__() + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + s = torch.sigmoid + return s(inputs), torch.log(s(inputs) * (1 - s(inputs))).sum( + -1, keepdim=True) + else: + return torch.log(inputs / + (1 - inputs)), -torch.log(inputs - inputs**2).sum( + -1, keepdim=True) + + +class Logit(Sigmoid): + def __init__(self): + super(Logit, self).__init__() + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + return super(Logit, self).forward(inputs, 'inverse') + else: + return super(Logit, self).forward(inputs, 'direct') + + + +class BatchNormFlow(nn.Module): + """ An implementation of a batch normalization layer from + Density estimation using Real NVP + (https://arxiv.org/abs/1605.08803). + """ + + def __init__(self, num_inputs, momentum=0.0, eps=1e-5): + super(BatchNormFlow, self).__init__() + + num_inputs = num_inputs[0][0] + self.log_gamma = nn.Parameter(torch.zeros(num_inputs)) + self.beta = nn.Parameter(torch.zeros(num_inputs)) + self.momentum = momentum + self.eps = eps + + self.register_buffer('running_mean', torch.zeros(num_inputs)) + self.register_buffer('running_var', torch.ones(num_inputs)) + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + if self.training: + inputs = inputs[0] + #inputs = torch.Tensor(inputs) + #inputs = np.array(inputs) + self.batch_mean = inputs.mean(0) + self.batch_var = ( + inputs - self.batch_mean).pow(2).mean(0) + self.eps + + self.running_mean.mul_(self.momentum) + self.running_var.mul_(self.momentum) + + self.running_mean.add_(self.batch_mean.data * + (1 - self.momentum)) + self.running_var.add_(self.batch_var.data * + (1 - self.momentum)) + + mean = self.batch_mean + var = self.batch_var + else: + mean = self.running_mean + var = self.running_var + + x_hat = (inputs - mean) / var.sqrt() + y = torch.exp(self.log_gamma) * x_hat + self.beta + return y, (self.log_gamma - 0.5 * torch.log(var)).sum( + -1, keepdim=True) + else: + if self.training: + mean = self.batch_mean + var = self.batch_var + else: + mean = self.running_mean + var = self.running_var + + x_hat = (inputs - self.beta) / torch.exp(self.log_gamma) + + y = x_hat * var.sqrt() + mean + + return y, (-self.log_gamma + 0.5 * torch.log(var)).sum( + -1, keepdim=True) + + def output_dims(self, input_dims): + assert len(input_dims) == 1, "Can only use 1 input" + return input_dims + +class ActNorm(nn.Module): + """ An implementation of a activation normalization layer + from Glow: Generative Flow with Invertible 1x1 Convolutions + (https://arxiv.org/abs/1807.03039). + """ + + def __init__(self, num_inputs): + super(ActNorm, self).__init__() + num_inputs = num_inputs[0][0] + self.weight = nn.Parameter(torch.ones(num_inputs)) + self.bias = nn.Parameter(torch.zeros(num_inputs)) + self.initialized = False + + def forward(self, inputs, cond_inputs=None, rev=False): + inputs = inputs[0] + if self.initialized == False: + self.weight.data.copy_(torch.log(1.0 / (inputs.std(0) + 1e-12))) + self.bias.data.copy_(inputs.mean(0)) + self.initialized = True + + if rev == False: + return ( + inputs - self.bias) * torch.exp(self.weight), self.weight.sum( + -1, keepdim=True).unsqueeze(0).repeat(inputs.size(0), 1) + else: + return inputs * torch.exp( + -self.weight) + self.bias, -self.weight.sum( + -1, keepdim=True).unsqueeze(0).repeat(inputs.size(0), 1) + + def output_dims(self, input_dims): + assert len(input_dims) == 1, "Can only use 1 input" + return input_dims + + +class InvertibleMM(nn.Module): + """ An implementation of a invertible matrix multiplication + layer from Glow: Generative Flow with Invertible 1x1 Convolutions + (https://arxiv.org/abs/1807.03039). + """ + + def __init__(self, num_inputs): + super(InvertibleMM, self).__init__() + self.W = nn.Parameter(torch.Tensor(num_inputs, num_inputs)) + nn.init.orthogonal_(self.W) + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + return inputs @ self.W, torch.slogdet( + self.W)[-1].unsqueeze(0).unsqueeze(0).repeat( + inputs.size(0), 1) + else: + return inputs @ torch.inverse(self.W), -torch.slogdet( + self.W)[-1].unsqueeze(0).unsqueeze(0).repeat( + inputs.size(0), 1) + + +class LUInvertibleMM(nn.Module): + """ An implementation of a invertible matrix multiplication + layer from Glow: Generative Flow with Invertible 1x1 Convolutions + (https://arxiv.org/abs/1807.03039). + """ + + def __init__(self, num_inputs): + super(LUInvertibleMM, self).__init__() + num_inputs = num_inputs[0][0] + self.W = torch.Tensor(num_inputs, num_inputs) + nn.init.orthogonal_(self.W) + self.L_mask = torch.tril(torch.ones(self.W.size()), -1) + self.U_mask = self.L_mask.t().clone() + + P, L, U = sp.linalg.lu(self.W.numpy()) + self.P = torch.from_numpy(P) + self.L = nn.Parameter(torch.from_numpy(L)) + self.U = nn.Parameter(torch.from_numpy(U)) + + S = np.diag(U) + sign_S = np.sign(S) + log_S = np.log(abs(S)) + self.sign_S = torch.from_numpy(sign_S) + self.log_S = nn.Parameter(torch.from_numpy(log_S)) + + self.I = torch.eye(self.L.size(0)) + + def forward(self, inputs, cond_inputs=None, rev=False): + if str(self.L_mask.device) != str(self.L.device): + self.L_mask = self.L_mask.to(self.L.device) + self.U_mask = self.U_mask.to(self.L.device) + self.I = self.I.to(self.L.device) + self.P = self.P.to(self.L.device) + self.sign_S = self.sign_S.to(self.L.device) + + L = self.L * self.L_mask + self.I + U = self.U * self.U_mask + torch.diag( + self.sign_S * torch.exp(self.log_S)) + W = self.P @ L @ U + + if rev == False: + return inputs[0] @ W, self.log_S.sum().unsqueeze(0).unsqueeze( + 0).repeat(inputs[0].size(0), 1) + else: + return inputs[0] @ torch.inverse( + W), -self.log_S.sum().unsqueeze(0).unsqueeze(0).repeat( + inputs[0].size(0), 1) + + def output_dims(self, input_dims): + assert len(input_dims) == 1, "Can only use 1 input" + return input_dims + + def jacobian(self, x, rev=False): + return 0. + +class Shuffle(nn.Module): + """ An implementation of a shuffling layer from + Density estimation using Real NVP + (https://arxiv.org/abs/1605.08803). + """ + + def __init__(self, num_inputs): + super(Shuffle, self).__init__() + self.perm = np.random.permutation(num_inputs) + self.inv_perm = np.argsort(self.perm) + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + return inputs[:, self.perm], torch.zeros( + inputs.size(0), 1, device=inputs.device) + else: + return inputs[:, self.inv_perm], torch.zeros( + inputs.size(0), 1, device=inputs.device) + + +class Reverse(nn.Module): + """ An implementation of a reversing layer from + Density estimation using Real NVP + (https://arxiv.org/abs/1605.08803). + """ + + def __init__(self, num_inputs): + super(Reverse, self).__init__() + self.perm = np.array(np.arange(0, num_inputs)[::-1]) + self.inv_perm = np.argsort(self.perm) + + def forward(self, inputs, cond_inputs=None, rev=False): + if rev == False: + return inputs[:, self.perm], torch.zeros( + inputs.size(0), 1, device=inputs.device) + else: + return inputs[:, self.inv_perm], torch.zeros( + inputs.size(0), 1, device=inputs.device) + + +class CouplingLayer(nn.Module): + """ An implementation of a coupling layer + from RealNVP (https://arxiv.org/abs/1605.08803). + """ + + def __init__(self, + num_inputs, + num_hidden, + mask, + num_cond_inputs=None, + s_act='tanh', + t_act='relu'): + super(CouplingLayer, self).__init__() + self.num_inputs = num_inputs[0][0] + self.mask = mask + + activations = {'relu': nn.ReLU, 'sigmoid': nn.Sigmoid, 'tanh': nn.Tanh} + s_act_func = activations[s_act] + t_act_func = activations[t_act] + + if num_cond_inputs is not None: + total_inputs = num_inputs + num_cond_inputs + else: + total_inputs = num_inputs + + total_inputs = total_inputs[0][0] + self.scale_net = nn.Sequential( + nn.Linear(total_inputs, num_hidden), s_act_func(), + nn.Linear(num_hidden, num_hidden), s_act_func(), + nn.Linear(num_hidden, num_inputs[0][0])) + self.translate_net = nn.Sequential( + nn.Linear(total_inputs, num_hidden), t_act_func(), + nn.Linear(num_hidden, num_hidden), t_act_func(), + nn.Linear(num_hidden, num_inputs[0][0])) + + def init(m): + if isinstance(m, nn.Linear): + m.bias.data.fill_(0) + nn.init.orthogonal_(m.weight.data) + + def forward(self, inputs, cond_inputs=None, rev=False): + mask = self.mask + inputs = inputs[0] + masked_inputs = inputs * mask + if cond_inputs is not None: + masked_inputs = torch.cat([masked_inputs, cond_inputs], -1) + + if rev == False: + log_s = self.scale_net(masked_inputs) * (1 - mask) + t = self.translate_net(masked_inputs) * (1 - mask) + s = torch.exp(log_s) + return inputs * s + t, log_s.sum(-1, keepdim=True) + else: + log_s = self.scale_net(masked_inputs) * (1 - mask) + t = self.translate_net(masked_inputs) * (1 - mask) + s = torch.exp(-log_s) + return (inputs - t) * s, -log_s.sum(-1, keepdim=True) + + def output_dims(self, input_dims): + assert len(input_dims) == 1, "Can only use 1 input" + return input_dims + +class FlowSequential(nn.Sequential): + """ A sequential container for flows. + In addition to a forward pass it implements a backward pass and + computes log jacobians. + """ + + def forward(self, inputs, cond_inputs=None, rev=False, logdets=None): + """ Performs a forward or backward pass for flow modules. + Args: + inputs: a tuple of inputs and logdets + mode: to run direct computation or inverse + """ + self.num_inputs = inputs.size(-1) + + if logdets is None: + logdets = torch.zeros(inputs.size(0), 1, device=inputs.device) + + # assert mode in ['direct', 'inverse'] + if rev == False: + for module in self._modules.values(): + inputs, logdet = module(inputs, cond_inputs, rev) + logdets += logdet + else: + for module in reversed(self._modules.values()): + inputs, logdet = module(inputs, cond_inputs, rev) + logdets += logdet + + return inputs, logdets + + def log_probs(self, inputs, cond_inputs = None): + u, log_jacob = self(inputs, cond_inputs) + log_probs = (-0.5 * u.pow(2) - 0.5 * math.log(2 * math.pi)).sum( + -1, keepdim=True) + return (log_probs + log_jacob).sum(-1, keepdim=True) + + def sample(self, num_samples=None, noise=None, cond_inputs=None): + if noise is None: + noise = torch.Tensor(num_samples, self.num_inputs).normal_() + device = next(self.parameters()).device + noise = noise.to(device) + if cond_inputs is not None: + cond_inputs = cond_inputs.to(device) + samples = self.forward(noise, cond_inputs, mode='inverse')[0] + return samples diff --git a/localization.py b/unused-scripts/localization.py similarity index 94% rename from localization.py rename to unused-scripts/localization.py index 17e5778..1e91dab 100644 --- a/localization.py +++ b/unused-scripts/localization.py @@ -35,7 +35,7 @@ def save_imgs(inputs, grad, cnt): def export_gradient_maps(model, testloader, optimizer, n_batches=1): plt.figure(figsize=(10, 10)) - testloader.dataset.get_fixed = True + testloader.dataset.get_fixed = False cnt = 0 degrees = -1 * np.arange(c.n_transforms_test) * 360.0 / c.n_transforms_test @@ -50,13 +50,13 @@ def export_gradient_maps(model, testloader, optimizer, n_batches=1): loss.backward() grad = inputs.grad.view(-1, c.n_transforms_test, *inputs.shape[-3:]) - grad = grad[labels > 0] + grad = grad[labels >= 0] if grad.shape[0] == 0: continue grad = t2np(grad) inputs = inputs.view(-1, c.n_transforms_test, *inputs.shape[-3:])[:, 0] - inputs = np.transpose(t2np(inputs[labels > 0]), [0, 2, 3, 1]) + inputs = np.transpose(t2np(inputs[labels >= 0]), [0, 2, 3, 1]) inputs_unnormed = np.clip(inputs * c.norm_std + c.norm_mean, 0, 1) for i_item in range(c.n_transforms_test): diff --git a/unused-scripts/logo_detection.py b/unused-scripts/logo_detection.py new file mode 100644 index 0000000..e1a304a --- /dev/null +++ b/unused-scripts/logo_detection.py @@ -0,0 +1,60 @@ +from skimage import io +import matplotlib.pyplot as plt +import numpy as np +import cv2 + +from skimage.color import rgb2gray +from skimage import feature + +for i in range(12): + break + +I1 = io.imread("bottle_logo_defective1.jpg") +cv2.imwrite("origin.jpg", I1) + +image = cv2.cvtColor(I1, cv2.COLOR_BGR2GRAY) +cv2.imwrite("Gray.jpg", image) + +image = cv2.GaussianBlur(image, (21, 21), 0) + +# seg_img = cv2.adaptiveThreshold(image, 255, cv2.ADAPTIVE_THRESH_MEAN_C, cv2.THRESH_BINARY, 3, 1) +seg_img = cv2.threshold(image, 30, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)[1] +cv2.imwrite('binary.jpg', seg_img) + +h0, w0 = seg_img.shape + +if seg_img[0][0] == 255: + for i in range(w0): + for j in range(2): + if seg_img[j][i] == 255: + seg_img[j][i] = 0 + for j in range(h0): + for i in range(2): + if seg_img[j][i] == 255: + seg_img[j][i] = 0 + +cv2.imwrite("Threshold.jpg", seg_img) + +num, labels, stats, centroids = cv2.connectedComponentsWithStats(seg_img) + +h, w = seg_img.shape +print(h * w) + +first_stat = 0 +second_stat = 0 + +# Largest area should be the bottle +# Second largest area should be the logo +for istat in stats: + if istat[4] > 2000 and istat[4] > second_stat: + if istat[4] > first_stat: + first_stat = istat[4] + else: + second_stat = istat[4] + logo_stat = istat + +print(logo_stat) +cv2.rectangle(I1, (logo_stat[0], logo_stat[1]), (logo_stat[0] + logo_stat[2], logo_stat[1] + logo_stat[3]), + (255, 0, 255), 2) + +cv2.imwrite("segmented.jpg", I1) diff --git a/utils.py b/utils.py deleted file mode 100644 index af3881c..0000000 --- a/utils.py +++ /dev/null @@ -1,105 +0,0 @@ -import os -import torch -from torch.utils.data import DataLoader -from torchvision import datasets, transforms - -import config as c -from multi_transform_loader import ImageFolderMultiTransform - - -def t2np(tensor): - '''pytorch tensor -> numpy array''' - return tensor.cpu().data.numpy() if tensor is not None else None - - -def get_loss(z, jac): - '''check equation 4 of the paper why this makes sense - oh and just ignore the scaling here''' - return torch.mean(0.5 * torch.sum(z ** 2, dim=(1,)) - jac) / z.shape[1] - - -def load_datasets(dataset_path, class_name): - ''' - Expected folder/file format to find anomalies of class from dataset location : - - train data: - - dataset_path/class_name/train/good/any_filename.png - dataset_path/class_name/train/good/another_filename.tif - dataset_path/class_name/train/good/xyz.png - [...] - - test data: - - 'normal data' = non-anomalies - - dataset_path/class_name/test/good/name_the_file_as_you_like_as_long_as_there_is_an_image_extension.webp - dataset_path/class_name/test/good/did_you_know_the_image_extension_webp?.png - dataset_path/class_name/test/good/did_you_know_that_filenames_may_contain_question_marks????.png - dataset_path/class_name/test/good/dont_know_how_it_is_with_windows.png - dataset_path/class_name/test/good/just_dont_use_windows_for_this.png - [...] - - anomalies - assume there are anomaly classes 'crack' and 'curved' - - dataset_path/class_name/test/crack/dat_crack_damn.png - dataset_path/class_name/test/crack/let_it_crack.png - dataset_path/class_name/test/crack/writing_docs_is_fun.png - [...] - - dataset_path/class_name/test/curved/wont_make_a_difference_if_you_put_all_anomalies_in_one_class.png - dataset_path/class_name/test/curved/but_this_code_is_practicable_for_the_mvtec_dataset.png - [...] - ''' - - def target_transform(target): - return class_perm[target] - - data_dir_train = os.path.join(dataset_path, class_name, 'train') - data_dir_test = os.path.join(dataset_path, class_name, 'test') - - classes = os.listdir(data_dir_test) - if 'good' not in classes: - print('There should exist a subdirectory "good". Read the doc of this function for further information.') - exit() - classes.sort() - class_perm = list() - class_idx = 1 - for cl in classes: - if cl == 'good': - class_perm.append(0) - else: - class_perm.append(class_idx) - class_idx += 1 - - augmentative_transforms = [] - if c.transf_rotations: - augmentative_transforms += [transforms.RandomRotation(180)] - if c.transf_brightness > 0.0 or c.transf_contrast > 0.0 or c.transf_saturation > 0.0: - augmentative_transforms += [transforms.ColorJitter(brightness=c.transf_brightness, contrast=c.transf_contrast, - saturation=c.transf_saturation)] - - tfs = [transforms.Resize(c.img_size)] + augmentative_transforms + [transforms.ToTensor(), - transforms.Normalize(c.norm_mean, c.norm_std)] - - transform_train = transforms.Compose(tfs) - - trainset = ImageFolderMultiTransform(data_dir_train, transform=transform_train, n_transforms=c.n_transforms) - testset = ImageFolderMultiTransform(data_dir_test, transform=transform_train, target_transform=target_transform, - n_transforms=c.n_transforms_test) - return trainset, testset - - -def make_dataloaders(trainset, testset): - trainloader = torch.utils.data.DataLoader(trainset, pin_memory=True, batch_size=c.batch_size, shuffle=True, - drop_last=False) - testloader = torch.utils.data.DataLoader(testset, pin_memory=True, batch_size=c.batch_size_test, shuffle=True, - drop_last=False) - return trainloader, testloader - - -def preprocess_batch(data): - '''move data to device and reshape image''' - inputs, labels = data - inputs, labels = inputs.to(c.device), labels.to(c.device) - inputs = inputs.view(-1, *inputs.shape[-3:]) - return inputs, labels diff --git a/work/black1/test/defect/Camera0_202009142018041_product.png b/work/black1/test/defect/Camera0_202009142018041_product.png new file mode 100644 index 0000000..61301a2 Binary files /dev/null and b/work/black1/test/defect/Camera0_202009142018041_product.png differ diff --git a/work/black1/test/defect/Camera0_202009142018586_product.png b/work/black1/test/defect/Camera0_202009142018586_product.png new file mode 100644 index 0000000..97612e9 Binary files /dev/null and b/work/black1/test/defect/Camera0_202009142018586_product.png differ diff --git a/work/black1/test/good/Camera0_202009142017487_product.png b/work/black1/test/good/Camera0_202009142017487_product.png new file mode 100644 index 0000000..1c1155e Binary files /dev/null and b/work/black1/test/good/Camera0_202009142017487_product.png differ diff --git a/work/black1/test/good/Camera0_202009142018133_product.png b/work/black1/test/good/Camera0_202009142018133_product.png new file mode 100644 index 0000000..ce170d0 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142018133_product.png differ diff --git a/work/black1/test/good/Camera0_202009142018224_product.png b/work/black1/test/good/Camera0_202009142018224_product.png new file mode 100644 index 0000000..f492689 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142018224_product.png differ diff --git a/work/black1/test/good/Camera0_202009142018306_product.png b/work/black1/test/good/Camera0_202009142018306_product.png new file mode 100644 index 0000000..90f5c70 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142018306_product.png differ diff --git a/work/black1/test/good/Camera0_202009142018379_product.png b/work/black1/test/good/Camera0_202009142018379_product.png new file mode 100644 index 0000000..ffeb8b8 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142018379_product.png differ diff --git a/work/black1/test/good/Camera0_202009142018442_product.png b/work/black1/test/good/Camera0_202009142018442_product.png new file mode 100644 index 0000000..e9677ce Binary files /dev/null and b/work/black1/test/good/Camera0_202009142018442_product.png differ diff --git a/work/black1/test/good/Camera0_202009142018514_product.png b/work/black1/test/good/Camera0_202009142018514_product.png new file mode 100644 index 0000000..a3a7782 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142018514_product.png differ diff --git a/work/black1/test/good/Camera0_202009142019053_product.png b/work/black1/test/good/Camera0_202009142019053_product.png new file mode 100644 index 0000000..80eee55 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142019053_product.png differ diff --git a/work/black1/test/good/Camera0_202009142019104_product.png b/work/black1/test/good/Camera0_202009142019104_product.png new file mode 100644 index 0000000..77c03d4 Binary files /dev/null and b/work/black1/test/good/Camera0_202009142019104_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310309_product.png b/work/black1/train/good/Camera0_202012032310309_product.png new file mode 100644 index 0000000..46618cc Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310309_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310312_product.png b/work/black1/train/good/Camera0_202012032310312_product.png new file mode 100644 index 0000000..378b913 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310312_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310316_product.png b/work/black1/train/good/Camera0_202012032310316_product.png new file mode 100644 index 0000000..3fd6d8d Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310316_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310318_product.png b/work/black1/train/good/Camera0_202012032310318_product.png new file mode 100644 index 0000000..c120244 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310318_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310320_product.png b/work/black1/train/good/Camera0_202012032310320_product.png new file mode 100644 index 0000000..78fefda Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310320_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310322_product.png b/work/black1/train/good/Camera0_202012032310322_product.png new file mode 100644 index 0000000..a9623e9 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310322_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310324_product.png b/work/black1/train/good/Camera0_202012032310324_product.png new file mode 100644 index 0000000..ec6c173 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310324_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310326_product.png b/work/black1/train/good/Camera0_202012032310326_product.png new file mode 100644 index 0000000..e1f68e2 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310326_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310328_product.png b/work/black1/train/good/Camera0_202012032310328_product.png new file mode 100644 index 0000000..5a1117f Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310328_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310330_product.png b/work/black1/train/good/Camera0_202012032310330_product.png new file mode 100644 index 0000000..902bf75 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310330_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310332_product.png b/work/black1/train/good/Camera0_202012032310332_product.png new file mode 100644 index 0000000..c1855b1 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310332_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310336_product.png b/work/black1/train/good/Camera0_202012032310336_product.png new file mode 100644 index 0000000..8edd48a Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310336_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310338_product.png b/work/black1/train/good/Camera0_202012032310338_product.png new file mode 100644 index 0000000..f4d1044 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310338_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310340_product.png b/work/black1/train/good/Camera0_202012032310340_product.png new file mode 100644 index 0000000..bd12ffe Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310340_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310341_product.png b/work/black1/train/good/Camera0_202012032310341_product.png new file mode 100644 index 0000000..930cbc6 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310341_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310343_product.png b/work/black1/train/good/Camera0_202012032310343_product.png new file mode 100644 index 0000000..fab9ca1 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310343_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310349_product.png b/work/black1/train/good/Camera0_202012032310349_product.png new file mode 100644 index 0000000..40cb540 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310349_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310352_product.png b/work/black1/train/good/Camera0_202012032310352_product.png new file mode 100644 index 0000000..b29d2c5 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310352_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310354_product.png b/work/black1/train/good/Camera0_202012032310354_product.png new file mode 100644 index 0000000..536f35e Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310354_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310356_product.png b/work/black1/train/good/Camera0_202012032310356_product.png new file mode 100644 index 0000000..a8b8e82 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310356_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310359_product.png b/work/black1/train/good/Camera0_202012032310359_product.png new file mode 100644 index 0000000..f4bd2ca Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310359_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310361_product.png b/work/black1/train/good/Camera0_202012032310361_product.png new file mode 100644 index 0000000..87a1109 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310361_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310363_product.png b/work/black1/train/good/Camera0_202012032310363_product.png new file mode 100644 index 0000000..b4e4487 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310363_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310371_product.png b/work/black1/train/good/Camera0_202012032310371_product.png new file mode 100644 index 0000000..c201c11 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310371_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310373_product.png b/work/black1/train/good/Camera0_202012032310373_product.png new file mode 100644 index 0000000..90f4faa Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310373_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310377_product.png b/work/black1/train/good/Camera0_202012032310377_product.png new file mode 100644 index 0000000..fb62fe6 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310377_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310378_product.png b/work/black1/train/good/Camera0_202012032310378_product.png new file mode 100644 index 0000000..414ffda Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310378_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310380_product.png b/work/black1/train/good/Camera0_202012032310380_product.png new file mode 100644 index 0000000..0e6f3fe Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310380_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310382_product.png b/work/black1/train/good/Camera0_202012032310382_product.png new file mode 100644 index 0000000..4678889 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310382_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310386_product.png b/work/black1/train/good/Camera0_202012032310386_product.png new file mode 100644 index 0000000..0f11a46 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310386_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310393_product.png b/work/black1/train/good/Camera0_202012032310393_product.png new file mode 100644 index 0000000..2238336 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310393_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310397_product.png b/work/black1/train/good/Camera0_202012032310397_product.png new file mode 100644 index 0000000..cded858 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310397_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310398_product.png b/work/black1/train/good/Camera0_202012032310398_product.png new file mode 100644 index 0000000..b55972e Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310398_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310400_product.png b/work/black1/train/good/Camera0_202012032310400_product.png new file mode 100644 index 0000000..01e89e9 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310400_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310404_product.png b/work/black1/train/good/Camera0_202012032310404_product.png new file mode 100644 index 0000000..8573328 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310404_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310408_product.png b/work/black1/train/good/Camera0_202012032310408_product.png new file mode 100644 index 0000000..2071ed8 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310408_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310409_product.png b/work/black1/train/good/Camera0_202012032310409_product.png new file mode 100644 index 0000000..5f318b1 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310409_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310411_product.png b/work/black1/train/good/Camera0_202012032310411_product.png new file mode 100644 index 0000000..9059499 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310411_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310413_product.png b/work/black1/train/good/Camera0_202012032310413_product.png new file mode 100644 index 0000000..eef2e01 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310413_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310417_product.png b/work/black1/train/good/Camera0_202012032310417_product.png new file mode 100644 index 0000000..276b9b7 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310417_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310419_product.png b/work/black1/train/good/Camera0_202012032310419_product.png new file mode 100644 index 0000000..887c4ce Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310419_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310423_product.png b/work/black1/train/good/Camera0_202012032310423_product.png new file mode 100644 index 0000000..8ae4e3d Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310423_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310425_product.png b/work/black1/train/good/Camera0_202012032310425_product.png new file mode 100644 index 0000000..13f2e96 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310425_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310426_product.png b/work/black1/train/good/Camera0_202012032310426_product.png new file mode 100644 index 0000000..1ff3015 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310426_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310430_product.png b/work/black1/train/good/Camera0_202012032310430_product.png new file mode 100644 index 0000000..e1809f2 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310430_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310432_product.png b/work/black1/train/good/Camera0_202012032310432_product.png new file mode 100644 index 0000000..21a9f40 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310432_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310436_product.png b/work/black1/train/good/Camera0_202012032310436_product.png new file mode 100644 index 0000000..6c0d837 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310436_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310438_product.png b/work/black1/train/good/Camera0_202012032310438_product.png new file mode 100644 index 0000000..804dbb1 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310438_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310440_product.png b/work/black1/train/good/Camera0_202012032310440_product.png new file mode 100644 index 0000000..d6f1fff Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310440_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310442_product.png b/work/black1/train/good/Camera0_202012032310442_product.png new file mode 100644 index 0000000..6ef58a2 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310442_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310444_product.png b/work/black1/train/good/Camera0_202012032310444_product.png new file mode 100644 index 0000000..4eca4b3 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310444_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310447_product.png b/work/black1/train/good/Camera0_202012032310447_product.png new file mode 100644 index 0000000..95ecfb1 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310447_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310451_product.png b/work/black1/train/good/Camera0_202012032310451_product.png new file mode 100644 index 0000000..96f5fc0 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310451_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310458_product.png b/work/black1/train/good/Camera0_202012032310458_product.png new file mode 100644 index 0000000..f042f46 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310458_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310460_product.png b/work/black1/train/good/Camera0_202012032310460_product.png new file mode 100644 index 0000000..6a62744 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310460_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310462_product.png b/work/black1/train/good/Camera0_202012032310462_product.png new file mode 100644 index 0000000..01cee9f Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310462_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310464_product.png b/work/black1/train/good/Camera0_202012032310464_product.png new file mode 100644 index 0000000..465f8c6 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310464_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310469_product.png b/work/black1/train/good/Camera0_202012032310469_product.png new file mode 100644 index 0000000..5f17ce4 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310469_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310473_product.png b/work/black1/train/good/Camera0_202012032310473_product.png new file mode 100644 index 0000000..faa8acd Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310473_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310475_product.png b/work/black1/train/good/Camera0_202012032310475_product.png new file mode 100644 index 0000000..41ae79c Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310475_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310477_product.png b/work/black1/train/good/Camera0_202012032310477_product.png new file mode 100644 index 0000000..c2ae3ec Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310477_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310479_product.png b/work/black1/train/good/Camera0_202012032310479_product.png new file mode 100644 index 0000000..7d8b34e Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310479_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310481_product.png b/work/black1/train/good/Camera0_202012032310481_product.png new file mode 100644 index 0000000..16b96b3 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310481_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310483_product.png b/work/black1/train/good/Camera0_202012032310483_product.png new file mode 100644 index 0000000..2cc028b Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310483_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310487_product.png b/work/black1/train/good/Camera0_202012032310487_product.png new file mode 100644 index 0000000..6463d98 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310487_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310488_product.png b/work/black1/train/good/Camera0_202012032310488_product.png new file mode 100644 index 0000000..3333808 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310488_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310490_product.png b/work/black1/train/good/Camera0_202012032310490_product.png new file mode 100644 index 0000000..ab2ac05 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310490_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310492_product.png b/work/black1/train/good/Camera0_202012032310492_product.png new file mode 100644 index 0000000..166a44c Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310492_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310494_product.png b/work/black1/train/good/Camera0_202012032310494_product.png new file mode 100644 index 0000000..8b3cfd0 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310494_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310496_product.png b/work/black1/train/good/Camera0_202012032310496_product.png new file mode 100644 index 0000000..78c8666 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310496_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310498_product.png b/work/black1/train/good/Camera0_202012032310498_product.png new file mode 100644 index 0000000..bf3b83b Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310498_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310500_product.png b/work/black1/train/good/Camera0_202012032310500_product.png new file mode 100644 index 0000000..29b1102 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310500_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310503_product.png b/work/black1/train/good/Camera0_202012032310503_product.png new file mode 100644 index 0000000..32ccb64 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310503_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310509_product.png b/work/black1/train/good/Camera0_202012032310509_product.png new file mode 100644 index 0000000..ad47391 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310509_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310511_product.png b/work/black1/train/good/Camera0_202012032310511_product.png new file mode 100644 index 0000000..7901f30 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310511_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310514_product.png b/work/black1/train/good/Camera0_202012032310514_product.png new file mode 100644 index 0000000..0bc42d6 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310514_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310516_product.png b/work/black1/train/good/Camera0_202012032310516_product.png new file mode 100644 index 0000000..7548dc9 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310516_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310524_product.png b/work/black1/train/good/Camera0_202012032310524_product.png new file mode 100644 index 0000000..a69d28d Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310524_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310529_product.png b/work/black1/train/good/Camera0_202012032310529_product.png new file mode 100644 index 0000000..8721b8b Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310529_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310531_product.png b/work/black1/train/good/Camera0_202012032310531_product.png new file mode 100644 index 0000000..9cb59d7 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310531_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310533_product.png b/work/black1/train/good/Camera0_202012032310533_product.png new file mode 100644 index 0000000..8cdfb95 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310533_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310537_product.png b/work/black1/train/good/Camera0_202012032310537_product.png new file mode 100644 index 0000000..24f629a Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310537_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310539_product.png b/work/black1/train/good/Camera0_202012032310539_product.png new file mode 100644 index 0000000..e93a213 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310539_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310543_product.png b/work/black1/train/good/Camera0_202012032310543_product.png new file mode 100644 index 0000000..1a0427c Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310543_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310545_product.png b/work/black1/train/good/Camera0_202012032310545_product.png new file mode 100644 index 0000000..48fe831 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310545_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310547_product.png b/work/black1/train/good/Camera0_202012032310547_product.png new file mode 100644 index 0000000..9f45b93 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310547_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310549_product.png b/work/black1/train/good/Camera0_202012032310549_product.png new file mode 100644 index 0000000..b5e3be5 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310549_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310551_product.png b/work/black1/train/good/Camera0_202012032310551_product.png new file mode 100644 index 0000000..d3d67e1 Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310551_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310553_product.png b/work/black1/train/good/Camera0_202012032310553_product.png new file mode 100644 index 0000000..932b54c Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310553_product.png differ diff --git a/work/black1/train/good/Camera0_202012032310554_product.png b/work/black1/train/good/Camera0_202012032310554_product.png new file mode 100644 index 0000000..5fa4dfc Binary files /dev/null and b/work/black1/train/good/Camera0_202012032310554_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307358_product.png b/work/black1/validate/defect/Camera0_202012032307358_product.png new file mode 100644 index 0000000..689957d Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307358_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307360_product.png b/work/black1/validate/defect/Camera0_202012032307360_product.png new file mode 100644 index 0000000..802865d Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307360_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307362_product.png b/work/black1/validate/defect/Camera0_202012032307362_product.png new file mode 100644 index 0000000..0922ef5 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307362_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307364_product.png b/work/black1/validate/defect/Camera0_202012032307364_product.png new file mode 100644 index 0000000..6ef36df Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307364_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307365_product.png b/work/black1/validate/defect/Camera0_202012032307365_product.png new file mode 100644 index 0000000..9dc1b31 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307365_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307369_product.png b/work/black1/validate/defect/Camera0_202012032307369_product.png new file mode 100644 index 0000000..d88cfa6 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307369_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307371_product.png b/work/black1/validate/defect/Camera0_202012032307371_product.png new file mode 100644 index 0000000..a9a883e Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307371_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307375_product.png b/work/black1/validate/defect/Camera0_202012032307375_product.png new file mode 100644 index 0000000..e23800d Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307375_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307380_product.png b/work/black1/validate/defect/Camera0_202012032307380_product.png new file mode 100644 index 0000000..a642ac3 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307380_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307382_product.png b/work/black1/validate/defect/Camera0_202012032307382_product.png new file mode 100644 index 0000000..037d172 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307382_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307386_product.png b/work/black1/validate/defect/Camera0_202012032307386_product.png new file mode 100644 index 0000000..4a214e5 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307386_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307388_product.png b/work/black1/validate/defect/Camera0_202012032307388_product.png new file mode 100644 index 0000000..98c2a34 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307388_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307392_product.png b/work/black1/validate/defect/Camera0_202012032307392_product.png new file mode 100644 index 0000000..e749375 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307392_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307394_product.png b/work/black1/validate/defect/Camera0_202012032307394_product.png new file mode 100644 index 0000000..7b9f0bd Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307394_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307396_product.png b/work/black1/validate/defect/Camera0_202012032307396_product.png new file mode 100644 index 0000000..d63673b Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307396_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307400_product.png b/work/black1/validate/defect/Camera0_202012032307400_product.png new file mode 100644 index 0000000..92cc56d Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307400_product.png differ diff --git a/work/black1/validate/defect/Camera0_202012032307402_product.png b/work/black1/validate/defect/Camera0_202012032307402_product.png new file mode 100644 index 0000000..d740a85 Binary files /dev/null and b/work/black1/validate/defect/Camera0_202012032307402_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311030_product.png b/work/black1/validate/good/Camera0_202012032311030_product.png new file mode 100644 index 0000000..582db4c Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311030_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311034_product.png b/work/black1/validate/good/Camera0_202012032311034_product.png new file mode 100644 index 0000000..3959961 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311034_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311036_product.png b/work/black1/validate/good/Camera0_202012032311036_product.png new file mode 100644 index 0000000..ea70013 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311036_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311040_product.png b/work/black1/validate/good/Camera0_202012032311040_product.png new file mode 100644 index 0000000..1810d2b Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311040_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311044_product.png b/work/black1/validate/good/Camera0_202012032311044_product.png new file mode 100644 index 0000000..6fe5487 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311044_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311048_product.png b/work/black1/validate/good/Camera0_202012032311048_product.png new file mode 100644 index 0000000..aaf335f Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311048_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311050_product.png b/work/black1/validate/good/Camera0_202012032311050_product.png new file mode 100644 index 0000000..6f5b384 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311050_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311051_product.png b/work/black1/validate/good/Camera0_202012032311051_product.png new file mode 100644 index 0000000..bf2f0c1 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311051_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311054_product.png b/work/black1/validate/good/Camera0_202012032311054_product.png new file mode 100644 index 0000000..d9a5224 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311054_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311057_product.png b/work/black1/validate/good/Camera0_202012032311057_product.png new file mode 100644 index 0000000..7da61f7 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311057_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311059_product.png b/work/black1/validate/good/Camera0_202012032311059_product.png new file mode 100644 index 0000000..bed6213 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311059_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311061_product.png b/work/black1/validate/good/Camera0_202012032311061_product.png new file mode 100644 index 0000000..57cb6a2 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311061_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311063_product.png b/work/black1/validate/good/Camera0_202012032311063_product.png new file mode 100644 index 0000000..a22e7af Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311063_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311065_product.png b/work/black1/validate/good/Camera0_202012032311065_product.png new file mode 100644 index 0000000..71ecb9b Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311065_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311067_product.png b/work/black1/validate/good/Camera0_202012032311067_product.png new file mode 100644 index 0000000..972d824 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311067_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311069_product.png b/work/black1/validate/good/Camera0_202012032311069_product.png new file mode 100644 index 0000000..fc9e0dd Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311069_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311073_product.png b/work/black1/validate/good/Camera0_202012032311073_product.png new file mode 100644 index 0000000..c3f0c78 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311073_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311075_product.png b/work/black1/validate/good/Camera0_202012032311075_product.png new file mode 100644 index 0000000..d54ed4e Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311075_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311077_product.png b/work/black1/validate/good/Camera0_202012032311077_product.png new file mode 100644 index 0000000..75d97ce Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311077_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311079_product.png b/work/black1/validate/good/Camera0_202012032311079_product.png new file mode 100644 index 0000000..8e19c99 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311079_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311082_product.png b/work/black1/validate/good/Camera0_202012032311082_product.png new file mode 100644 index 0000000..fdbb2da Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311082_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311084_product.png b/work/black1/validate/good/Camera0_202012032311084_product.png new file mode 100644 index 0000000..0c4ea3f Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311084_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311088_product.png b/work/black1/validate/good/Camera0_202012032311088_product.png new file mode 100644 index 0000000..3797c1a Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311088_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311090_product.png b/work/black1/validate/good/Camera0_202012032311090_product.png new file mode 100644 index 0000000..ff925f9 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311090_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311092_product.png b/work/black1/validate/good/Camera0_202012032311092_product.png new file mode 100644 index 0000000..42e4703 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311092_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311096_product.png b/work/black1/validate/good/Camera0_202012032311096_product.png new file mode 100644 index 0000000..1f47dbb Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311096_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311098_product.png b/work/black1/validate/good/Camera0_202012032311098_product.png new file mode 100644 index 0000000..aa41c53 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311098_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311099_product.png b/work/black1/validate/good/Camera0_202012032311099_product.png new file mode 100644 index 0000000..347305f Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311099_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311101_product.png b/work/black1/validate/good/Camera0_202012032311101_product.png new file mode 100644 index 0000000..c58e3f5 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311101_product.png differ diff --git a/work/black1/validate/good/Camera0_202012032311105_product.png b/work/black1/validate/good/Camera0_202012032311105_product.png new file mode 100644 index 0000000..42237d3 Binary files /dev/null and b/work/black1/validate/good/Camera0_202012032311105_product.png differ diff --git a/work/pink1/test/defect/bad1.jpg b/work/pink1/test/defect/bad1.jpg new file mode 100644 index 0000000..75a76b9 Binary files /dev/null and b/work/pink1/test/defect/bad1.jpg differ diff --git a/work/pink1/test/defect/bad2.jpg b/work/pink1/test/defect/bad2.jpg new file mode 100644 index 0000000..575c189 Binary files /dev/null and b/work/pink1/test/defect/bad2.jpg differ diff --git a/work/pink1/test/good/good1.jpg b/work/pink1/test/good/good1.jpg new file mode 100644 index 0000000..b908e06 Binary files /dev/null and b/work/pink1/test/good/good1.jpg differ diff --git a/work/pink1/test/good/good2.jpg b/work/pink1/test/good/good2.jpg new file mode 100644 index 0000000..a294345 Binary files /dev/null and b/work/pink1/test/good/good2.jpg differ diff --git a/work/pink1/test/good/good3.jpg b/work/pink1/test/good/good3.jpg new file mode 100644 index 0000000..701858f Binary files /dev/null and b/work/pink1/test/good/good3.jpg differ diff --git a/work/pink1/train/good/01.jpg b/work/pink1/train/good/01.jpg new file mode 100644 index 0000000..d845b51 Binary files /dev/null and b/work/pink1/train/good/01.jpg differ diff --git a/work/pink1/train/good/02.jpg b/work/pink1/train/good/02.jpg new file mode 100644 index 0000000..caaaf22 Binary files /dev/null and b/work/pink1/train/good/02.jpg differ diff --git a/work/pink1/train/good/03.jpg b/work/pink1/train/good/03.jpg new file mode 100644 index 0000000..892eedf Binary files /dev/null and b/work/pink1/train/good/03.jpg differ diff --git a/work/pink1/train/good/04.jpg b/work/pink1/train/good/04.jpg new file mode 100644 index 0000000..6fe8d76 Binary files /dev/null and b/work/pink1/train/good/04.jpg differ diff --git a/work/pink1/train/good/05.jpg b/work/pink1/train/good/05.jpg new file mode 100644 index 0000000..7ef416b Binary files /dev/null and b/work/pink1/train/good/05.jpg differ diff --git a/work/pink1/train/good/06.jpg b/work/pink1/train/good/06.jpg new file mode 100644 index 0000000..119db0a Binary files /dev/null and b/work/pink1/train/good/06.jpg differ diff --git a/work/pink1/train/good/07.jpg b/work/pink1/train/good/07.jpg new file mode 100644 index 0000000..d2707c2 Binary files /dev/null and b/work/pink1/train/good/07.jpg differ diff --git a/work/pink1/train/good/08.jpg b/work/pink1/train/good/08.jpg new file mode 100644 index 0000000..50a8f7b Binary files /dev/null and b/work/pink1/train/good/08.jpg differ diff --git a/work/pink1/train/good/09.jpg b/work/pink1/train/good/09.jpg new file mode 100644 index 0000000..5a1f65b Binary files /dev/null and b/work/pink1/train/good/09.jpg differ diff --git a/work/pink1/train/good/10.jpg b/work/pink1/train/good/10.jpg new file mode 100644 index 0000000..72fbbad Binary files /dev/null and b/work/pink1/train/good/10.jpg differ diff --git a/work/pink1/train/good/11.jpg b/work/pink1/train/good/11.jpg new file mode 100644 index 0000000..3075ae7 Binary files /dev/null and b/work/pink1/train/good/11.jpg differ diff --git a/work/pink1/train/good/12.jpg b/work/pink1/train/good/12.jpg new file mode 100644 index 0000000..c6753b2 Binary files /dev/null and b/work/pink1/train/good/12.jpg differ diff --git a/work/pink1/train/good/13.jpg b/work/pink1/train/good/13.jpg new file mode 100644 index 0000000..256b538 Binary files /dev/null and b/work/pink1/train/good/13.jpg differ diff --git a/work/pink1/train/good/14.jpg b/work/pink1/train/good/14.jpg new file mode 100644 index 0000000..6476f82 Binary files /dev/null and b/work/pink1/train/good/14.jpg differ diff --git a/work/pink1/train/good/15.jpg b/work/pink1/train/good/15.jpg new file mode 100644 index 0000000..eb2fd6e Binary files /dev/null and b/work/pink1/train/good/15.jpg differ diff --git a/work/pink1/train/good/16.jpg b/work/pink1/train/good/16.jpg new file mode 100644 index 0000000..eb2fd6e Binary files /dev/null and b/work/pink1/train/good/16.jpg differ diff --git a/work/readme.md b/work/readme.md new file mode 100644 index 0000000..e69de29