VisualCloze / data /degradation_toolkit /image_operators.py
lzyhha
clean
af44a4b
import os
import cv2
import numpy as np
import argparse
from skimage.filters import gaussian
from scipy.ndimage.interpolation import map_coordinates
from tqdm import tqdm
from PIL import Image
def single2uint(img):
return np.uint8((img.clip(0, 1)*255.).round())
def uint2single(img):
return np.float32(img/255.)
def Laplacian_edge_detector(img):
# input: [0, 1]
# return: [0, 1] (H, W, 3)
img = np.clip(img*255, 0, 255).astype(np.uint8) # (H, W, 3)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.Laplacian(img, cv2.CV_16S) # (H, W)
img = cv2.convertScaleAbs(img)
img = img.astype(np.float32) / 255.
img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3)
return img
def Laplacian_edge_detector_uint8(img):
# input: [0, 255]
# return: [0, 255] (H, W, 3)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.Laplacian(img, cv2.CV_16S) # (H, W)
img = cv2.convertScaleAbs(img)
img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3)
return img
def Canny_edge_detector(img):
# input: [0, 1]
# return: [0, 1] (H, W, 3)
img = np.clip(img*255, 0, 255).astype(np.uint8) # (H, W, 3)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.Canny(img, 50, 200) # (H, W)
img = cv2.convertScaleAbs(img)
img = img.astype(np.float32) / 255.
img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3)
return img
def Canny_edge_detector_uint8(img):
# input: [0, 255]
# return: [0, 255] (H, W, 3)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.Canny(img, 50, 200) # (H, W)
img = cv2.convertScaleAbs(img)
img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3)
return img
def Sobel_edge_detector(img):
# input: [0, 1]
# return: [0, 1] (H, W, 3)
img = np.clip(img*255, 0, 255).astype(np.uint8) # (H, W, 3)
img = cv2.GaussianBlur(img, (3, 3), 0)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
img = cv2.Sobel(img, cv2.CV_16S, 1, 1) # (H, W)
img = cv2.convertScaleAbs(img)
img = img.astype(np.float32) / 255.
img = np.expand_dims(img, 2).repeat(3, axis=2) # (H, W, 3)
return img
def erosion(img, kernel_size=5):
kernel = np.ones((kernel_size, kernel_size), np.uint8)
img = cv2.erode(img, kernel, iterations=1)
return img
def dilatation(img, kernel_size=5):
kernel = np.ones((kernel_size, kernel_size), np.uint8)
img = cv2.dilate(img, kernel, iterations=1)
return img
def opening(img):
return dilatation(erosion(img))
def closing(img):
return erosion(dilatation(img))
def morphological_gradient(img):
return dilatation(img) - erosion(img)
def top_hat(img):
return img - opening(img)
def black_hat(img):
return closing(img) - img
def adjust_contrast(image, clip_limit=2.0, tile_grid_size=(8, 8)):
image = single2uint(image)
lab = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)
l, a, b = cv2.split(lab)
clahe = cv2.createCLAHE(clipLimit=clip_limit, tileGridSize=tile_grid_size)
l_eq = clahe.apply(l)
lab_eq = cv2.merge((l_eq, a, b))
result = cv2.cvtColor(lab_eq, cv2.COLOR_LAB2BGR)
result = uint2single(result)
return result
def embossing(img):
kernel = np.array([[0, -1, -1],
[1, 0, -1],
[1, 1, 0]])
return cv2.filter2D(img, -1, kernel)
def hough_transform_line_detection(img):
img = single2uint(img)
dst = cv2.Canny(img, 50, 200, apertureSize=3)
cdst = cv2.cvtColor(dst, cv2.COLOR_GRAY2BGR)
lines = cv2.HoughLinesP(dst, 1, np.pi / 180, 230, None, 0, 0)
if lines is not None:
for i in range(0, len(lines)):
rho = lines[i][0][0]
theta = lines[i][0][1]
a = np.cos(theta)
b = np.sin(theta)
x0 = a * rho
y0 = b * rho
pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))
pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))
cv2.line(img, pt1, pt2, (0, 0, 255), 3, cv2.LINE_AA)
return uint2single(img)
def hough_circle_detection(img):
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 100, param1=100, param2=30, minRadius=50, maxRadius=200)
circles = np.uint16(np.around(circles))
for i in circles[0, :]:
cv2.circle(img, (i[0], i[1]), i[2], (0, 0, 255), 2)
return img
def disk(radius, alias_blur=0.1, dtype=np.float32):
if radius <= 8:
L = np.arange(-8, 8 + 1)
ksize = (3, 3)
else:
L = np.arange(-radius, radius + 1)
ksize = (5, 5)
X, Y = np.meshgrid(L, L)
aliased_disk = np.array((X ** 2 + Y ** 2) <= radius ** 2, dtype=dtype)
aliased_disk /= np.sum(aliased_disk)
# supersample disk to antialias
return cv2.GaussianBlur(aliased_disk, ksize=ksize, sigmaX=alias_blur)
def defocus_blur(image, level=(1, 0.1)):
c = level
kernel = disk(radius=c[0], alias_blur=c[1])
channels = []
for d in range(3):
channels.append(cv2.filter2D(image[:, :, d], -1, kernel))
channels = np.array(channels).transpose((1, 2, 0)) # 3x64x64 -> 64x64x3
return np.clip(channels, 0, 1)
def masks_CFA_Bayer(shape):
pattern = "RGGB"
channels = dict((channel, np.zeros(shape)) for channel in "RGB")
for channel, (y, x) in zip(pattern, [(0, 0), (0, 1), (1, 0), (1, 1)]):
channels[channel][y::2, x::2] = 1
return tuple(channels[c].astype(bool) for c in "RGB")
def cfa4_to_rgb(CFA4):
RGB = np.zeros((CFA4.shape[0]*2, CFA4.shape[1]*2, 3), dtype=np.uint8)
RGB[0::2, 0::2, 0] = CFA4[:, :, 0] # R
RGB[0::2, 1::2, 1] = CFA4[:, :, 1] # G on R row
RGB[1::2, 0::2, 1] = CFA4[:, :, 2] # G on B row
RGB[1::2, 1::2, 2] = CFA4[:, :, 3] # B
return RGB
def mosaic_CFA_Bayer(RGB):
RGB = single2uint(RGB)
R_m, G_m, B_m = masks_CFA_Bayer(RGB.shape[0:2])
mask = np.concatenate(
(R_m[..., np.newaxis], G_m[..., np.newaxis], B_m[..., np.newaxis]), axis=-1
)
mosaic = np.multiply(mask, RGB) # mask*RGB
CFA = mosaic.sum(2).astype(np.uint8)
CFA4 = np.zeros((RGB.shape[0] // 2, RGB.shape[1] // 2, 4), dtype=np.uint8)
CFA4[:, :, 0] = CFA[0::2, 0::2]
CFA4[:, :, 1] = CFA[0::2, 1::2]
CFA4[:, :, 2] = CFA[1::2, 0::2]
CFA4[:, :, 3] = CFA[1::2, 1::2]
rgb = cfa4_to_rgb(CFA4)
rgb = uint2single(rgb)
return rgb
def simulate_barrel_distortion(image, k1=0.02, k2=0.01):
height, width = image.shape[:2]
mapx, mapy = np.meshgrid(np.arange(width), np.arange(height))
mapx = 2 * mapx / (width - 1) - 1
mapy = 2 * mapy / (height - 1) - 1
r = np.sqrt(mapx**2 + mapy**2)
mapx = mapx * (1 + k1 * r**2 + k2 * r**4)
mapy = mapy * (1 + k1 * r**2 + k2 * r**4)
mapx = (mapx + 1) * (width - 1) / 2
mapy = (mapy + 1) * (height - 1) / 2
distorted_image = cv2.remap(image, mapx.astype(np.float32), mapy.astype(np.float32), cv2.INTER_LINEAR)
return distorted_image
def simulate_pincushion_distortion(image, k1=-0.02, k2=-0.01):
height, width = image.shape[:2]
mapx, mapy = np.meshgrid(np.arange(width), np.arange(height))
mapx = 2 * mapx / (width - 1) - 1
mapy = 2 * mapy / (height - 1) - 1
r = np.sqrt(mapx**2 + mapy**2)
mapx = mapx * (1 + k1 * r**2 + k2 * r**4)
mapy = mapy * (1 + k1 * r**2 + k2 * r**4)
mapx = (mapx + 1) * (width - 1) / 2
mapy = (mapy + 1) * (height - 1) / 2
distorted_image = cv2.remap(image, mapx.astype(np.float32), mapy.astype(np.float32), cv2.INTER_LINEAR)
return distorted_image
def rgb2gray(rgb):
return np.dot(rgb[..., :3], [0.2989, 0.5870, 0.1140])
def spatter(x, severity=1):
c = [(0.65, 0.3, 4, 0.69, 0.6, 0),
(0.65, 0.3, 3, 0.68, 0.6, 0),
(0.65, 0.3, 2, 0.68, 0.5, 0),
(0.65, 0.3, 1, 0.65, 1.5, 1),
(0.67, 0.4, 1, 0.65, 1.5, 1)][severity - 1]
x_PIL = x
x = np.array(x, dtype=np.float32) / 255.
liquid_layer = np.random.normal(size=x.shape[:2], loc=c[0], scale=c[1])
liquid_layer = gaussian(liquid_layer, sigma=c[2])
liquid_layer[liquid_layer < c[3]] = 0
if c[5] == 0:
liquid_layer = (liquid_layer * 255).astype(np.uint8)
dist = 255 - cv2.Canny(liquid_layer, 50, 150)
dist = cv2.distanceTransform(dist, cv2.DIST_L2, 5)
_, dist = cv2.threshold(dist, 20, 20, cv2.THRESH_TRUNC)
dist = cv2.blur(dist, (3, 3)).astype(np.uint8)
dist = cv2.equalizeHist(dist)
ker = np.array([[-2, -1, 0], [-1, 1, 1], [0, 1, 2]])
dist = cv2.filter2D(dist, cv2.CV_8U, ker)
dist = cv2.blur(dist, (3, 3)).astype(np.float32)
m = cv2.cvtColor(liquid_layer * dist, cv2.COLOR_GRAY2BGRA)
m /= np.max(m, axis=(0, 1))
m *= c[4]
# water is pale turqouise
color = np.concatenate((175 / 255. * np.ones_like(m[..., :1]),
238 / 255. * np.ones_like(m[..., :1]),
238 / 255. * np.ones_like(m[..., :1])), axis=2)
color = cv2.cvtColor(color, cv2.COLOR_BGR2BGRA)
if len(x.shape) < 3 or x.shape[2] < 3:
add_spatter_color = cv2.cvtColor(np.clip(m * color, 0, 1),
cv2.COLOR_BGRA2BGR)
add_spatter_gray = rgb2gray(add_spatter_color)
return (np.clip(x + add_spatter_gray, 0, 1) * 255).astype(np.uint8)
else:
x = cv2.cvtColor(x, cv2.COLOR_BGR2BGRA)
return (cv2.cvtColor(np.clip(x + m * color, 0, 1),
cv2.COLOR_BGRA2BGR) * 255).astype(np.uint8)
else:
m = np.where(liquid_layer > c[3], 1, 0)
m = gaussian(m.astype(np.float32), sigma=c[4])
m[m < 0.8] = 0
x_rgb = np.array(x_PIL)
# mud brown
color = np.concatenate((63 / 255. * np.ones_like(x_rgb[..., :1]),
42 / 255. * np.ones_like(x_rgb[..., :1]),
20 / 255. * np.ones_like(x_rgb[..., :1])),
axis=2)
color *= m[..., np.newaxis]
if len(x.shape) < 3 or x.shape[2] < 3:
x *= (1 - m)
return (np.clip(x + rgb2gray(color), 0, 1) * 255).astype(np.uint8)
else:
x *= (1 - m[..., np.newaxis])
return (np.clip(x + color, 0, 1) * 255).astype(np.uint8)
# mod of https://gist.github.com/erniejunior/601cdf56d2b424757de5
def elastic_transform(image, severity=3):
image = np.array(image, dtype=np.float32) / 255.
shape = image.shape
shape_size = shape[:2]
sigma = np.array(shape_size) * 0.01
alpha = [250 * 0.05, 250 * 0.065, 250 * 0.085, 250 * 0.1, 250 * 0.12][
severity - 1]
max_dx = shape[0] * 0.005
max_dy = shape[0] * 0.005
dx = (gaussian(np.random.uniform(-max_dx, max_dx, size=shape[:2]),
sigma, mode='reflect', truncate=3) * alpha).astype(
np.float32)
dy = (gaussian(np.random.uniform(-max_dy, max_dy, size=shape[:2]),
sigma, mode='reflect', truncate=3) * alpha).astype(
np.float32)
if len(image.shape) < 3 or image.shape[2] < 3:
x, y = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]))
indices = np.reshape(y + dy, (-1, 1)), np.reshape(x + dx, (-1, 1))
else:
dx, dy = dx[..., np.newaxis], dy[..., np.newaxis]
x, y, z = np.meshgrid(np.arange(shape[1]), np.arange(shape[0]),
np.arange(shape[2]))
indices = np.reshape(y + dy, (-1, 1)), np.reshape(x + dx,
(-1, 1)), np.reshape(
z, (-1, 1))
return np.clip(
map_coordinates(image, indices, order=1, mode='reflect').reshape(
shape), 0, 1) * 255
def frost(x, severity=2):
c = [(1, 0.4),
(0.8, 0.6),
(0.7, 0.7),
(0.65, 0.7),
(0.6, 0.75)][severity - 1]
idx = np.random.randint(5)
filename = [os.path.join("degradation_toolkit/frost", 'frost1.png'),
os.path.join("degradation_toolkit/frost", 'frost2.png'),
os.path.join("degradation_toolkit/frost", 'frost3.png'),
os.path.join("degradation_toolkit/frost", 'frost4.jpg'),
os.path.join("degradation_toolkit/frost", 'frost5.jpg'),
os.path.join("degradation_toolkit/frost", 'frost6.jpg')][idx]
frost = Image.open(filename)
frost = frost.convert("RGB")
frost = np.array(frost)
# frost = cv2.imread(filename)
frost = uint2single(frost)
frost_shape = frost.shape
x_shape = np.array(x).shape
# resize the frost image so it fits to the image dimensions
scaling_factor = 1
if frost_shape[0] >= x_shape[0] and frost_shape[1] >= x_shape[1]:
scaling_factor = 1
elif frost_shape[0] < x_shape[0] and frost_shape[1] >= x_shape[1]:
scaling_factor = x_shape[0] / frost_shape[0]
elif frost_shape[0] >= x_shape[0] and frost_shape[1] < x_shape[1]:
scaling_factor = x_shape[1] / frost_shape[1]
elif frost_shape[0] < x_shape[0] and frost_shape[1] < x_shape[
1]: # If both dims are too small, pick the bigger scaling factor
scaling_factor_0 = x_shape[0] / frost_shape[0]
scaling_factor_1 = x_shape[1] / frost_shape[1]
scaling_factor = np.maximum(scaling_factor_0, scaling_factor_1)
scaling_factor *= 1.1
new_shape = (int(np.ceil(frost_shape[1] * scaling_factor)),
int(np.ceil(frost_shape[0] * scaling_factor)))
frost_rescaled = cv2.resize(frost, dsize=new_shape,
interpolation=cv2.INTER_CUBIC)
# randomly crop
x_start, y_start = np.random.randint(0, frost_rescaled.shape[0] - x_shape[
0]), np.random.randint(0, frost_rescaled.shape[1] - x_shape[1])
if len(x_shape) < 3 or x_shape[2] < 3:
frost_rescaled = frost_rescaled[x_start:x_start + x_shape[0],
y_start:y_start + x_shape[1]]
frost_rescaled = rgb2gray(frost_rescaled)
else:
frost_rescaled = frost_rescaled[x_start:x_start + x_shape[0],
y_start:y_start + x_shape[1]][..., [2, 1, 0]]
return c[0] * np.array(x) + c[1] * frost_rescaled