This commit is contained in:
ZeYi Lin
2024-09-12 14:32:26 +08:00
12 changed files with 556 additions and 5 deletions
+1
View File
@@ -7,6 +7,7 @@
.env
demo/kb_output/*.jpg
demo/kb_output/*.png
**/flagged/
# build outputs
dist
build
+16
View File
@@ -292,4 +292,20 @@ LOCALES = {
"label": "抠图图像",
},
},
"beauty_tab": {
"en": {
"label": "Beauty",
},
"zh": {
"label": "美颜",
},
},
"whitening_strength": {
"en": {
"label": "whitening strength",
},
"zh": {
"label": "美白强度",
},
},
}
+9 -3
View File
@@ -41,7 +41,7 @@ class IDPhotoProcessor:
face_detect_option,
head_measure_ratio=0.2,
top_distance_max=0.12,
top_distance_min=0.10,
whitening_strength=0,
):
top_distance_min = top_distance_max - 0.02
@@ -115,6 +115,7 @@ class IDPhotoProcessor:
idphoto_json["size_mode"] in LOCALES["size_mode"][language]["choices"][1]
)
# 生成证件照
try:
result = creator(
input_image,
@@ -122,7 +123,9 @@ class IDPhotoProcessor:
size=idphoto_json["size"],
head_measure_ratio=head_measure_ratio,
head_top_range=(top_distance_max, top_distance_min),
whitening_strength=whitening_strength,
)
# 如果检测到人脸数量不等于1
except FaceError:
return [
gr.update(value=None), # img_output_standard
@@ -136,7 +139,7 @@ class IDPhotoProcessor:
),
None, # file_download (assuming it should be None or have no update)
]
# 如果 API 错误
except APIError as e:
return [
gr.update(value=None), # img_output_standard
@@ -150,13 +153,14 @@ class IDPhotoProcessor:
),
None, # file_download (assuming it should be None or have no update)
]
# 证件照生成正常
else:
(result_image_standard, result_image_hd, _, _, _, _) = result
result_image_standard_png = np.uint8(result_image_standard)
result_image_hd_png = np.uint8(result_image_hd)
# 纯色渲染
if (
idphoto_json["render_mode"]
== LOCALES["render_mode"][language]["choices"][0]
@@ -167,6 +171,7 @@ class IDPhotoProcessor:
result_image_hd = np.uint8(
add_background(result_image_hd, bgr=idphoto_json["color_bgr"])
)
# 上下渐变渲染
elif (
idphoto_json["render_mode"]
== LOCALES["render_mode"][language]["choices"][1]
@@ -185,6 +190,7 @@ class IDPhotoProcessor:
mode="updown_gradient",
)
)
# 中心渐变渲染
else:
result_image_standard = np.uint8(
add_background(
+26
View File
@@ -63,6 +63,7 @@ def create_ui(
value=human_matting_models[0],
)
# TAB1 - 关键参数
with gr.Tab(
LOCALES["key_param"][DEFAULT_LANG]["label"]
) as key_parameter_tab:
@@ -105,6 +106,7 @@ def create_ui(
value=LOCALES["render_mode"][DEFAULT_LANG]["choices"][0],
)
# TAB2 - 高级参数
with gr.Tab(
LOCALES["advance_param"][DEFAULT_LANG]["label"]
) as advance_parameter_tab:
@@ -140,6 +142,20 @@ def create_ui(
interactive=True,
)
# TAB3 - 美颜
with gr.Tab(
LOCALES["beauty_tab"][DEFAULT_LANG]["label"]
) as beauty_parameter_tab:
whitening_option = gr.Slider(
label=LOCALES["whitening_strength"][DEFAULT_LANG]["label"],
minimum=0,
maximum=10,
value=2,
step=1,
interactive=True,
)
# TAB4 - 水印
with gr.Tab(
LOCALES["watermark_tab"][DEFAULT_LANG]["label"]
) as watermark_parameter_tab:
@@ -387,6 +403,12 @@ def create_ui(
matting_image_accordion: gr.update(
label=LOCALES["matting_image"][language]["label"]
),
beauty_parameter_tab: gr.update(
label=LOCALES["beauty_tab"][language]["label"]
),
whitening_option: gr.update(
label=LOCALES["whitening_strength"][language]["label"]
),
}
def change_color(colors):
@@ -425,6 +447,7 @@ def create_ui(
return {custom_image_kb: gr.update(visible=False)}
# ---------------- 绑定事件 ----------------
# 语言切换
language_options.input(
change_language,
inputs=[language_options],
@@ -458,6 +481,8 @@ def create_ui(
watermark_text_space,
watermark_options,
matting_image_accordion,
beauty_parameter_tab,
whitening_option,
],
)
@@ -502,6 +527,7 @@ def create_ui(
face_detect_model_options,
head_measure_ratio_option,
top_distance_option,
whitening_option,
],
outputs=[
img_output_standard,
+17 -2
View File
@@ -12,7 +12,8 @@ from typing import Tuple
import hivision.creator.utils as U
from .context import Context, ContextHandler, Params, Result
from .human_matting import extract_human
from .face_detector import detect_face_mtcnn, detect_face_face_plusplus
from .face_detector import detect_face_mtcnn
from hivision.plugin.beauty.whitening import make_whitening
from .photo_adjuster import adjust_photo
@@ -56,15 +57,19 @@ class IDCreator:
head_height_ratio: float = 0.45,
head_top_range: float = (0.12, 0.1),
face: Tuple[int, int, int, int] = None,
whitening_strength: int = 0,
) -> Result:
"""
证件照处理函数
:param image: 输入图像
:param change_bg_only: 是否只需要换底
:param change_bg_only: 是否只需要抠图
:param crop_only: 是否只需要裁剪
:param size: 输出的图像大小(h,w)
:param head_measure_ratio: 人脸面积与全图面积的期望比值
:param head_height_ratio: 人脸中心处在全图高度的比例期望值
:param head_top_range: 头距离顶部的比例(max,min)
:param face: 人脸坐标
:param whitening_strength: 美白强度
:return: 返回处理后的证件照和一系列参数
"""
@@ -77,6 +82,7 @@ class IDCreator:
head_top_range=head_top_range,
crop_only=crop_only,
face=face,
whitening_strength=whitening_strength,
)
self.ctx = Context(params)
ctx = self.ctx
@@ -87,8 +93,15 @@ class IDCreator:
ctx.origin_image = ctx.processing_image.copy()
self.before_all and self.before_all(ctx)
# 美白
if ctx.params.whitening_strength > 0:
ctx.processing_image = make_whitening(
ctx.processing_image, ctx.params.whitening_strength
)
# 1. 人像抠图
if not ctx.params.crop_only:
# 调用抠图工作流
self.matting_handler(ctx)
self.after_matting and self.after_matting(ctx)
else:
@@ -115,6 +128,8 @@ class IDCreator:
result_image_hd, result_image_standard, clothing_params, typography_params = (
adjust_photo(ctx)
)
# 4. 返回结果
ctx.result = Result(
standard=result_image_standard,
hd=result_image_hd,
+6
View File
@@ -21,6 +21,7 @@ class Params:
head_height_ratio: float = 0.45,
head_top_range: float = (0.12, 0.1),
face: Tuple[int, int, int, int] = None,
whitening_strength: int = 0,
):
self.__size = size
self.__change_bg_only = change_bg_only
@@ -29,6 +30,7 @@ class Params:
self.__head_height_ratio = head_height_ratio
self.__head_top_range = head_top_range
self.__face = face
self.__whitening_strength = whitening_strength
@property
def size(self):
@@ -58,6 +60,10 @@ class Params:
def face(self):
return self.__face
@property
def whitening_strength(self):
return self.__whitening_strength
class Result:
def __init__(
+1
View File
@@ -0,0 +1 @@
from .beauty_tools import BeautyTools
+49
View File
@@ -0,0 +1,49 @@
"""
@author: cuny
@file: MakeBeautiful.py
@time: 2022/7/7 20:23
@description:
美颜工具集合文件,作为暴露在外的插件接口
"""
from .grind_skin import grindSkin
from .whitening import MakeWhiter
from .thin_face import thinFace
import numpy as np
def BeautyTools(
input_image: np.ndarray,
landmark,
thinStrength: int,
thinPlace: int,
grindStrength: int,
whiterStrength: int,
) -> np.ndarray:
"""
美颜工具的接口函数,用于实现美颜效果
Args:
input_image: 输入的图像
landmark: 瘦脸需要的人脸关键点信息,为fd68返回的第二个参数
thinStrength: 瘦脸强度,为0-10(如果更高其实也没什么问题),当强度为0或者更低时,则不瘦脸
thinPlace: 选择瘦脸区域,为0-2之间的值,越大瘦脸的点越靠下
grindStrength: 磨皮强度,为0-10(如果更高其实也没什么问题),当强度为0或者更低时,则不磨皮
whiterStrength: 美白强度,为0-10(如果更高其实也没什么问题),当强度为0或者更低时,则不美白
Returns:
output_image 输出图像
"""
try:
_, _, _ = input_image.shape
except ValueError:
raise TypeError("输入图像必须为3通道或者4通道!")
# 三通道或者四通道图像
# 首先进行瘦脸
input_image = thinFace(
input_image, landmark, place=thinPlace, strength=thinStrength
)
# 其次进行磨皮
input_image = grindSkin(src=input_image, strength=grindStrength)
# 最后进行美白
makeWhiter = MakeWhiter()
input_image = makeWhiter.run(input_image, strength=whiterStrength)
return input_image
+44
View File
@@ -0,0 +1,44 @@
"""
@author: cuny
@file: GrindSkin.py
@time: 2022/7/2 14:44
@description:
磨皮算法
"""
import cv2
import numpy as np
def grindSkin(src, grindDegree: int = 3, detailDegree: int = 1, strength: int = 9):
"""
Dest =(Src * (100 - Opacity) + (Src + 2 * GaussBlur(EPFFilter(Src) - Src)) * Opacity) /100
人像磨皮方案,后续会考虑使用一些皮肤区域检测算法来实现仅皮肤区域磨皮,增加算法的精细程度——或者使用人脸关键点
https://www.cnblogs.com/Imageshop/p/4709710.html
Args:
src: 原图
grindDegree: 磨皮程度调节参数
detailDegree: 细节程度调节参数
strength: 融合程度,作为磨皮强度(0 - 10)
Returns:
磨皮后的图像
"""
if strength <= 0:
return src
dst = src.copy()
opacity = min(10.0, strength) / 10.0
dx = grindDegree * 5 # 双边滤波参数之一
fc = grindDegree * 12.5 # 双边滤波参数之一
temp1 = cv2.bilateralFilter(src[:, :, :3], dx, fc, fc)
temp2 = cv2.subtract(temp1, src[:, :, :3])
temp3 = cv2.GaussianBlur(temp2, (2 * detailDegree - 1, 2 * detailDegree - 1), 0)
temp4 = cv2.add(cv2.add(temp3, temp3), src[:, :, :3])
dst[:, :, :3] = cv2.addWeighted(temp4, opacity, src[:, :, :3], 1 - opacity, 0.0)
return dst
if __name__ == "__main__":
input_image = cv2.imread("test_image/7.jpg")
output_image = grindSkin(src=input_image)
cv2.imwrite("grindSkinCompare.png", np.hstack((input_image, output_image)))
Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

+304
View File
@@ -0,0 +1,304 @@
"""
@author: cuny
@file: ThinFace.py
@time: 2022/7/2 15:50
@description:
瘦脸算法,用到了图像局部平移法
先使用人脸关键点检测,然后再使用图像局部平移法
需要注意的是,这部分不会包含dlib人脸关键点检测,因为考虑到模型载入的问题
"""
import cv2
import math
import numpy as np
class TranslationWarp(object):
"""
本类包含瘦脸算法,由于瘦脸算法包含了很多个版本,所以以类的方式呈现
前两个算法没什么好讲的,网上资料很多
第三个采用numpy内部的自定义函数处理,在处理速度上有一些提升
最后采用cv2.map算法,处理速度大幅度提升
"""
# 瘦脸
@staticmethod
def localTranslationWarp(srcImg, startX, startY, endX, endY, radius):
# 双线性插值法
def BilinearInsert(src, ux, uy):
w, h, c = src.shape
if c == 3:
x1 = int(ux)
x2 = x1 + 1
y1 = int(uy)
y2 = y1 + 1
part1 = (
src[y1, x1].astype(np.float64) * (float(x2) - ux) * (float(y2) - uy)
)
part2 = (
src[y1, x2].astype(np.float64) * (ux - float(x1)) * (float(y2) - uy)
)
part3 = (
src[y2, x1].astype(np.float64) * (float(x2) - ux) * (uy - float(y1))
)
part4 = (
src[y2, x2].astype(np.float64) * (ux - float(x1)) * (uy - float(y1))
)
insertValue = part1 + part2 + part3 + part4
return insertValue.astype(np.int8)
ddradius = float(radius * radius) # 圆的半径
copyImg = srcImg.copy() # copy后的图像矩阵
# 计算公式中的|m-c|^2
ddmc = (endX - startX) * (endX - startX) + (endY - startY) * (endY - startY)
H, W, C = srcImg.shape # 获取图像的形状
for i in range(W):
for j in range(H):
# # 计算该点是否在形变圆的范围之内
# # 优化,第一步,直接判断是会在(startX,startY)的矩阵框中
if math.fabs(i - startX) > radius and math.fabs(j - startY) > radius:
continue
distance = (i - startX) * (i - startX) + (j - startY) * (j - startY)
if distance < ddradius:
# 计算出(i,j)坐标的原坐标
# 计算公式中右边平方号里的部分
ratio = (ddradius - distance) / (ddradius - distance + ddmc)
ratio = ratio * ratio
# 映射原位置
UX = i - ratio * (endX - startX)
UY = j - ratio * (endY - startY)
# 根据双线性插值法得到UXUY的值
# start_ = time.time()
value = BilinearInsert(srcImg, UX, UY)
# print(f"双线性插值耗时;{time.time() - start_}")
# 改变当前 i j的值
copyImg[j, i] = value
return copyImg
# 瘦脸pro1, 限制了for循环的遍历次数
@staticmethod
def localTranslationWarpLimitFor(
srcImg, startP: np.matrix, endP: np.matrix, radius: float
):
startX, startY = startP[0, 0], startP[0, 1]
endX, endY = endP[0, 0], endP[0, 1]
# 双线性插值法
def BilinearInsert(src, ux, uy):
w, h, c = src.shape
if c == 3:
x1 = int(ux)
x2 = x1 + 1
y1 = int(uy)
y2 = y1 + 1
part1 = (
src[y1, x1].astype(np.float64) * (float(x2) - ux) * (float(y2) - uy)
)
part2 = (
src[y1, x2].astype(np.float64) * (ux - float(x1)) * (float(y2) - uy)
)
part3 = (
src[y2, x1].astype(np.float64) * (float(x2) - ux) * (uy - float(y1))
)
part4 = (
src[y2, x2].astype(np.float64) * (ux - float(x1)) * (uy - float(y1))
)
insertValue = part1 + part2 + part3 + part4
return insertValue.astype(np.int8)
ddradius = float(radius * radius) # 圆的半径
copyImg = srcImg.copy() # copy后的图像矩阵
# 计算公式中的|m-c|^2
ddmc = (endX - startX) ** 2 + (endY - startY) ** 2
# 计算正方形的左上角起始点
startTX, startTY = (
startX - math.floor(radius + 1),
startY - math.floor((radius + 1)),
)
# 计算正方形的右下角的结束点
endTX, endTY = (
startX + math.floor(radius + 1),
startY + math.floor((radius + 1)),
)
# 剪切srcImg
srcImg = srcImg[startTY : endTY + 1, startTX : endTX + 1, :]
# db.cv_show(srcImg)
# 裁剪后的图像相当于在x,y都减少了startX - math.floor(radius + 1)
# 原本的endX, endY在切后的坐标点
endX, endY = (
endX - startX + math.floor(radius + 1),
endY - startY + math.floor(radius + 1),
)
# 原本的startX, startY剪切后的坐标点
startX, startY = (math.floor(radius + 1), math.floor(radius + 1))
H, W, C = srcImg.shape # 获取图像的形状
for i in range(W):
for j in range(H):
# 计算该点是否在形变圆的范围之内
# 优化,第一步,直接判断是会在(startX,startY)的矩阵框中
# if math.fabs(i - startX) > radius and math.fabs(j - startY) > radius:
# continue
distance = (i - startX) * (i - startX) + (j - startY) * (j - startY)
if distance < ddradius:
# 计算出(i,j)坐标的原坐标
# 计算公式中右边平方号里的部分
ratio = (ddradius - distance) / (ddradius - distance + ddmc)
ratio = ratio * ratio
# 映射原位置
UX = i - ratio * (endX - startX)
UY = j - ratio * (endY - startY)
# 根据双线性插值法得到UXUY的值
# start_ = time.time()
value = BilinearInsert(srcImg, UX, UY)
# print(f"双线性插值耗时;{time.time() - start_}")
# 改变当前 i j的值
copyImg[j + startTY, i + startTX] = value
return copyImg
# # 瘦脸pro2,采用了numpy自定义函数做处理
# def localTranslationWarpNumpy(self, srcImg, startP: np.matrix, endP: np.matrix, radius: float):
# startX , startY = startP[0, 0], startP[0, 1]
# endX, endY = endP[0, 0], endP[0, 1]
# ddradius = float(radius * radius) # 圆的半径
# copyImg = srcImg.copy() # copy后的图像矩阵
# # 计算公式中的|m-c|^2
# ddmc = (endX - startX)**2 + (endY - startY)**2
# # 计算正方形的左上角起始点
# startTX, startTY = (startX - math.floor(radius + 1), startY - math.floor((radius + 1)))
# # 计算正方形的右下角的结束点
# endTX, endTY = (startX + math.floor(radius + 1), startY + math.floor((radius + 1)))
# # 剪切srcImg
# self.thinImage = srcImg[startTY : endTY + 1, startTX : endTX + 1, :]
# # s = self.thinImage
# # db.cv_show(srcImg)
# # 裁剪后的图像相当于在x,y都减少了startX - math.floor(radius + 1)
# # 原本的endX, endY在切后的坐标点
# endX, endY = (endX - startX + math.floor(radius + 1), endY - startY + math.floor(radius + 1))
# # 原本的startX, startY剪切后的坐标点
# startX ,startY = (math.floor(radius + 1), math.floor(radius + 1))
# H, W, C = self.thinImage.shape # 获取图像的形状
# index_m = np.arange(H * W).reshape((H, W))
# triangle_ufunc = np.frompyfunc(self.process, 9, 3)
# # start_ = time.time()
# finalImgB, finalImgG, finalImgR = triangle_ufunc(index_m, self, W, ddradius, ddmc, startX, startY, endX, endY)
# finaleImg = np.dstack((finalImgB, finalImgG, finalImgR)).astype(np.uint8)
# finaleImg = np.fliplr(np.rot90(finaleImg, -1))
# copyImg[startTY: endTY + 1, startTX: endTX + 1, :] = finaleImg
# # print(f"图像处理耗时;{time.time() - start_}")
# # db.cv_show(copyImg)
# return copyImg
# 瘦脸pro3,采用opencv内置函数
@staticmethod
def localTranslationWarpFastWithStrength(
srcImg, startP: np.matrix, endP: np.matrix, radius, strength: float = 100.0
):
"""
采用opencv内置函数
Args:
srcImg: 源图像
startP: 起点位置
endP: 终点位置
radius: 处理半径
strength: 瘦脸强度,一般取100以上
Returns:
"""
startX, startY = startP[0, 0], startP[0, 1]
endX, endY = endP[0, 0], endP[0, 1]
ddradius = float(radius * radius)
# copyImg = np.zeros(srcImg.shape, np.uint8)
# copyImg = srcImg.copy()
maskImg = np.zeros(srcImg.shape[:2], np.uint8)
cv2.circle(maskImg, (startX, startY), math.ceil(radius), (255, 255, 255), -1)
K0 = 100 / strength
# 计算公式中的|m-c|^2
ddmc_x = (endX - startX) * (endX - startX)
ddmc_y = (endY - startY) * (endY - startY)
H, W, C = srcImg.shape
mapX = np.vstack([np.arange(W).astype(np.float32).reshape(1, -1)] * H)
mapY = np.hstack([np.arange(H).astype(np.float32).reshape(-1, 1)] * W)
distance_x = (mapX - startX) * (mapX - startX)
distance_y = (mapY - startY) * (mapY - startY)
distance = distance_x + distance_y
K1 = np.sqrt(distance)
ratio_x = (ddradius - distance_x) / (ddradius - distance_x + K0 * ddmc_x)
ratio_y = (ddradius - distance_y) / (ddradius - distance_y + K0 * ddmc_y)
ratio_x = ratio_x * ratio_x
ratio_y = ratio_y * ratio_y
UX = mapX - ratio_x * (endX - startX) * (1 - K1 / radius)
UY = mapY - ratio_y * (endY - startY) * (1 - K1 / radius)
np.copyto(UX, mapX, where=maskImg == 0)
np.copyto(UY, mapY, where=maskImg == 0)
UX = UX.astype(np.float32)
UY = UY.astype(np.float32)
copyImg = cv2.remap(srcImg, UX, UY, interpolation=cv2.INTER_LINEAR)
return copyImg
def thinFace(src, landmark, place: int = 0, strength=30.0):
"""
瘦脸程序接口,输入人脸关键点信息和强度,即可实现瘦脸
注意处理四通道图像
Args:
src: 原图
landmark: 关键点信息
place: 选择瘦脸区域,为0-4之间的值
strength: 瘦脸强度,输入值在0-10之间,如果小于或者等于0,则不瘦脸
Returns:
瘦脸后的图像
"""
strength = min(100.0, strength * 10.0)
if strength <= 0.0:
return src
# 也可以设置瘦脸区域
place = max(0, min(4, int(place)))
left_landmark = landmark[4 + place]
left_landmark_down = landmark[6 + place]
right_landmark = landmark[13 + place]
right_landmark_down = landmark[15 + place]
endPt = landmark[58]
# 计算第4个点到第6个点的距离作为瘦脸距离
r_left = math.sqrt(
(left_landmark[0, 0] - left_landmark_down[0, 0]) ** 2
+ (left_landmark[0, 1] - left_landmark_down[0, 1]) ** 2
)
# 计算第14个点到第16个点的距离作为瘦脸距离
r_right = math.sqrt(
(right_landmark[0, 0] - right_landmark_down[0, 0]) ** 2
+ (right_landmark[0, 1] - right_landmark_down[0, 1]) ** 2
)
# 瘦左边脸
thin_image = TranslationWarp.localTranslationWarpFastWithStrength(
src, left_landmark[0], endPt[0], r_left, strength
)
# 瘦右边脸
thin_image = TranslationWarp.localTranslationWarpFastWithStrength(
thin_image, right_landmark[0], endPt[0], r_right, strength
)
return thin_image
# if __name__ == "__main__":
# import os
# from hycv.FaceDetection68.faceDetection68 import FaceDetection68
# local_file = os.path.dirname(__file__)
# PREDICTOR_PATH = f"{local_file}/weights/shape_predictor_68_face_landmarks.dat" # 关键点检测模型路径
# fd68 = FaceDetection68(model_path=PREDICTOR_PATH)
# input_image = cv2.imread("test_image/4.jpg", -1)
# _, landmark_, _ = fd68.facePoints(input_image)
# output_image = thinFace(input_image, landmark_, strength=30.2)
# cv2.imwrite("thinFaceCompare.png", np.hstack((input_image, output_image)))
+83
View File
@@ -0,0 +1,83 @@
import cv2
import numpy as np
import os
import gradio as gr
class LutWhite:
CUBE64_ROWS = 8
CUBE64_SIZE = 64
CUBE256_SIZE = 256
CUBE_SCALE = CUBE256_SIZE // CUBE64_SIZE
def __init__(self, lut_image):
self.lut = self._create_lut(lut_image)
def _create_lut(self, lut_image):
reshape_lut = np.zeros(
(self.CUBE256_SIZE, self.CUBE256_SIZE, self.CUBE256_SIZE, 3), dtype=np.uint8
)
for i in range(self.CUBE64_SIZE):
tmp = i // self.CUBE64_ROWS
cx = (i % self.CUBE64_ROWS) * self.CUBE64_SIZE
cy = tmp * self.CUBE64_SIZE
cube64 = lut_image[cy : cy + self.CUBE64_SIZE, cx : cx + self.CUBE64_SIZE]
if cube64.size == 0:
continue
cube256 = cv2.resize(cube64, (self.CUBE256_SIZE, self.CUBE256_SIZE))
reshape_lut[i * self.CUBE_SCALE : (i + 1) * self.CUBE_SCALE] = cube256
return reshape_lut
def apply(self, src):
b, g, r = src[:, :, 0], src[:, :, 1], src[:, :, 2]
return self.lut[b, g, r]
class MakeWhiter:
def __init__(self, lut_image):
self.lut_white = LutWhite(lut_image)
def run(self, src: np.ndarray, strength: int) -> np.ndarray:
strength = np.clip(strength / 10.0, 0, 1)
if strength <= 0:
return src
img = self.lut_white.apply(src[:, :, :3])
return cv2.addWeighted(src[:, :, :3], 1 - strength, img, strength, 0)
base_dir = os.path.dirname(os.path.abspath(__file__))
default_lut = cv2.imread(os.path.join(base_dir, "lut/lut_origin.png"))
make_whiter = MakeWhiter(default_lut)
def make_whitening(image, strength):
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
output_image = make_whiter.run(image, strength)
return cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB)
def make_whitening_png(image, strength):
image = cv2.cvtColor(np.array(image), cv2.COLOR_RGBA2BGRA)
b, g, r, a = cv2.split(image)
bgr_image = cv2.merge((b, g, r))
b_w, g_w, r_w = cv2.split(make_whiter.run(bgr_image, strength))
output_image = cv2.merge((b_w, g_w, r_w, a))
return cv2.cvtColor(output_image, cv2.COLOR_RGBA2BGRA)
# 启动Gradio应用
if __name__ == "__main__":
demo = gr.Interface(
fn=make_whitening_png,
inputs=[
gr.Image(type="pil", image_mode="RGBA", label="Input Image"),
gr.Slider(0, 10, step=1, label="Whitening Strength"),
],
outputs=gr.Image(type="pil"),
title="Image Whitening Demo",
description="Upload an image and adjust the whitening strength to see the effect.",
)
demo.launch()