transforms.py 4.97 KB
Newer Older
valentini's avatar
valentini committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from numpy.core.fromnumeric import size
import torchvision.transforms.functional as TF
import torch.nn.functional as F
import math
import random
import logging
import torch.nn.functional as F
import cv2
import numpy as np
from typing import Sequence

# taken fromcvtorchvision
INTER_MODE = {'NEAREST': cv2.INTER_NEAREST, 'BILINEAR': cv2.INTER_LINEAR, 'BICUBIC': cv2.INTER_CUBIC}
def rotateOpenCv(img, angle, resample='BILINEAR', expand=False, center=None):
    """Rotate the image by angle.
    Args:
        img (PIL Image): PIL Image to be rotated.
        angle ({float, int}): In degrees clockwise order.
        resample ({NEAREST, BILINEAR, BICUBIC}, optional):
            An optional resampling filter.
            See http://pillow.readthedocs.io/en/3.4.x/handbook/concepts.html#filters
            If omitted, or if the image has mode "1" or "P", it is set to PIL.Image.NEAREST.
        expand (bool, optional): Optional expansion flag.
            If true, expands the output image to make it large enough to hold the entire rotated image.
            If false or omitted, make the output image the same size as the input image.
            Note that the expand flag assumes rotation around the center and no translation.
        center (2-tuple, optional): Optional center of rotation.
            Origin is the upper left corner.
            Default is the center of the image.
    """
    imgtype = img.dtype
    h, w, _ = img.shape
    point = center or (w/2, h/2)
    M = cv2.getRotationMatrix2D(point, angle=-angle, scale=1)

    if expand:
        if center is None:
            cos = np.abs(M[0, 0])
            sin = np.abs(M[0, 1])

            # compute the new bounding dimensions of the image
            nW = int((h * sin) + (w * cos))
            nH = int((h * cos) + (w * sin))

            # adjust the rotation matrix to take into account translation
            M[0, 2] += (nW / 2) - point[0]
            M[1, 2] += (nH / 2) - point[1]

            # perform the actual rotation and return the image
            dst = cv2.warpAffine(img, M, (nW, nH))
        else:
            xx = []
            yy = []
            for point in (np.array([0, 0, 1]), np.array([w-1, 0, 1]), np.array([w-1, h-1, 1]), np.array([0, h-1, 1])):
                target = M@point
                xx.append(target[0])
                yy.append(target[1])
            nh = int(math.ceil(max(yy)) - math.floor(min(yy)))
            nw = int(math.ceil(max(xx)) - math.floor(min(xx)))
            # adjust the rotation matrix to take into account translation
            M[0, 2] += (nw - w)/2
            M[1, 2] += (nh - h)/2
            dst = cv2.warpAffine(img, M, (nw, nh), flags=INTER_MODE[resample])
    else:
        dst = cv2.warpAffine(img, M, (w, h), flags=INTER_MODE[resample])
    return dst.astype(imgtype)

class DescreteRandomRotation:
    def __init__(self, angles: Sequence[int]):
        self.angles = angles

    def __call__(self, x):
        angle = random.choice(self.angles)

        return TF.rotate(x, angle)

class DescreteRandomRotationCV2:
    def __init__(self, angles: Sequence[int]):
        self.angles = angles

    def __call__(self, x):
        angle = random.choice(self.angles)

        return rotateOpenCv(x, angle)

def MosaicPatches(x, size_x:int, size_y: int):
    b, c, w, h = x.shape
    h_half, w_half = h // 2, w // 2
    h_size, w_size = h_half, w_half 
    return [
        x[:, :, 0:h_size, 0:w_size],
        x[:, :, 0:h_size, (w - w_size):w],
        x[:, :, (h - h_size):h, 0:w_size],
        x[:, :, (h - h_size):h, (w - w_size):w]]

def RebuildMosaicPatches(x, size_x:int, size_y: int, unfolder):
    b, c, px, py,_, _ = unfolder
    unfold_shape = b, c, px, py, size_x, size_y
    patches_orig = x.view(unfold_shape)
    output_c = 3
    output_h = py * size_y
    output_w = px * size_x
    #reorder the tensor as b,c,w,px,h,py
    #patches_orig = patches_orig.permute(0, 1, 4, 2, 5, 3).contiguous()
    patches_orig = patches_orig.permute(0, 1, 2, 4, 3, 5).contiguous()
    # merge together w,px and h,py
    return patches_orig.view(1, output_c, output_h, output_w)

def paralellCrop(img1: np.ndarray, img2: np.ndarray, crop = 128, scale=1, random=None, cropInfo=None, cropMode=0): 
    if not random: random = np.random

    _, y, x = img1.shape
    
    cropX, cropY = None, None
    if cropInfo is None:
        if cropMode == 'random':
            cropX = int(random.random() * (x - crop))
            cropY = int(random.random() * (y - crop))
        else : #Center
            cropX = int(x/2 - crop/2)
            cropY = int(y/2 - crop/2)
    else:
        cropX, cropY = cropInfo

    cropXEnd = cropX + crop
    cropYEnd = cropY + crop

    cropXprarallel = cropX * scale
    cropYprarallel = cropY * scale
    cropXprarallelEnd = cropXprarallel + (crop * scale)
    cropYprarallelEnd = cropYprarallel + (crop * scale)
    
    img1crop = img1[:, cropY:cropYEnd, cropX:cropXEnd]
    img2crop = img2[:, cropYprarallel:cropYprarallelEnd, cropXprarallel:cropXprarallelEnd]
    
    return img1crop, img2crop, (cropX, cropY)