--- a +++ b/dsb2018_topcoders/selim/aug/functional.py @@ -0,0 +1,402 @@ +import cv2 +cv2.setNumThreads(0) +cv2.ocl.setUseOpenCL(False) +import numpy as np +import math +from scipy.ndimage.filters import gaussian_filter +from functools import wraps + + +def vflip(img): + return cv2.flip(img, 0) + + +def hflip(img): + return cv2.flip(img, 1) + + +def random_flip(img, code): + return cv2.flip(img, code) + + +def transpose(img): + return img.transpose(1, 0, 2) if len(img.shape) > 2 else img.transpose(1, 0) + + +def rot90(img, factor): + img = np.rot90(img, factor) + return np.ascontiguousarray(img) + + +def rotate(img, angle): + height, width = img.shape[0:2] + mat = cv2.getRotationMatrix2D((width/2, height/2), angle, 1.0) + img = cv2.warpAffine(img, mat, (width, height), + flags=cv2.INTER_LINEAR, + borderMode=cv2.BORDER_REFLECT_101) + return img + + +def shift_scale_rotate(img, angle, scale, dx, dy): + height, width = img.shape[:2] + + cc = math.cos(angle/180*math.pi) * scale + ss = math.sin(angle/180*math.pi) * scale + rotate_matrix = np.array([[cc, -ss], [ss, cc]]) + + box0 = np.array([[0, 0], [width, 0], [width, height], [0, height], ]) + box1 = box0 - np.array([width/2, height/2]) + box1 = np.dot(box1, rotate_matrix.T) + np.array([width/2+dx*width, height/2+dy*height]) + + box0 = box0.astype(np.float32) + box1 = box1.astype(np.float32) + mat = cv2.getPerspectiveTransform(box0, box1) + img = cv2.warpPerspective(img, mat, (width, height), + flags=cv2.INTER_LINEAR, + borderMode=cv2.BORDER_REFLECT_101) + + return img + + +def center_crop(img, height, width): + h, w, c = img.shape + dy = (h-height)//2 + dx = (w-width)//2 + y1 = dy + y2 = y1 + height + x1 = dx + x2 = x1 + width + img = img[y1:y2, x1:x2, :] + return img + + +def clip(img, dtype, maxval): + return np.clip(img, 0, maxval).astype(dtype) + + +def clipped(func): + @wraps(func) + def wrapped_function(img, *args, **kwargs): + dtype, maxval = img.dtype, np.max(img) + return clip(func(img, *args, **kwargs), dtype, maxval) + return wrapped_function + + +def shift_hsv(img, hue_shift, sat_shift, val_shift): + dtype = img.dtype + img = cv2.cvtColor(img, cv2.COLOR_RGB2HSV).astype(np.int32) + h, s, v = cv2.split(img) + h = cv2.add(h, hue_shift) + h = np.where(h < 0, 255 - h, h) + h = np.where(h > 255, h - 255, h) + h = h.astype(dtype) + s = clip(cv2.add(s, sat_shift), dtype, 255 if dtype == np.uint8 else 1.) + v = clip(cv2.add(v, val_shift), dtype, 255 if dtype == np.uint8 else 1.) + img = cv2.merge((h, s, v)).astype(dtype) + img = cv2.cvtColor(img, cv2.COLOR_HSV2RGB) + return img + +@clipped +def shift_rgb(img, r_shift, g_shift, b_shift): + img[...,0] = img[...,0] + r_shift + img[...,1] = img[...,1] + g_shift + img[...,2] = img[...,2] + b_shift + return img + +def clahe(img, clipLimit=2.0, tileGridSize=(8,8)): + img_yuv = cv2.cvtColor(img, cv2.COLOR_RGB2LAB) + clahe = cv2.createCLAHE(clipLimit=clipLimit, tileGridSize=tileGridSize) + img_yuv[:, :, 0] = clahe.apply(img_yuv[:, :, 0]) + img_output = cv2.cvtColor(img_yuv, cv2.COLOR_LAB2RGB) + return img_output + +def blur(img, ksize): + return cv2.blur(img, (ksize, ksize)) + +def median_blur(img, ksize): + return cv2.medianBlur(img, ksize) + +def motion_blur(img): + kernel = np.zeros((9, 9)) + xs, ys = np.random.randint(0, kernel.shape[1]), np.random.randint(0, kernel.shape[0]) + xe, ye = np.random.randint(0, kernel.shape[1]), np.random.randint(0, kernel.shape[0]) + cv2.line(kernel, (xs, ys), (xe, ye), 1, thickness=1) + return cv2.filter2D(img, -1, kernel / np.sum(kernel)) + +def random_polosa(img): + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + if np.mean(gray) < 100: + empty = np.zeros(img.shape[:2], dtype=np.uint8) + xs, ys = np.random.randint(0, empty.shape[1]), np.random.randint(0, empty.shape[0]) + xe, ye = np.random.randint(0, empty.shape[1]), np.random.randint(0, empty.shape[0]) + factor = np.random.randint(1, 10) / 3. + cv2.line(empty, (xs, ys), (xe, ye), np.max(gray) / factor, thickness=np.random.randint(10, 100)) + empty = cv2.blur(empty, (5, 5)) + empty = empty | gray + return cv2.cvtColor(empty, cv2.COLOR_GRAY2RGB) + return img + +def distort1(img, k=0, dx=0, dy=0): + """" + ## unconverntional augmnet ################################################################################3 + ## https://stackoverflow.com/questions/6199636/formulas-for-barrel-pincushion-distortion + + ## https://stackoverflow.com/questions/10364201/image-transformation-in-opencv + ## https://stackoverflow.com/questions/2477774/correcting-fisheye-distortion-programmatically + ## http://www.coldvision.io/2017/03/02/advanced-lane-finding-using-opencv/ + + ## barrel\pincushion distortion + """ + height, width = img.shape[:2] + # map_x, map_y = + # cv2.initUndistortRectifyMap(intrinsics, dist_coeffs, None, None, (width,height),cv2.CV_32FC1) + # https://stackoverflow.com/questions/6199636/formulas-for-barrel-pincushion-distortion + # https://stackoverflow.com/questions/10364201/image-transformation-in-opencv + k = k * 0.00001 + dx = dx * width + dy = dy * height + x, y = np.mgrid[0:width:1, 0:height:1] + x = x.astype(np.float32) - width/2 - dx + y = y.astype(np.float32) - height/2 - dy + theta = np.arctan2(y, x) + d = (x*x + y*y)**0.5 + r = d*(1+k*d*d) + map_x = r*np.cos(theta) + width/2 + dx + map_y = r*np.sin(theta) + height/2 + dy + + img = cv2.remap(img, map_x, map_y, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101) + return img + + +def distort2(img, num_steps=10, xsteps=[], ysteps=[]): + """ + #http://pythology.blogspot.sg/2014/03/interpolation-on-regular-distorted-grid.html + ## grid distortion + """ + height, width = img.shape[:2] + + x_step = width // num_steps + xx = np.zeros(width, np.float32) + prev = 0 + for idx, x in enumerate(range(0, width, x_step)): + start = x + end = x + x_step + if end > width: + end = width + cur = width + else: + cur = prev + x_step*xsteps[idx] + + xx[start:end] = np.linspace(prev, cur, end-start) + prev = cur + + y_step = height // num_steps + yy = np.zeros(height, np.float32) + prev = 0 + for idx, y in enumerate(range(0, height, y_step)): + start = y + end = y + y_step + if end > height: + end = height + cur = height + else: + cur = prev + y_step*ysteps[idx] + + yy[start:end] = np.linspace(prev, cur, end-start) + prev = cur + + map_x, map_y = np.meshgrid(xx, yy) + map_x = map_x.astype(np.float32) + map_y = map_y.astype(np.float32) + img = cv2.remap(img, map_x, map_y, + interpolation=cv2.INTER_LINEAR, + borderMode=cv2.BORDER_REFLECT_101) + return img + +def elastic_transform_fast(image, alpha, sigma, alpha_affine, random_state=None): + """Elastic deformation of images as described in [Simard2003]_ (with modifications). + .. [Simard2003] Simard, Steinkraus and Platt, "Best Practices for + Convolutional Neural Networks applied to Visual Document Analysis", in + Proc. of the International Conference on Document Analysis and + Recognition, 2003. + + Based on https://gist.github.com/erniejunior/601cdf56d2b424757de5 + """ + if random_state is None: + random_state = np.random.RandomState(1234) + + shape = image.shape + shape_size = shape[:2] + + + # Random affine + center_square = np.float32(shape_size) // 2 + square_size = min(shape_size) // 3 + alpha = float(alpha) + sigma = float(sigma) + alpha_affine = float(alpha_affine) + + pts1 = np.float32([center_square + square_size, [center_square[0] + square_size, center_square[1] - square_size], + center_square - square_size]) + pts2 = pts1 + random_state.uniform(-alpha_affine, alpha_affine, size=pts1.shape).astype(np.float32) + M = cv2.getAffineTransform(pts1, pts2) + + image = cv2.warpAffine(image, M, shape_size[::-1], borderMode=cv2.BORDER_REFLECT_101) + + dx = np.float32(gaussian_filter((random_state.rand(*shape_size) * 2 - 1), sigma) * alpha) + dy = np.float32(gaussian_filter((random_state.rand(*shape_size) * 2 - 1), sigma) * alpha) + + x, y = np.meshgrid(np.arange(shape[1]), np.arange(shape[0])) + + mapx = np.float32(x + dx) + mapy = np.float32(y + dy) + + return cv2.remap(image, mapx, mapy, interpolation=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REFLECT_101) + + +def remap_color(img, bg, center, max): + def get_lut(img, bg, center, max): + ma = np.max(img) + # me = np.mean(img) + # th = np.mean([ma, me]) * 1.5 + th = ma / 2 + gap = 10 + channels = [[], [], []] + range2 = ma - int(th) + for i in range(3): + channels[i].append(np.linspace(bg[i] - gap, center[i] - gap, int(th)).astype(np.uint8)) + channels[i].append(np.linspace(center[i] - gap, max[i] + gap, range2).astype(np.uint8)) + channels[i].append([max[i] + gap] * (256 - sum(map(len, channels[i])))) + channels[i] = np.hstack(channels[i]) + return np.dstack(channels) + + # img = adjust_gamma(img, 5.) + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + if np.mean(gray) > 100: + return img + lut = get_lut(img, bg, center, max) + res = cv2.LUT(img, lut).astype(np.uint8) + return res + +def invert(img): + return 255 - img + +def channel_shuffle(img): + ch_arr = [0, 1, 2] + np.random.shuffle(ch_arr) + img = img[..., ch_arr] + return img + +@clipped +def gauss_noise(image, var): + row, col, ch = image.shape + mean = var + # var = 30 + sigma = var**0.5 + gauss = np.random.normal(mean,sigma,(row,col,ch)) + gauss = gauss.reshape(row,col,ch) + gauss = (gauss - np.min(gauss)).astype(np.uint8) + return image.astype(np.int32) + gauss + +def salt_pepper_noise(image): + #todo + s_vs_p = 0.5 + amount = 0.004 + noisy = image + # Salt mode + num_salt = np.ceil(amount * image.size * s_vs_p) + coords = [np.random.randint(0, i - 1, int(num_salt)) + for i in image.shape] + noisy[coords] = 255 + + # Pepper mode + num_pepper = np.ceil(amount * image.size * (1. - s_vs_p)) + coords = [np.random.randint(0, i - 1, int(num_pepper)) + for i in image.shape] + noisy[coords] = 0 + return noisy + +def poisson_noise(image): + #todo + vals = len(np.unique(image)) + vals = 2 ** np.ceil(np.log2(vals)) + noisy = np.random.poisson(image * vals) / float(vals) + return noisy + +def speckle_noise(image): + #todo + row, col, ch = image.shape + gauss = np.random.randn(row,col,ch) + gauss = gauss.reshape(row,col,ch) + noisy = image + image * gauss + return noisy + +@clipped +def random_brightness(img, alpha): + return alpha * img + +@clipped +def random_contrast(img, alpha): + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + gray = (3.0 * (1.0 - alpha) / gray.size) * np.sum(gray) + return alpha * img + gray + +def to_three_channel_gray(img): + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + invgray = 255 - gray + clahe = cv2.createCLAHE(clipLimit=2, tileGridSize=(8, 8)) + if np.mean(invgray) < np.mean(gray): + invgray, gray = gray, invgray + res = [invgray, gray, clahe.apply(invgray)] + return cv2.merge(res) + +def to_gray(img): + gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) + if np.mean(gray) > 127: + gray = 255 - gray + return cv2.cvtColor(gray, cv2.COLOR_GRAY2RGB) + + +def add_channel(img): + lab = cv2.cvtColor(img, cv2.COLOR_RGB2LAB) + clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(21, 21)) + lab = clahe.apply(lab[:, :, 0]) + if lab.mean() > 127: + lab = 255 - lab + return np.dstack((img, lab)) + + + + +def fix_mask(msk): + # if len(msk.shape) > 2: + # msk[..., 2] = (msk[..., 2] > 127) + # msk[..., 1] = (msk[..., 1] > 127) * (msk[..., 2] == 0) + # msk[..., 0] = (msk[..., 1] == 0) * (msk[..., 2] == 0) + # else: + msk = (msk > 127) + return msk.astype(np.uint8) * 255 + + +def img_to_tensor(im): + return np.moveaxis(im / (255. if im.dtype == np.uint8 else 1), -1, 0).astype(np.float32) + + +def mask_to_tensor(mask, num_classes): + if num_classes > 1: + if 1: + #softmax + long_mask = np.zeros((mask.shape[:2]), dtype=np.int64) + if len(mask.shape) == 3: + for c in range(mask.shape[2]): + long_mask[mask[...,c] > 0] = c + else: + long_mask[mask > 127] = 1 + long_mask[mask == 0] = 0 + return long_mask + else: + mask = img_to_tensor(mask) + else: + mask = np.expand_dims(mask / (255. if mask.dtype == np.uint8 else 1), 0).astype(np.float32) + return mask +