imgboost-ai 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- imgboost/__init__.py +10 -0
- imgboost/cli.py +169 -0
- imgboost/core.py +162 -0
- imgboost/processors/__init__.py +9 -0
- imgboost/processors/document.py +269 -0
- imgboost/processors/filters.py +257 -0
- imgboost/processors/superres.py +252 -0
- imgboost/utils/__init__.py +7 -0
- imgboost/utils/image_io.py +231 -0
- imgboost_ai-0.1.0.dist-info/METADATA +387 -0
- imgboost_ai-0.1.0.dist-info/RECORD +15 -0
- imgboost_ai-0.1.0.dist-info/WHEEL +5 -0
- imgboost_ai-0.1.0.dist-info/entry_points.txt +2 -0
- imgboost_ai-0.1.0.dist-info/licenses/LICENSE +21 -0
- imgboost_ai-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
import numpy as np
|
|
3
|
+
from skimage import exposure
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ImageFilters:
|
|
7
|
+
"""
|
|
8
|
+
Filtros e melhorias de imagem usando OpenCV.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, denoise_strength=10, contrast_clip=3.0):
|
|
12
|
+
"""
|
|
13
|
+
Inicializa os filtros.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
denoise_strength: Força do denoise (0-30)
|
|
17
|
+
contrast_clip: Intensidade do CLAHE (1.0-5.0)
|
|
18
|
+
"""
|
|
19
|
+
self.denoise_strength = denoise_strength
|
|
20
|
+
self.contrast_clip = contrast_clip
|
|
21
|
+
|
|
22
|
+
def denoise(self, img, strength=None):
|
|
23
|
+
"""
|
|
24
|
+
Remove ruído da imagem.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
img: Imagem BGR
|
|
28
|
+
strength: Força do denoise (sobrescreve padrão)
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
Imagem com ruído reduzido
|
|
32
|
+
"""
|
|
33
|
+
if strength is None:
|
|
34
|
+
strength = self.denoise_strength
|
|
35
|
+
|
|
36
|
+
# Converter para o tipo correto se necessário
|
|
37
|
+
if img.dtype != np.uint8:
|
|
38
|
+
img = img.astype(np.uint8)
|
|
39
|
+
|
|
40
|
+
if len(img.shape) == 3:
|
|
41
|
+
# Imagem colorida
|
|
42
|
+
return cv2.fastNlMeansDenoisingColored(
|
|
43
|
+
img, None,
|
|
44
|
+
h=strength,
|
|
45
|
+
hColor=strength,
|
|
46
|
+
templateWindowSize=7,
|
|
47
|
+
searchWindowSize=21
|
|
48
|
+
)
|
|
49
|
+
else:
|
|
50
|
+
# Imagem em escala de cinza
|
|
51
|
+
return cv2.fastNlMeansDenoising(
|
|
52
|
+
img, None,
|
|
53
|
+
h=strength,
|
|
54
|
+
templateWindowSize=7,
|
|
55
|
+
searchWindowSize=21
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def enhance_contrast(self, img):
|
|
59
|
+
"""
|
|
60
|
+
Melhora o contraste usando CLAHE (Contrast Limited Adaptive Histogram Equalization).
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
img: Imagem BGR
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Imagem com contraste melhorado
|
|
67
|
+
"""
|
|
68
|
+
# Converter para LAB
|
|
69
|
+
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
|
70
|
+
l, a, b = cv2.split(lab)
|
|
71
|
+
|
|
72
|
+
# Aplicar CLAHE no canal L
|
|
73
|
+
clahe = cv2.createCLAHE(
|
|
74
|
+
clipLimit=self.contrast_clip,
|
|
75
|
+
tileGridSize=(8, 8)
|
|
76
|
+
)
|
|
77
|
+
cl = clahe.apply(l)
|
|
78
|
+
|
|
79
|
+
# Recombinar canais
|
|
80
|
+
limg = cv2.merge((cl, a, b))
|
|
81
|
+
|
|
82
|
+
# Converter de volta para BGR
|
|
83
|
+
return cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
|
|
84
|
+
|
|
85
|
+
def sharpen(self, img, strength=1.0):
|
|
86
|
+
"""
|
|
87
|
+
Aplica sharpening na imagem.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
img: Imagem BGR
|
|
91
|
+
strength: Intensidade do sharpen (0.5-2.0)
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
Imagem com sharpen aplicado
|
|
95
|
+
"""
|
|
96
|
+
# Kernel de sharpen
|
|
97
|
+
kernel = np.array([
|
|
98
|
+
[-1, -1, -1],
|
|
99
|
+
[-1, 9, -1],
|
|
100
|
+
[-1, -1, -1]
|
|
101
|
+
]) * strength / 9
|
|
102
|
+
|
|
103
|
+
kernel[1, 1] = 1 + (8 * strength / 9)
|
|
104
|
+
|
|
105
|
+
return cv2.filter2D(img, -1, kernel)
|
|
106
|
+
|
|
107
|
+
def unsharp_mask(self, img, sigma=1.0, strength=1.5):
|
|
108
|
+
"""
|
|
109
|
+
Aplica unsharp mask para sharpening mais refinado.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
img: Imagem BGR
|
|
113
|
+
sigma: Tamanho do blur
|
|
114
|
+
strength: Intensidade do efeito
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Imagem com unsharp mask aplicado
|
|
118
|
+
"""
|
|
119
|
+
# Criar versão borrada
|
|
120
|
+
blurred = cv2.GaussianBlur(img, (0, 0), sigma)
|
|
121
|
+
|
|
122
|
+
# Sharpened = Original + (Original - Blurred) * strength
|
|
123
|
+
sharpened = cv2.addWeighted(img, 1.0 + strength, blurred, -strength, 0)
|
|
124
|
+
|
|
125
|
+
return sharpened
|
|
126
|
+
|
|
127
|
+
def enhance_dark_ui(self, img):
|
|
128
|
+
"""
|
|
129
|
+
Otimiza imagens de interfaces escuras (WhatsApp Dark, etc).
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
img: Imagem BGR
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
Imagem otimizada
|
|
136
|
+
"""
|
|
137
|
+
# Converter para LAB
|
|
138
|
+
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
|
139
|
+
l, a, b = cv2.split(lab)
|
|
140
|
+
|
|
141
|
+
# Ajuste de gamma para realçar áreas escuras
|
|
142
|
+
gamma = 0.8
|
|
143
|
+
l = self._adjust_gamma(l, gamma)
|
|
144
|
+
|
|
145
|
+
# CLAHE mais agressivo
|
|
146
|
+
clahe = cv2.createCLAHE(clipLimit=4.0, tileGridSize=(8, 8))
|
|
147
|
+
l = clahe.apply(l)
|
|
148
|
+
|
|
149
|
+
# Recombinar
|
|
150
|
+
limg = cv2.merge((l, a, b))
|
|
151
|
+
img = cv2.cvtColor(limg, cv2.COLOR_LAB2BGR)
|
|
152
|
+
|
|
153
|
+
# Redução seletiva de ruído (menor que normal para não perder detalhes)
|
|
154
|
+
img = cv2.fastNlMeansDenoisingColored(img, None, 5, 5, 7, 21)
|
|
155
|
+
|
|
156
|
+
return img
|
|
157
|
+
|
|
158
|
+
def adjust_brightness_contrast(self, img, brightness=0, contrast=0):
|
|
159
|
+
"""
|
|
160
|
+
Ajusta brilho e contraste.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
img: Imagem BGR
|
|
164
|
+
brightness: Ajuste de brilho (-100 a 100)
|
|
165
|
+
contrast: Ajuste de contraste (-100 a 100)
|
|
166
|
+
|
|
167
|
+
Returns:
|
|
168
|
+
Imagem ajustada
|
|
169
|
+
"""
|
|
170
|
+
if brightness != 0:
|
|
171
|
+
if brightness > 0:
|
|
172
|
+
shadow = brightness
|
|
173
|
+
highlight = 255
|
|
174
|
+
else:
|
|
175
|
+
shadow = 0
|
|
176
|
+
highlight = 255 + brightness
|
|
177
|
+
alpha_b = (highlight - shadow) / 255
|
|
178
|
+
gamma_b = shadow
|
|
179
|
+
|
|
180
|
+
img = cv2.addWeighted(img, alpha_b, img, 0, gamma_b)
|
|
181
|
+
|
|
182
|
+
if contrast != 0:
|
|
183
|
+
f = 131 * (contrast + 127) / (127 * (131 - contrast))
|
|
184
|
+
alpha_c = f
|
|
185
|
+
gamma_c = 127 * (1 - f)
|
|
186
|
+
|
|
187
|
+
img = cv2.addWeighted(img, alpha_c, img, 0, gamma_c)
|
|
188
|
+
|
|
189
|
+
return img
|
|
190
|
+
|
|
191
|
+
def _adjust_gamma(self, img, gamma=1.0):
|
|
192
|
+
"""
|
|
193
|
+
Ajusta gamma da imagem.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
img: Imagem em escala de cinza
|
|
197
|
+
gamma: Valor de gamma
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Imagem com gamma ajustado
|
|
201
|
+
"""
|
|
202
|
+
inv_gamma = 1.0 / gamma
|
|
203
|
+
table = np.array([
|
|
204
|
+
((i / 255.0) ** inv_gamma) * 255
|
|
205
|
+
for i in range(256)
|
|
206
|
+
]).astype("uint8")
|
|
207
|
+
|
|
208
|
+
return cv2.LUT(img, table)
|
|
209
|
+
|
|
210
|
+
def enhance_edges(self, img):
|
|
211
|
+
"""
|
|
212
|
+
Realça bordas na imagem.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
img: Imagem BGR
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
Imagem com bordas realçadas
|
|
219
|
+
"""
|
|
220
|
+
# Converter para escala de cinza
|
|
221
|
+
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
|
|
222
|
+
|
|
223
|
+
# Detectar bordas
|
|
224
|
+
edges = cv2.Canny(gray, 100, 200)
|
|
225
|
+
|
|
226
|
+
# Dilatar bordas
|
|
227
|
+
kernel = np.ones((2, 2), np.uint8)
|
|
228
|
+
edges = cv2.dilate(edges, kernel, iterations=1)
|
|
229
|
+
|
|
230
|
+
# Converter bordas para BGR
|
|
231
|
+
edges_bgr = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
|
|
232
|
+
|
|
233
|
+
# Adicionar bordas à imagem original
|
|
234
|
+
enhanced = cv2.addWeighted(img, 0.9, edges_bgr, 0.1, 0)
|
|
235
|
+
|
|
236
|
+
return enhanced
|
|
237
|
+
|
|
238
|
+
def auto_white_balance(self, img):
|
|
239
|
+
"""
|
|
240
|
+
Balanço automático de branco.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
img: Imagem BGR
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Imagem com balanço de branco corrigido
|
|
247
|
+
"""
|
|
248
|
+
result = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
|
249
|
+
avg_a = np.average(result[:, :, 1])
|
|
250
|
+
avg_b = np.average(result[:, :, 2])
|
|
251
|
+
|
|
252
|
+
result[:, :, 1] = result[:, :, 1] - ((avg_a - 128) * (result[:, :, 0] / 255.0) * 1.1)
|
|
253
|
+
result[:, :, 2] = result[:, :, 2] - ((avg_b - 128) * (result[:, :, 0] / 255.0) * 1.1)
|
|
254
|
+
|
|
255
|
+
result = cv2.cvtColor(result, cv2.COLOR_LAB2BGR)
|
|
256
|
+
|
|
257
|
+
return result
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import cv2
|
|
2
|
+
import numpy as np
|
|
3
|
+
import warnings
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class SuperResolution:
|
|
7
|
+
"""
|
|
8
|
+
Processador de super-resolução para upscaling de imagens.
|
|
9
|
+
|
|
10
|
+
Nota: Para usar modelos de IA como Real-ESRGAN, você precisa baixar
|
|
11
|
+
os pesos do modelo separadamente. Esta implementação usa upscaling
|
|
12
|
+
de alta qualidade do OpenCV como fallback.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(self, use_ai=False, model_path=None):
|
|
16
|
+
"""
|
|
17
|
+
Inicializa o processador de super-resolução.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
use_ai: Tentar usar modelos de IA (Real-ESRGAN)
|
|
21
|
+
model_path: Caminho para os pesos do modelo
|
|
22
|
+
"""
|
|
23
|
+
self.use_ai = use_ai
|
|
24
|
+
self.model_path = model_path
|
|
25
|
+
self.ai_model = None
|
|
26
|
+
|
|
27
|
+
if use_ai:
|
|
28
|
+
self._init_ai_model()
|
|
29
|
+
|
|
30
|
+
def _init_ai_model(self):
|
|
31
|
+
"""Inicializa modelo de IA se disponível."""
|
|
32
|
+
try:
|
|
33
|
+
# Tentar importar Real-ESRGAN
|
|
34
|
+
from basicsr.archs.rrdbnet_arch import RRDBNet
|
|
35
|
+
import torch
|
|
36
|
+
|
|
37
|
+
# Configurar modelo
|
|
38
|
+
self.ai_model = RRDBNet(
|
|
39
|
+
num_in_ch=3,
|
|
40
|
+
num_out_ch=3,
|
|
41
|
+
num_feat=64,
|
|
42
|
+
num_block=23,
|
|
43
|
+
num_grow_ch=32,
|
|
44
|
+
scale=4
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Carregar pesos se disponível
|
|
48
|
+
if self.model_path and torch.cuda.is_available():
|
|
49
|
+
self.ai_model.load_state_dict(torch.load(self.model_path))
|
|
50
|
+
self.ai_model.eval()
|
|
51
|
+
self.ai_model = self.ai_model.cuda()
|
|
52
|
+
else:
|
|
53
|
+
warnings.warn(
|
|
54
|
+
"Modelo de IA não configurado. Usando upscaling tradicional."
|
|
55
|
+
)
|
|
56
|
+
self.ai_model = None
|
|
57
|
+
|
|
58
|
+
except ImportError:
|
|
59
|
+
warnings.warn(
|
|
60
|
+
"Dependências de IA não encontradas. Instale: pip install basicsr realesrgan"
|
|
61
|
+
)
|
|
62
|
+
self.ai_model = None
|
|
63
|
+
|
|
64
|
+
def upscale(self, img, scale=2):
|
|
65
|
+
"""
|
|
66
|
+
Aumenta resolução da imagem.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
img: Imagem BGR
|
|
70
|
+
scale: Fator de escala (2, 4, 8)
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Imagem com resolução aumentada
|
|
74
|
+
"""
|
|
75
|
+
if self.ai_model is not None:
|
|
76
|
+
return self._upscale_with_ai(img, scale)
|
|
77
|
+
else:
|
|
78
|
+
return self._upscale_traditional(img, scale)
|
|
79
|
+
|
|
80
|
+
def _upscale_with_ai(self, img, scale):
|
|
81
|
+
"""
|
|
82
|
+
Upscale usando modelo de IA (Real-ESRGAN).
|
|
83
|
+
|
|
84
|
+
Args:
|
|
85
|
+
img: Imagem BGR
|
|
86
|
+
scale: Fator de escala
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
Imagem upscaled
|
|
90
|
+
"""
|
|
91
|
+
try:
|
|
92
|
+
import torch
|
|
93
|
+
|
|
94
|
+
# Converter para tensor
|
|
95
|
+
img_tensor = torch.from_numpy(
|
|
96
|
+
np.transpose(img, (2, 0, 1))
|
|
97
|
+
).float().div(255).unsqueeze(0)
|
|
98
|
+
|
|
99
|
+
if torch.cuda.is_available():
|
|
100
|
+
img_tensor = img_tensor.cuda()
|
|
101
|
+
|
|
102
|
+
# Inferência
|
|
103
|
+
with torch.no_grad():
|
|
104
|
+
output = self.ai_model(img_tensor)
|
|
105
|
+
|
|
106
|
+
# Converter de volta para numpy
|
|
107
|
+
output = output.squeeze().cpu().numpy()
|
|
108
|
+
output = np.transpose(output, (1, 2, 0))
|
|
109
|
+
output = (output * 255).clip(0, 255).astype(np.uint8)
|
|
110
|
+
|
|
111
|
+
return output
|
|
112
|
+
|
|
113
|
+
except Exception as e:
|
|
114
|
+
warnings.warn(f"Erro no upscale com IA: {e}. Usando método tradicional.")
|
|
115
|
+
return self._upscale_traditional(img, scale)
|
|
116
|
+
|
|
117
|
+
def _upscale_traditional(self, img, scale):
|
|
118
|
+
"""
|
|
119
|
+
Upscale usando interpolação de alta qualidade.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
img: Imagem BGR
|
|
123
|
+
scale: Fator de escala
|
|
124
|
+
|
|
125
|
+
Returns:
|
|
126
|
+
Imagem upscaled
|
|
127
|
+
"""
|
|
128
|
+
height, width = img.shape[:2]
|
|
129
|
+
new_width = int(width * scale)
|
|
130
|
+
new_height = int(height * scale)
|
|
131
|
+
|
|
132
|
+
# LANCZOS4 é a melhor interpolação para upscaling
|
|
133
|
+
upscaled = cv2.resize(
|
|
134
|
+
img,
|
|
135
|
+
(new_width, new_height),
|
|
136
|
+
interpolation=cv2.INTER_LANCZOS4
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
# Aplicar unsharp mask para recuperar detalhes
|
|
140
|
+
upscaled = self._unsharp_mask(upscaled, sigma=1.0, strength=0.5)
|
|
141
|
+
|
|
142
|
+
return upscaled
|
|
143
|
+
|
|
144
|
+
def _unsharp_mask(self, img, sigma=1.0, strength=0.5):
|
|
145
|
+
"""
|
|
146
|
+
Aplica unsharp mask para melhorar nitidez.
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
img: Imagem BGR
|
|
150
|
+
sigma: Tamanho do blur
|
|
151
|
+
strength: Intensidade
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
Imagem com sharpen aplicado
|
|
155
|
+
"""
|
|
156
|
+
blurred = cv2.GaussianBlur(img, (0, 0), sigma)
|
|
157
|
+
sharpened = cv2.addWeighted(img, 1.0 + strength, blurred, -strength, 0)
|
|
158
|
+
return sharpened
|
|
159
|
+
|
|
160
|
+
def upscale_smart(self, img, target_width=None, target_height=None):
|
|
161
|
+
"""
|
|
162
|
+
Upscale inteligente para dimensão alvo.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
img: Imagem BGR
|
|
166
|
+
target_width: Largura alvo (opcional)
|
|
167
|
+
target_height: Altura alvo (opcional)
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
Imagem redimensionada
|
|
171
|
+
"""
|
|
172
|
+
height, width = img.shape[:2]
|
|
173
|
+
|
|
174
|
+
if target_width and target_height:
|
|
175
|
+
new_width = target_width
|
|
176
|
+
new_height = target_height
|
|
177
|
+
elif target_width:
|
|
178
|
+
ratio = target_width / width
|
|
179
|
+
new_width = target_width
|
|
180
|
+
new_height = int(height * ratio)
|
|
181
|
+
elif target_height:
|
|
182
|
+
ratio = target_height / height
|
|
183
|
+
new_height = target_height
|
|
184
|
+
new_width = int(width * ratio)
|
|
185
|
+
else:
|
|
186
|
+
return img
|
|
187
|
+
|
|
188
|
+
# Calcular escala
|
|
189
|
+
scale = max(new_width / width, new_height / height)
|
|
190
|
+
|
|
191
|
+
if scale > 1:
|
|
192
|
+
# Upscaling
|
|
193
|
+
return self.upscale(img, scale=int(np.ceil(scale)))
|
|
194
|
+
else:
|
|
195
|
+
# Downscaling - usar INTER_AREA
|
|
196
|
+
return cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_AREA)
|
|
197
|
+
|
|
198
|
+
def multi_scale_enhance(self, img):
|
|
199
|
+
"""
|
|
200
|
+
Melhoria multi-escala para preservar detalhes.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
img: Imagem BGR
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
Imagem melhorada
|
|
207
|
+
"""
|
|
208
|
+
# Criar pirâmide gaussiana
|
|
209
|
+
pyramid = [img]
|
|
210
|
+
for i in range(3):
|
|
211
|
+
pyramid.append(cv2.pyrDown(pyramid[-1]))
|
|
212
|
+
|
|
213
|
+
# Reconstruir com detalhes preservados
|
|
214
|
+
result = pyramid[-1]
|
|
215
|
+
for i in range(len(pyramid) - 2, -1, -1):
|
|
216
|
+
result = cv2.pyrUp(result)
|
|
217
|
+
# Ajustar tamanho se necessário
|
|
218
|
+
if result.shape[:2] != pyramid[i].shape[:2]:
|
|
219
|
+
result = cv2.resize(result, (pyramid[i].shape[1], pyramid[i].shape[0]))
|
|
220
|
+
# Mesclar com detalhes
|
|
221
|
+
result = cv2.addWeighted(result, 0.6, pyramid[i], 0.4, 0)
|
|
222
|
+
|
|
223
|
+
return result
|
|
224
|
+
|
|
225
|
+
def enhance_texture(self, img):
|
|
226
|
+
"""
|
|
227
|
+
Realça texturas após upscaling.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
img: Imagem BGR
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Imagem com texturas realçadas
|
|
234
|
+
"""
|
|
235
|
+
# Converter para LAB
|
|
236
|
+
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
|
|
237
|
+
l, a, b = cv2.split(lab)
|
|
238
|
+
|
|
239
|
+
# Realçar detalhes finos no canal L
|
|
240
|
+
kernel = np.array([
|
|
241
|
+
[0, -1, 0],
|
|
242
|
+
[-1, 5, -1],
|
|
243
|
+
[0, -1, 0]
|
|
244
|
+
]) / 5.0
|
|
245
|
+
|
|
246
|
+
l_enhanced = cv2.filter2D(l, -1, kernel)
|
|
247
|
+
|
|
248
|
+
# Recombinar
|
|
249
|
+
lab_enhanced = cv2.merge([l_enhanced, a, b])
|
|
250
|
+
result = cv2.cvtColor(lab_enhanced, cv2.COLOR_LAB2BGR)
|
|
251
|
+
|
|
252
|
+
return result
|