williamcfrancis commited on
Commit
e22b55b
·
1 Parent(s): 9cca45c

Upload 74 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. README.md +34 -13
  3. angle_model_train.py +55 -0
  4. build_dataset.py +73 -0
  5. create_blurred.py +59 -0
  6. create_fft.py +31 -0
  7. deblur_img.py +85 -0
  8. length_model_train.py +52 -0
  9. pretrained_models/angle_model.hdf5 +3 -0
  10. pretrained_models/length_model.hdf5 +3 -0
  11. readme_imgs/img1.jpg +0 -0
  12. readme_imgs/img1_result.jpg +0 -0
  13. readme_imgs/img2.jpg +0 -0
  14. readme_imgs/img2_result.jpg +0 -0
  15. readme_imgs/img3.jpg +0 -0
  16. readme_imgs/img3_result.jpg +0 -0
  17. sidekick/__init__.py +0 -0
  18. sidekick/__pycache__/__init__.cpython-36.pyc +0 -0
  19. sidekick/__pycache__/__init__.cpython-37.pyc +0 -0
  20. sidekick/callbs/__init__.py +2 -0
  21. sidekick/callbs/__pycache__/__init__.cpython-36.pyc +0 -0
  22. sidekick/callbs/__pycache__/__init__.cpython-37.pyc +0 -0
  23. sidekick/callbs/__pycache__/manualcheckpoint.cpython-36.pyc +0 -0
  24. sidekick/callbs/__pycache__/manualcheckpoint.cpython-37.pyc +0 -0
  25. sidekick/callbs/__pycache__/trainmonitor.cpython-36.pyc +0 -0
  26. sidekick/callbs/__pycache__/trainmonitor.cpython-37.pyc +0 -0
  27. sidekick/callbs/manualcheckpoint.py +19 -0
  28. sidekick/callbs/trainmonitor.py +52 -0
  29. sidekick/datah/__init__.py +1 -0
  30. sidekick/datah/__pycache__/__init__.cpython-36.pyc +0 -0
  31. sidekick/datah/__pycache__/__init__.cpython-37.pyc +0 -0
  32. sidekick/datah/__pycache__/loader.cpython-36.pyc +0 -0
  33. sidekick/datah/__pycache__/loader.cpython-37.pyc +0 -0
  34. sidekick/datah/loader.py +30 -0
  35. sidekick/eval/__init__.py +1 -0
  36. sidekick/eval/__pycache__/__init__.cpython-36.pyc +0 -0
  37. sidekick/eval/__pycache__/calc_ranked.cpython-36.pyc +0 -0
  38. sidekick/eval/calc_ranked.py +23 -0
  39. sidekick/io/__init__.py +1 -0
  40. sidekick/io/__pycache__/__init__.cpython-36.pyc +0 -0
  41. sidekick/io/__pycache__/__init__.cpython-37.pyc +0 -0
  42. sidekick/io/__pycache__/hdf5_writer.cpython-36.pyc +0 -0
  43. sidekick/io/__pycache__/hdf5_writer.cpython-37.pyc +0 -0
  44. sidekick/io/__pycache__/hdf5datagen.cpython-36.pyc +0 -0
  45. sidekick/io/__pycache__/hdf5datagen.cpython-37.pyc +0 -0
  46. sidekick/io/hdf5_writer.py +53 -0
  47. sidekick/io/hdf5datagen.py +50 -0
  48. sidekick/nn/conv/__pycache__/__init__.cpython-37.pyc +0 -0
  49. sidekick/nn/conv/__pycache__/angle_model.cpython-37.pyc +0 -0
  50. sidekick/nn/conv/__pycache__/length_model.cpython-37.pyc +0 -0
.gitattributes CHANGED
@@ -32,3 +32,5 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
32
  *.zip filter=lfs diff=lfs merge=lfs -text
33
  *.zst filter=lfs diff=lfs merge=lfs -text
34
  *tfevents* filter=lfs diff=lfs merge=lfs -text
