This repository has been archived by the owner on May 30, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathmirage_tank.py
161 lines (130 loc) · 4.33 KB
/
mirage_tank.py
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Create 'mirage tank' images in Python
Part of the code adapted from https://www.cnblogs.com/sryml/p/10970270.html
"""
import time
from typing import Callable
import cv2
import numpy as np
from numba import njit
def profile(func: Callable) -> Callable:
"""
A decorator function for elapsed-time profiling
usage: @profile
:param func: function to profile
"""
def with_profiling(*args, **kwargs):
start_time = time.time()
ret = func(*args, **kwargs)
elapsed_time = time.time() - start_time
print('{} finished, takes {:.4f}s'.format(func.__name__, elapsed_time))
return ret
return with_profiling
@profile
def desaturate(image: np.ndarray) -> np.ndarray:
"""
Photoshop-like desaturation, formula:
lambda r, g, b: (max(r, g, b) + min(r, g, b)) / 2
source: https://stackoverflow.com/a/28873770
:param image: input RGB image read by cv2
:return: desaturated grayscale image
"""
cp: np.ndarray = image.copy().astype(np.float_)
desaturated: np.ndarray = (np.amax(cp, 2) + np.amin(cp, 2)) / 2
return desaturated.astype(np.uint8)
@profile
def adjust_lightness(image: np.ndarray, ratio: float) -> np.ndarray:
"""
Adjust the lightness of the image
:param image: input grayscale image
:param ratio: float number from -100% to +100%
:return: adjusted image
"""
cp: np.ndarray = image.copy().astype(np.float_)
if ratio > 0:
return (cp * (1 - ratio) + 255 * ratio).astype(np.uint8)
return np.ceil(cp * (1 + ratio)).astype(np.uint8)
@profile
def invert(image: np.ndarray) -> np.ndarray:
"""
Invert the color of the image
:param image: input grayscale image
:return: inverted image
"""
return 255 - image
@profile
def linear_dodge_blend(img_x: np.ndarray, img_y: np.ndarray) -> np.ndarray:
"""
Blend image x and y in 'linear dodge' mode
:param img_x: input grayscale image on top
:param img_y: input grayscale image at bottom
:return:
"""
return img_x + img_y
@profile
@njit
def divide_blend(img_x: np.ndarray, img_y: np.ndarray) -> np.ndarray:
"""
Blend image x and y in 'divide' mode
:param img_x: input grayscale image on top
:param img_y: input grayscale image at bottom
:return:
"""
result = np.zeros_like(img_x, np.float_)
height, width = img_x.shape
for i in range(height):
for j in range(width):
if img_x[i, j] == 0:
color = img_y[i, j] and 255 or 0
elif img_x[i, j] == 255:
color = img_y[i, j]
elif img_x[i, j] == img_y[i, j]:
color = 255
else:
color = (img_y[i, j] / img_x[i, j]) * 255
result[i, j] = color
return result.astype(np.uint8)
@profile
def add_mask(img_x: np.ndarray, img_y: np.ndarray) -> np.ndarray:
"""
Add image y to x as the alpha channel
:param img_x: input grayscale image
:param img_y: input grayscale image
:return: the result image
"""
result: np.ndarray = cv2.cvtColor(img_x, cv2.COLOR_GRAY2BGRA)
result[:, :, 3] = img_y
return result
def build(source_x: str, source_y: str, target_name: str, shrink: float = 1):
"""
Build a 'mirage tank' image from [source_x] and [source_y]
(Assuming [source_x] and [source_y] have the same size)
:param source_x: name of the image shown on white background
:param source_y: name of the image shown on black background
:param target_name: name of the image to be saved
:param shrink: size change comparing to the source image
"""
print("start process")
img_a = cv2.cvtColor(
cv2.imread(source_x, cv2.IMREAD_UNCHANGED),
cv2.COLOR_BGR2RGB
)
img_b = cv2.cvtColor(
cv2.imread(source_y, cv2.IMREAD_UNCHANGED),
cv2.COLOR_BGR2RGB
)
height, width, _ = img_a.shape
height = int(height * shrink)
width = int(width * shrink)
img_a = invert(
adjust_lightness(desaturate(cv2.resize(img_a, (width, height))), 0.5)
)
img_b = adjust_lightness(
desaturate(cv2.resize(img_b, (width, height))), -0.5
)
linear_dodged = linear_dodge_blend(img_a, img_b)
divided = divide_blend(linear_dodged, img_b)
cv2.imwrite(target_name, add_mask(divided, linear_dodged))
print("finished")