|
29 | 29 | from math import floor, ceil |
30 | 30 |
|
31 | 31 | import numpy as np |
32 | | -# from skimage import img_as_ubyte |
33 | | -# from skimage import transform |
34 | 32 |
|
35 | 33 | import os |
36 | 34 | import random |
37 | 35 | import warnings |
| 36 | +import cv2 |
| 37 | +from skimage.draw import line |
38 | 38 |
|
39 | 39 | # Python 2-3 compatibility - not currently needed. |
40 | 40 | # try: |
@@ -735,6 +735,135 @@ def do(image): |
735 | 735 | return augmented_images |
736 | 736 |
|
737 | 737 |
|
| 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 | + |
738 | 867 | class RotateRange(Operation): |
739 | 868 | """ |
740 | 869 | 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 |
1832 | 1961 | self.value_shift = value_shift |
1833 | 1962 |
|
1834 | 1963 | def perform_operation(self, images): |
1835 | | - |
1836 | 1964 | def do(image): |
1837 | 1965 | hsv = np.array(image.convert("HSV"), 'float64') |
1838 | 1966 | hsv /= 255. |
|
0 commit comments