35
+ pretrained_models/angle_model.hdf5 filter=lfs diff=lfs merge=lfs -text
36
+ pretrained_models/length_model.hdf5 filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -1,13 +1,34 @@
1
- ---
2
- title: Deep Blind Motion Deblurring
3
- emoji: 🐠
4
- colorFrom: blue
5
- colorTo: green
6
- sdk: gradio
7
- sdk_version: 3.16.2
8
- app_file: app.py
9
- pinned: false
10
- license: apache-2.0
11
- ---
12
-
13
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Blind Motion Deblurring for Legible License Plates using Deep Learning
2
+
3
+ This project uses deep learning techniques to estimate a length and angle parameter for the point-spread function responsible for motion-deblurring of an image. This estimation is achieved by training a deep CNN model on the fast-fourier transformation of the blurred images. By using enough random examples of motion blurred images, the model learns how to estimate any kind of motion blur (upto a certain blur degree), making this approach a truly blind motion deblurring example. Once a length and angle of motion blur is estimated by the model, one can easily deblur the image using Wiener Deconvolution. This technique can have many applications, but we used it specifically for deblurring and making license plates legible. As seen below, the images demonstrate our model in action. With the introduction of some artifacts, the model manages to deblur the images to a point where the license plates are legible.
4
+
5
+ <img src="readme_imgs/img1.jpg" width="360px"> <img src="readme_imgs/img1_result.jpg" width="360px">
6
+
7
+ <img src="readme_imgs/img2.jpg" width="360px"> <img src="readme_imgs/img2_result.jpg" width="360px">
8
+
9
+ <img src="readme_imgs/img3.jpg" width="360px"> <img src="readme_imgs/img3_result.jpg" width="360px">
10
+
11
+ ## Package Requirements:-
12
+ 1. Python3
13
+ 2. Numpy
14
+ 3. OpenCV 4
15
+ 4. Tensorflow 2
16
+ 5. H5py
17
+ 6. Imutils
18
+ 7. Progressbar
19
+ 8. Scikit-Learn
20
+ ## How to Run Code:-
21
+
22
+ ### Training the length and angle models:-
23
+
24
+ 1. Download the dataset of images from [here](https://cocodataset.org/#download). Download atleast 20000 images to train models optimally. (We used the COCO dataset to train our model. But any other dataset of general images will also suffice)
25
+ 2. Use the create_blurred.py to generate the motion blurred dataset as ```python create_blurred.py -i <path_to_input_dir> -o <path_to_output_dir> [-m <optional_number_of_images_to_generate>```. The output directory to store images must exist. The script randomly blurs the images using a random blur length and angle. The range of blur length and angle can be changes on lines 38-39. The script also generates a json file to store the labels for blur length and angle. Note that for blur angle we consider all angle over 180 degrees to be cyclic and wrap around (example 240 is 240-180=60) as it doesn't affect the PSF and significantly reduces the number of classes.
26
+ 3. Use the create_fft.py to generate the fast-fourier transform images of the blurred images to use for training. Run the script as ```python create_fft.py -i <path_to_input_dir> -o <path_to_output_dir>```. The input directory is the folder where the blurred images are stored. The output directory must be created manually.
27
+ 4. Use the build_dataset.py to generate the hdf5 dataset to train. We use this to overcome the bottleneck of working with a large number of images in memory. Run the script as ```python build_dataset.py -m <flag to determine which model is being trained: use either "angle" or "length"> -i <path to input fft images> -to <output hdf5 train file name/path. Must end with .hd5f extension> -vo <path/filename to output hd5f val data> -l <path to input labels json file. properly input either angle or length labels>```. We have resized our images to (224x224) to facilitate training. If you plan to use a different size change the lines 51 and 64. Before this script is run, make sure to delete any previously present .hdf5 files.
28
+ 5. Use the angle_model_train.py script to train the model to estimate the angle parameter of the blur. Change the path to the train and val hdf5 files on lines 17 and 18 and run the script as ```python angle_model_train -o <path to store output metrics. Must be created and empyt at the start of training> [-m <model checkpoint path to resume training> [-e <current epoch to restart training from>```.
29
+ 6. Similarly, length_model_train.py can be used to train the length model.
30
+ 7. Remember to properly modify all variables in the train files
31
+
32
+ ### Testing the models to deblur images
33
+
34
+ Run the deblur_img.py script as ```python deblur_img.py -i <path to input blur image> -a <path to trained angle model> -l <path to trained length model>```. The final deblurred image is saved as result.jpg on the same directory as the script.
angle_model_train.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sidekick.nn.conv.angle_model import MiniVgg
2
+ from sidekick.io.hdf5datagen import Hdf5DataGen
3
+ from sidekick.callbs.manualcheckpoint import ManualCheckpoint
4
+ from sidekick.callbs.trainmonitor import TrainMonitor
5
+ from sidekick.prepro.process import Process
6
+ from sidekick.prepro.imgtoarrayprepro import ImgtoArrPrePro
7
+ from tensorflow.keras.optimizers import SGD
8
+ from tensorflow.keras.models import load_model
9
+ import argparse
10
+
11
+ ap= argparse.ArgumentParser()
12
+ ap.add_argument('-o','--output', type=str, required=True ,help="Path to output directory to store metrics")
13
+ ap.add_argument('-m', '--model', help='Path to checkpointed model')
14
+ ap.add_argument('-e','--epoch', type=int, default=0, help="Starting epoch of training")
15
+ args= vars(ap.parse_args())
16
+
17
+ hdf5_train_path= "train.hdf5"
18
+ hdf5_val_path= "val.hdf5"
19
+ epochs= 50
20
+ lr= 1e-2
21
+ batch_size= 32
22
+ num_classes= 180
23
+ fig_path= args['output']+"train_plot.jpg"
24
+ json_path= args['output']+"train_values.json"
25
+
26
+ print('[NOTE]:- Building Dataset...\n')
27
+ pro= Process(224, 224)
28
+ i2a= ImgtoArrPrePro()
29
+
30
+ train_gen= Hdf5DataGen(hdf5_train_path, batch_size, num_classes, preprocessors=[pro, i2a])
31
+ val_gen= Hdf5DataGen(hdf5_val_path, batch_size, num_classes, preprocessors=[pro, i2a])
32
+
33
+
34
+ if args['model'] is None:
35
+ print("[NOTE]:- Building model from scratch...")
36
+ model= MiniVgg.build(224, 224, 1, num_classes)
37
+ opt= SGD(learning_rate=lr, momentum=0.9, nesterov=True)
38
+ model.compile(loss="categorical_crossentropy", metrics=['accuracy'], optimizer=opt)
39
+ else:
40
+ print("[NOTE]:- Building model {}\n".format(args['model']))
41
+ model= load_model(args['model'])
42
+
43
+ callbacks= [ManualCheckpoint(args['output'], save_at=1, start_from=args['epoch']),
44
+ TrainMonitor(figPath= fig_path, jsonPath= json_path, startAt=args['epoch'])]
45
+
46
+ print("[NOTE]:- Training model...\n")
47
+
48
+ model.fit_generator(train_gen.generator(),
49
+ steps_per_epoch=train_gen.data_length//batch_size,
50
+ validation_data= val_gen.generator(),
51
+ validation_steps= val_gen.data_length//batch_size,
52
+ epochs=epochs,
53
+ max_queue_size=10,
54
+ callbacks= callbacks,
55
+ initial_epoch=args['epoch'])
build_dataset.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from sklearn.preprocessing import LabelEncoder, LabelBinarizer
3
+ from sklearn.model_selection import train_test_split
4
+ from sidekick.io.hdf5_writer import Hdf5Writer
5
+ from imutils import paths
6
+ import cv2
7
+ import os
8
+ import progressbar
9
+ import json
10
+ import argparse
11
+
12
+ ap= argparse.ArgumentParser()
13
+ ap.add_argument('--model_training', '-m', required=True, help='Flag to determine which model is trained. Choose from "angle" and "length".')
14
+ ap.add_argument('--input_dir', '-i', required=True, help='Path to input dir for images')
15
+ ap.add_argument('--train_output_file', '-to', required=True, help='Path to train output file. Must not exist by default.')
16
+ ap.add_argument('--val_output_file', '-vo', required=True, help='Path to val output file. Must not exist by default.')
17
+ ap.add_argument('--label_file', '-l', required=True, help='Path to input training labels.')
18
+
19
+ args= vars(ap.parse_args())
20
+
21
+ model_flag= args['model_training']
22
+ data_path= args['input_dir']
23
+ hdf5_train= args['train_output_file']
24
+ hdf5_test= args['val_output_file']
25
+ label_file= args['label_file']
26
+
27
+ class_to_use= []
28
+ f= open(label_file, 'r')
29
+ label_dict= json.loads(f.read())
30
+
31
+
32
+ train_paths= list(paths.list_images(data_path))
33
+ train_labels= [label_dict[t.split(os.path.sep)[-1]] for t in train_paths]
34
+
35
+ if model_flag=='angle':
36
+ le= LabelEncoder()
37
+ train_labels= le.fit_transform(train_labels)
38
+ print(le.classes_)
39
+ print("Number of classes are: {}".format(len(le.classes_)))
40
+
41
+ train_paths, test_paths, train_labels, test_labels= train_test_split(train_paths,train_labels,
42
+ test_size=0.2)
43
+
44
+ print(train_paths[10], train_labels[10], test_paths[10], test_labels[10])
45
+
46
+ files= [('train', train_paths, train_labels, hdf5_train),
47
+ ('val', test_paths, test_labels, hdf5_test)]
48
+
49
+ for optype, paths, labels, output_path in files:
50
+
51
+ dat_writer= Hdf5Writer((len(paths), 224, 224), output_path)
52
+
53
+ # Initializing the progress bar display
54
+ display=["Building Dataset: ", progressbar.Percentage(), " ",
55
+ progressbar.Bar(), " ", progressbar.ETA()]
56
+
57
+ # Start the progress bar
58
+ progress= progressbar.ProgressBar(maxval=len(paths), widgets=display).start()
59
+
60
+ # Iterate through each img path
61
+ for (i, (p, l)) in enumerate(zip(paths,labels)):
62
+ img= cv2.imread(p)
63
+ img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
64
+ img = cv2.resize(img, (224, 224))
65
+ img= img.astype('float') / 255.0
66
+
67
+
68
+ dat_writer.add([img], [l])
69
+ progress.update(i)
70
+
71
+ # Finish the progress for one type
72
+ progress.finish()
73
+ dat_writer.close()
create_blurred.py ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import os
3
+ import cv2
4
+ import random
5
+ import json
6
+ import argparse
7
+
8
+ ap= argparse.ArgumentParser()
9
+ ap.add_argument('--input_dir', '-i', required=True, help='Path to input dir for images')
10
+ ap.add_argument('--output_dir', '-o', required=True, help='Path to output dir to store files. Must be created')
11
+ ap.add_argument('--max_imgs', '-m', default=20000, type=int, help='Max number of images to generate')
12
+
13
+ args= vars(ap.parse_args())
14
+
15
+ def apply_motion_blur(image, size, angle):
16
+ k = np.zeros((size, size), dtype=np.float32)
17
+ k[ (size-1)// 2 , :] = np.ones(size, dtype=np.float32)
18
+ k = cv2.warpAffine(k, cv2.getRotationMatrix2D( (size / 2 -0.5 , size / 2 -0.5 ) , angle, 1.0), (size, size) )
19
+ k = k * ( 1.0 / np.sum(k) )
20
+ return cv2.filter2D(image, -1, k)
21
+
22
+
23
+ folder = args['input_dir']
24
+ folder_save = args['output_dir']
25
+ max_images = args['max_imgs']
26
+
27
+ print(max_images)
28
+
29
+
30
+ labels_angle = {}
31
+ labels_length= {}
32
+ images_done = 0
33
+ for filename in os.listdir(folder):
34
+ img = cv2.imread(os.path.join(folder,filename))
35
+ if img is not None and img.shape[1] > img.shape[0]:
36
+ img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
37
+ img_resized = cv2.resize(img_gray, (640,480), interpolation = cv2.INTER_AREA)
38
+ length = random.randint(20,40)
39
+ angle = random.randint(0,359)
40
+ blurred = apply_motion_blur(img_resized, length, angle)
41
+ cv2.imwrite(os.path.join(folder_save,filename), blurred)
42
+ if angle>=180:
43
+ angle_a= angle - 180
44
+ else:
45
+ angle_a= angle
46
+ labels_angle[filename] = angle_a
47
+ labels_length[filename]= length
48
+ images_done += 1
49
+ print("%s done"%images_done)
50
+ if(images_done == max_images):
51
+ print('Done!!!')
52
+ break
53
+
54
+ with open('angle_labels.json', 'w') as file:
55
+ json.dump(labels_angle, file)
56
+ with open('length_labels.json', 'w') as file:
57
+ json.dump(labels_length, file)
58
+
59
+
create_fft.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ import os
3
+ import cv2
4
+ import argparse
5
+
6
+ ap = argparse.ArgumentParser()
7
+ ap.add_argument('--input_dir', '-i', required=True, help='Path to input dir for images')
8
+ ap.add_argument('--output_dir', '-o', required=True, help='Path to output dir to store files. Must be created')
9
+
10
+ args= vars(ap.parse_args())
11
+
12
+
13
+ folder = args['input_dir']
14
+ folder_save = args['output_dir']
15
+
16
+
17
+ labels = {}
18
+ images_done = 0
19
+ for filename in os.listdir(folder):
20
+ img = cv2.imread(os.path.join(folder,filename))
21
+ if img is not None:
22
+ img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
23
+ img_gray = np.float32(img_gray) / 255.0
24
+ f = np.fft.fft2(img_gray)
25
+ fshift = np.fft.fftshift(f)
26
+ mag_spec = 20 * np.log(np.abs(fshift))
27
+ mag_spec = np.asarray(mag_spec, dtype=np.uint8)
28
+ cv2.imwrite(os.path.join(folder_save,filename), mag_spec)
29
+ images_done += 1
30
+ print("%s done"%images_done)
31
+
deblur_img.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ from tensorflow.keras.models import load_model
4
+ from tensorflow.keras.preprocessing.image import img_to_array
5
+ import argparse
6
+
7
+ ap= argparse.ArgumentParser()
8
+ ap.add_argument('--image', '-i', required=True, help='Path to input blurred image')
9
+ ap.add_argument('--angle_model', '-a', required=True, help='Path to trained angle model')
10
+ ap.add_argument('--length_model', '-l', required=True, help='Path to trained length model')
11
+ args= vars(ap.parse_args())
12
+
13
+ def process(ip_image, length, deblur_angle):
14
+ noise = 0.01
15
+ size = 200
16
+ length= int(length)
17
+ angle = (deblur_angle*np.pi) /180
18
+
19
+ psf = np.ones((1, length), np.float32) #base image for psf
20
+ costerm, sinterm = np.cos(angle), np.sin(angle)
21
+ Ang = np.float32([[-costerm, sinterm, 0], [sinterm, costerm, 0]])
22
+ size2 = size // 2
23
+ Ang[:,2] = (size2, size2) - np.dot(Ang[:,:2], ((length-1)*0.5, 0))
24
+ psf = cv2.warpAffine(psf, Ang, (size, size), flags=cv2.INTER_CUBIC) #Warp affine to get the desired psf
25
+ # cv2.imshow("PSF",psf)
26
+ # cv2.waitKey(0)
27
+ # cv2.destroyAllWindows()
28
+
29
+ gray = ip_image
30
+ gray = np.float32(gray) / 255.0
31
+ gray_dft = cv2.dft(gray, flags=cv2.DFT_COMPLEX_OUTPUT) #DFT of the image
32
+ psf /= psf.sum() #Dividing by the sum
33
+ psf_mat = np.zeros_like(gray)
34
+ psf_mat[:size, :size] = psf
35
+ psf_dft = cv2.dft(psf_mat, flags=cv2.DFT_COMPLEX_OUTPUT) #DFT of the psf
36
+ PSFsq = (psf_dft**2).sum(-1)
37
+ imgPSF = psf_dft / (PSFsq + noise)[...,np.newaxis] #H in the equation for wiener deconvolution
38
+ gray_op = cv2.mulSpectrums(gray_dft, imgPSF, 0)
39
+ gray_res = cv2.idft(gray_op,flags = cv2.DFT_SCALE | cv2.DFT_REAL_OUTPUT) #Inverse DFT
40
+ gray_res = np.roll(gray_res, -size//2,0)
41
+ gray_res = np.roll(gray_res, -size//2,1)
42
+
43
+ return gray_res
44
+
45
+
46
+ # Function to visualize the Fast Fourier Transform of the blurred images.
47
+ def create_fft(img):
48
+ img = np.float32(img) / 255.0
49
+ f = np.fft.fft2(img)
50
+ fshift = np.fft.fftshift(f)
51
+ mag_spec = 20 * np.log(np.abs(fshift))
52
+ mag_spec = np.asarray(mag_spec, dtype=np.uint8)
53
+
54
+ return mag_spec
55
+
56
+ # Change this variable with the name of the trained models.
57
+ angle_model_name= args['angle_model']
58
+ length_model_name= args['length_model']
59
+ model1= load_model(angle_model_name)
60
+ model2= load_model(length_model_name)
61
+
62
+ # read blurred image
63
+ ip_image = cv2.imread(args['image'])
64
+ ip_image= cv2.cvtColor(ip_image, cv2.COLOR_BGR2GRAY)
65
+ ip_image= cv2.resize(ip_image, (640, 480))
66
+ # FFT visualization of the blurred image
67
+ fft_img= create_fft(ip_image)
68
+
69
+ # Predicting the psf parameters of length and angle.
70
+ img= cv2.resize(create_fft(ip_image), (224,224))
71
+ img= np.expand_dims(img_to_array(img), axis=0)/ 255.0
72
+ preds= model1.predict(img)
73
+ # angle_value= np.sum(np.multiply(np.arange(0, 180), preds[0]))
74
+ angle_value = np.mean(np.argsort(preds[0])[-3:])
75
+
76
+ print("Predicted Blur Angle: ", angle_value)
77
+ length_value= model2.predict(img)[0][0]
78
+ print("Predicted Blur Length: ",length_value)
79
+
80
+ op_image = process(ip_image, length_value, angle_value)
81
+ op_image = (op_image*255).astype(np.uint8)
82
+ op_image = (255/(np.max(op_image)-np.min(op_image))) * (op_image-np.min(op_image))
83
+
84
+ cv2.imwrite("result.jpg", op_image)
85
+
length_model_train.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sidekick.nn.conv.length_model import MiniVgg
2
+ from sidekick.io.hdf5datagen import Hdf5DataGen
3
+ from sidekick.callbs.manualcheckpoint import ManualCheckpoint
4
+ from tensorflow.keras.models import load_model
5
+ from sidekick.prepro.process import Process
6
+ from sidekick.prepro.imgtoarrayprepro import ImgtoArrPrePro
7
+ from tensorflow.keras.optimizers import SGD
8
+ import argparse
9
+
10
+ ap= argparse.ArgumentParser()
11
+ ap.add_argument('-o','--output', type=str, required=True ,help="Path to output directory")
12
+ ap.add_argument('-m', '--model', help='Path to checkpointed model')
13
+ ap.add_argument('-e','--epoch', type=int, default=0, help="Starting epoch of training")
14
+ args= vars(ap.parse_args())
15
+
16
+ hdf5_train_path= "train.hdf5"
17
+ hdf5_val_path= "val.hdf5"
18
+ epochs= 50
19
+ lr= 1e-2
20
+ batch_size= 32
21
+ num_classes= 1
22
+ fig_path= args['output']+"train_plot.jpg"
23
+ json_path= args['output']+"train_values.json"
24
+
25
+ print('[NOTE]:- Building Dataset...\n')
26
+ pro= Process(224, 224)
27
+ i2a= ImgtoArrPrePro()
28
+
29
+ train_gen= Hdf5DataGen(hdf5_train_path, batch_size, num_classes, encode=False, preprocessors=[pro, i2a])
30
+ val_gen= Hdf5DataGen(hdf5_val_path, batch_size, num_classes, encode=False, preprocessors=[pro, i2a])
31
+
32
+ if args['model'] is None:
33
+ print("[NOTE]:- Building model from scratch...")
34
+ model= MiniVgg.build(224, 224, 1, num_classes)
35
+ opt= SGD(learning_rate=lr, momentum=0.9, nesterov=True)
36
+ model.compile(loss="mean_absolute_percentage_error", optimizer=opt)
37
+ else:
38
+ print("[NOTE]:- Building model {}\n".format(args['model']))
39
+ model= load_model(args['model'])
40
+
41
+ callbacks= [ManualCheckpoint(args['output'], save_at=1, start_from=args['epoch'])]
42
+
43
+ print("[NOTE]:- Training model...\n")
44
+
45
+ model.fit_generator(train_gen.generator(),
46
+ steps_per_epoch=train_gen.data_length//batch_size,
47
+ validation_data= val_gen.generator(),
48
+ validation_steps= val_gen.data_length//batch_size,
49
+ epochs=epochs,
50
+ max_queue_size=10,
51
+ callbacks=callbacks,
52
+ initial_epoch=args['epoch'])
pretrained_models/angle_model.hdf5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3fbe443169ad14a6799704313d16f9066ad8643be0b90806df89311d916c0746
3
+ size 49559272
pretrained_models/length_model.hdf5 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4d6fd68204c720d0ed2643d8ef8f6dc68c677307c9c1a877edef427c5f466ac7
3
+ size 48092168
readme_imgs/img1.jpg ADDED
readme_imgs/img1_result.jpg ADDED
readme_imgs/img2.jpg ADDED
readme_imgs/img2_result.jpg ADDED
readme_imgs/img3.jpg ADDED
readme_imgs/img3_result.jpg ADDED
sidekick/__init__.py ADDED
File without changes
sidekick/__pycache__/__init__.cpython-36.pyc ADDED
Binary file (136 Bytes). View file
 
sidekick/__pycache__/__init__.cpython-37.pyc ADDED
Binary file (138 Bytes). View file
 
sidekick/callbs/__init__.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ from .trainmonitor import TrainMonitor
2
+ from .manualcheckpoint import ManualCheckpoint
sidekick/callbs/__pycache__/__init__.cpython-36.pyc ADDED
Binary file (278 Bytes). View file
 
sidekick/callbs/__pycache__/__init__.cpython-37.pyc ADDED
Binary file (258 Bytes). View file
 
sidekick/callbs/__pycache__/manualcheckpoint.cpython-36.pyc ADDED
Binary file (1.1 kB). View file
 
sidekick/callbs/__pycache__/manualcheckpoint.cpython-37.pyc ADDED
Binary file (1.03 kB). View file
 
sidekick/callbs/__pycache__/trainmonitor.cpython-36.pyc ADDED
Binary file (2.01 kB). View file
 
sidekick/callbs/__pycache__/trainmonitor.cpython-37.pyc ADDED
Binary file (1.99 kB). View file
 
sidekick/callbs/manualcheckpoint.py ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tensorflow.keras.callbacks import Callback
2
+ import os
3
+
4
+ class ManualCheckpoint(Callback):
5
+ def __init__(self, output, save_at=3, start_from=0):
6
+ super(Callback, self).__init__()
7
+
8
+ self.output= output
9
+ self.save_at= save_at
10
+ self.initial_epoch= start_from
11
+
12
+ def on_epoch_end(self, epoch, logs={}):
13
+ if (self.initial_epoch+1) % self.save_at==0:
14
+ save_path= os.path.sep.join([self.output,
15
+ "weights-epoch {}.hdf5".format(self.initial_epoch+1)])
16
+
17
+ self.model.save(save_path, overwrite=True)
18
+
19
+ self.initial_epoch+=1
sidekick/callbs/trainmonitor.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tensorflow.keras import callbacks
2
+ import matplotlib
3
+ matplotlib.use('Agg')
4
+ import matplotlib.pyplot as plt
5
+ import numpy as np
6
+ import json
7
+ import os
8
+
9
+ class TrainMonitor(callbacks.BaseLogger):
10
+ def __init__(self, figPath, jsonPath=None, startAt=0):
11
+ super(TrainMonitor, self).__init__()
12
+
13
+ self.figPath= figPath
14
+ self.jsonPath= jsonPath
15
+ self.startAt= startAt
16
+
17
+ def on_train_begin(self, logs={}):
18
+ self.H={}
19
+
20
+ if self.jsonPath is not None:
21
+ if os.path.exists(self.jsonPath):
22
+ self.H = json.loads(open(self.jsonPath).read())
23
+
24
+ if self.startAt > 0:
25
+ for k in self.H.keys():
26
+ self.H[k] = self.H[k][:self.startAt]
27
+
28
+ def on_epoch_end(self, epoch, logs={}):
29
+ for keys, values in logs.items():
30
+ l= self.H.get(keys, [])
31
+ l.append(float(values))
32
+ self.H[keys] = l
33
+
34
+ if self.jsonPath is not None:
35
+ with open(self.jsonPath, 'w') as f:
36
+ f.write(json.dumps(self.H))
37
+ f.close()
38
+
39
+ if len(self.H["loss"]) > 1:
40
+ N = np.arange(0, len(self.H["loss"]), 1)
41
+ plt.style.use("ggplot")
42
+ plt.figure()
43
+ plt.plot(N, self.H["loss"], label="train_loss")
44
+ plt.plot(N, self.H["val_loss"], label="val_loss")
45
+ plt.plot(N, self.H["accuracy"], label="train_acc")
46
+ plt.plot(N, self.H["val_accuracy"], label="val_acc")
47
+ plt.title("Training Loss and Accuracy [Epoch {}]".format(len(self.H["loss"])))
48
+ plt.xlabel("Epoch #")
49
+ plt.ylabel("Loss/Accuracy")
50
+ plt.legend()
51
+ plt.savefig(self.figPath)
52
+ plt.close()
sidekick/datah/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .loader import Loader
sidekick/datah/__pycache__/__init__.cpython-36.pyc ADDED
Binary file (179 Bytes). View file
 
sidekick/datah/__pycache__/__init__.cpython-37.pyc ADDED
Binary file (181 Bytes). View file
 
sidekick/datah/__pycache__/loader.cpython-36.pyc ADDED
Binary file (1.05 kB). View file
 
sidekick/datah/__pycache__/loader.cpython-37.pyc ADDED
Binary file (1.03 kB). View file
 
sidekick/datah/loader.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import cv2
2
+ import numpy as np
3
+ import os
4
+
5
+ class Loader:
6
+ def __init__(self,preprocessors=None):
7
+ self.preprocessors=preprocessors
8
+ if self.preprocessors is None:
9
+ self.preprocessors=[]
10
+
11
+ def load(self,imgpaths,verbose=-1):
12
+ data=[]
13
+ labels=[]
14
+
15
+ for (i,imgpath) in enumerate(imgpaths):
16
+ image=cv2.imread(imgpath)
17
+ label= imgpath.split(os.path.sep)[-2]
18
+
19
+ if self.preprocessors is not None:
20
+ for p in self.preprocessors:
21
+ image=p.preprocess(image)
22
+
23
+ data.append(image)
24
+ labels.append(label)
25
+
26
+ if verbose > 0 and i >0 and (i+1)% verbose==0:
27
+ print("processed:{}/{}".format(i+1,len(imgpaths)))
28
+
29
+ print("Done!")
30
+ return (np.array(data),np.array(labels))
sidekick/eval/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .calc_ranked import calculate_ranked
sidekick/eval/__pycache__/__init__.cpython-36.pyc ADDED
Binary file (222 Bytes). View file
 
sidekick/eval/__pycache__/calc_ranked.cpython-36.pyc ADDED
Binary file (518 Bytes). View file
 
sidekick/eval/calc_ranked.py ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+
3
+ def calculate_ranked(preds, labels):
4
+ rank1=0
5
+ rank5=0
6
+
7
+ for p,l in zip(preds, labels):
8
+ #sort preds in descending order of their confidence and return the indices of these
9
+ p= np.argsort(p)[::-1]
10
+
11
+ # checking for rank5
12
+ if l in p[:5]:
13
+ rank5+=1
14
+ # checking rank1
15
+ if l==p[0]:
16
+ rank1+=1
17
+
18
+
19
+ # Final accuracies
20
+ rank1= rank1/len(labels)
21
+ rank5= rank5/len(labels)
22
+
23
+ return rank1,rank5
sidekick/io/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ from .hdf5_writer import Hdf5Writer
sidekick/io/__pycache__/__init__.cpython-36.pyc ADDED
Binary file (214 Bytes). View file
 
sidekick/io/__pycache__/__init__.cpython-37.pyc ADDED
Binary file (194 Bytes). View file
 
sidekick/io/__pycache__/hdf5_writer.cpython-36.pyc ADDED
Binary file (1.8 kB). View file
 
sidekick/io/__pycache__/hdf5_writer.cpython-37.pyc ADDED
Binary file (1.78 kB). View file
 
sidekick/io/__pycache__/hdf5datagen.cpython-36.pyc ADDED
Binary file (1.48 kB). View file
 
sidekick/io/__pycache__/hdf5datagen.cpython-37.pyc ADDED
Binary file (1.46 kB). View file
 
sidekick/io/hdf5_writer.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import h5py
2
+ import os
3
+
4
+ class Hdf5Writer:
5
+ def __init__(self, dims, outputPath, dbName='Images', buffSize= 1000):
6
+
7
+ # throw an error if the file already exists
8
+ if os.path.exists(outputPath):
9
+ raise ValueError("PATH ALREADY PRESENT. PLEASE DELETE FILES"
10
+ "BEFORE PROCEEDING.")
11
+
12
+ # database to store data
13
+ self.db= h5py.File(outputPath, 'w')
14
+ # define dataset containers to store data and labels
15
+ self.data= self.db.create_dataset(dbName, dims, dtype='float')
16
+ self.labels= self.db.create_dataset('Labels', shape=(dims[0],), dtype='int')
17
+
18
+ # defining a buffer and index variable for the buffer
19
+ self.buffSize= buffSize
20
+ self.buffer= {"data": [], "labels": []}
21
+ self.idx= 0
22
+
23
+ def add(self, values, labels):
24
+ self.buffer['data'].extend(values)
25
+ self.buffer['labels'].extend(labels)
26
+
27
+ if len(self.buffer['data'])>=self.buffSize:
28
+ self.flush()
29
+
30
+ def flush(self):
31
+ # When buffer size is reached flush data to dataset container
32
+ temp_idx= self.idx + len(self.buffer['data'])
33
+ # index from prev_idx to new_idx
34
+ self.data[self.idx:temp_idx]= self.buffer['data']
35
+ self.labels[self.idx:temp_idx]= self.buffer['labels']
36
+ # update new_idx
37
+ self.idx=temp_idx
38
+ # reinitialize the buffer
39
+ self.buffer={'data': [], 'labels': []}
40
+
41
+ def flushClassNames(self, classNames):
42
+
43
+ # Creating a special
44
+ labelNames= self.db.create_dataset('Label_Names', (len(classNames),),
45
+ dtype=h5py.special_dtype(vlen=unicode))
46
+ labelNames[:]= classNames
47
+
48
+ def close(self):
49
+
50
+ if len(self.buffer['data'])>0:
51
+ self.flush()
52
+
53
+ self.db.close()
sidekick/io/hdf5datagen.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tensorflow.keras.utils import to_categorical
2
+ import h5py
3
+ import numpy as np
4
+
5
+ class Hdf5DataGen:
6
+ def __init__(self, dbPath, batchSize, classes, encode=True, aug=None, preprocessors=None):
7
+
8
+ self.db= h5py.File(dbPath, 'r')
9
+ self.batchSize= batchSize
10
+ self.num_classes= classes
11
+ self.encode= encode
12
+ self.aug= aug
13
+ self.preprocessors= preprocessors
14
+
15
+ self.data_length= self.db['Images'].shape[0]
16
+
17
+ def generator(self, counter=np.inf):
18
+ start=0
19
+
20
+ while start< counter:
21
+ for i in np.arange(0, self.data_length, self.batchSize):
22
+ data = self.db['Images'][i:i+self.batchSize]
23
+ labels = self.db['Labels'][i:i + self.batchSize]
24
+
25
+ if self.encode:
26
+ labels= to_categorical(labels, self.num_classes)
27
+
28
+ if self.preprocessors is not None:
29
+ processed_data=[]
30
+
31
+ for d in data:
32
+ for p in self.preprocessors:
33
+ d= p.preprocess(d)
34
+
35
+ processed_data.append(d)
36
+
37
+ data= np.array(processed_data)
38
+
39
+ if self.aug is not None:
40
+ # Notice the next to get next value from generator
41
+ data, labels= next(self.aug.flow(
42
+ data, labels, batch_size= self.batchSize
43
+ ))
44
+
45
+ yield (data, labels)
46
+
47
+ start+=1
48
+
49
+ def close(self):
50
+ self.db.close()
sidekick/nn/conv/__pycache__/__init__.cpython-37.pyc ADDED
Binary file (348 Bytes). View file
 
sidekick/nn/conv/__pycache__/angle_model.cpython-37.pyc ADDED
Binary file (1.89 kB). View file
 
sidekick/nn/conv/__pycache__/length_model.cpython-37.pyc ADDED
Binary file (1.88 kB). View file