Skip to content

Commit a8a1754

Browse files
committed
Add linear motion noise
1 parent c92e5b3 commit a8a1754

File tree

2 files changed

+146
-3
lines changed

2 files changed

+146
-3
lines changed

Augmentor/Operations.py

Lines changed: 131 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,12 @@
2929
from math import floor, ceil
3030

3131
import numpy as np
32-
# from skimage import img_as_ubyte
33-
# from skimage import transform
3432

3533
import os
3634
import random
3735
import warnings
36+
import cv2
37+
from skimage.draw import line
3838

3939
# Python 2-3 compatibility - not currently needed.
4040
# try:
@@ -735,6 +735,135 @@ def do(image):
735735
return augmented_images
736736

737737

738+
class LinearMotion(Operation):
739+
"""
740+
This class is used to perform linear motion on images.
741+
The algorithm is heavily based on https://github.com/lospooky/pyblur/tree/master/pyblur
742+
"""
743+
744+
def __init__(self, probability, size, angle, linetype):
745+
"""
746+
:param probability: Controls the probability that the operation is
747+
performed when it is invoked in the pipeline.
748+
:param size: size of linear motion kernel
749+
:param angle: angle of linear motion
750+
:param linetype: type of linear motion line
751+
752+
"""
753+
Operation.__init__(self, probability)
754+
self.size = size
755+
self.angle = angle
756+
self.line_dict = self.gen_motion_lines()
757+
self.kernel = self.get_kernel(size, angle, linetype)
758+
759+
def get_kernel(self, size, angle, linetype):
760+
kernel_width = size
761+
kernel_center = int(math.floor(size / 2))
762+
angle = self.get_sanitize_angle_value(kernel_center, angle)
763+
kernel = np.zeros((kernel_width, kernel_width), dtype=np.float32)
764+
line_anchors = self.line_dict[size][angle]
765+
if (linetype == 'right'):
766+
line_anchors[0] = kernel_center
767+
line_anchors[1] = kernel_center
768+
if (linetype == 'left'):
769+
line_anchors[2] = kernel_center
770+
line_anchors[3] = kernel_center
771+
rr, cc = line(line_anchors[0], line_anchors[1], line_anchors[2], line_anchors[3])
772+
kernel[rr, cc] = 1
773+
normalization_factor = np.count_nonzero(kernel)
774+
kernel = kernel / normalization_factor
775+
return kernel
776+
777+
def get_nearest_value(self, theta, valid_angles):
778+
idx = (np.abs(valid_angles - theta)).argmin()
779+
return valid_angles[idx]
780+
781+
def get_sanitize_angle_value(self, kernel_center, angle):
782+
num_distinct_lines = kernel_center * 4
783+
angle = math.fmod(angle, 180.0)
784+
valid_line_angles = np.linspace(0, 180, num_distinct_lines, endpoint=False)
785+
angle = self.get_nearest_value(angle, valid_line_angles)
786+
return angle
787+
788+
def gen_motion_lines(self):
789+
790+
ret = {}
791+
# 3x3 lines
792+
lines = {}
793+
lines[0] = [1, 0, 1, 2]
794+
lines[45] = [2, 0, 0, 2]
795+
lines[90] = [0, 1, 2, 1]
796+
lines[135] = [0, 0, 2, 2]
797+
ret[3] = lines
798+
799+
# 5x5 lines
800+
lines = {}
801+
lines[0] = [2, 0, 2, 4]
802+
lines[22.5] = [3, 0, 1, 4]
803+
lines[45] = [0, 4, 4, 0]
804+
lines[67.5] = [0, 3, 4, 1]
805+
lines[90] = [0, 2, 4, 2]
806+
lines[112.5] = [0, 1, 4, 3]
807+
lines[135] = [0, 0, 4, 4]
808+
lines[157.5] = [1, 0, 3, 4]
809+
ret[5] = lines
810+
811+
# 7x7 lines
812+
lines = {}
813+
lines[0] = [3, 0, 3, 6]
814+
lines[15] = [4, 0, 2, 6]
815+
lines[30] = [5, 0, 1, 6]
816+
lines[45] = [6, 0, 0, 6]
817+
lines[60] = [6, 1, 0, 5]
818+
lines[75] = [6, 2, 0, 4]
819+
lines[90] = [0, 3, 6, 3]
820+
lines[105] = [0, 2, 6, 4]
821+
lines[120] = [0, 1, 6, 5]
822+
lines[135] = [0, 0, 6, 6]
823+
lines[150] = [1, 0, 5, 6]
824+
lines[165] = [2, 0, 4, 6]
825+
ret[7] = lines
826+
827+
# 9x9 lines
828+
lines = {}
829+
lines[0] = [4, 0, 4, 8]
830+
lines[11.25] = [5, 0, 3, 8]
831+
lines[22.5] = [6, 0, 2, 8]
832+
lines[33.75] = [7, 0, 1, 8]
833+
lines[45] = [8, 0, 0, 8]
834+
lines[56.25] = [8, 1, 0, 7]
835+
lines[67.5] = [8, 2, 0, 6]
836+
lines[78.75] = [8, 3, 0, 5]
837+
lines[90] = [8, 4, 0, 4]
838+
lines[101.25] = [0, 3, 8, 5]
839+
lines[112.5] = [0, 2, 8, 6]
840+
lines[123.75] = [0, 1, 8, 7]
841+
lines[135] = [0, 0, 8, 8]
842+
lines[146.25] = [1, 0, 7, 8]
843+
lines[157.5] = [2, 0, 6, 8]
844+
lines[168.75] = [3, 0, 5, 8]
845+
ret[9] = lines
846+
847+
return ret
848+
849+
def perform_operation(self, images):
850+
"""
851+
"""
852+
853+
def do(image):
854+
img_np = np.array(image)
855+
img_np_filtered = cv2.filter2D(img_np, -1, self.kernel)
856+
857+
return Image.fromarray(img_np_filtered)
858+
859+
augmented_images = []
860+
861+
for image in images:
862+
augmented_images.append(do(image))
863+
864+
return augmented_images
865+
866+
738867
class RotateRange(Operation):
739868
"""
740869
This class is used to perform rotations on images by arbitrary numbers of
@@ -1832,7 +1961,6 @@ def __init__(self, probability, hue_shift, saturation_scale, saturation_shift, v
18321961
self.value_shift = value_shift
18331962

18341963
def perform_operation(self, images):
1835-
18361964
def do(image):
18371965
hsv = np.array(image.convert("HSV"), 'float64')
18381966
hsv /= 255.

Augmentor/Pipeline.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,21 @@ def flip_top_bottom(self, probability):
10111011
else:
10121012
self.add_operation(Flip(probability=probability, top_bottom_left_right="TOP_BOTTOM"))
10131013

1014+
def linear_motion(self, probability, size, angle, linetype="left"):
1015+
"""
1016+
Perform linear motion blurring
1017+
1018+
:param probability: A value between 0 and 1 representing the
1019+
probability that the operation should be performed.
1020+
:param size: size of kernel
1021+
:param angle: motion angle3
1022+
:param linetype: type of line (left, right or full (both left and right))
1023+
"""
1024+
if not 0 < probability <= 1:
1025+
raise ValueError(Pipeline._probability_error_text)
1026+
else:
1027+
self.add_operation(LinearMotion(probability=probability, size=size, angle=angle, linetype=linetype))
1028+
10141029
def flip_left_right(self, probability):
10151030
"""
10161031
Flip (mirror) the image along its horizontal axis, i.e. from left to

0 commit comments

Comments
 (0)