| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058 |
- import os
- import copy
- import time
- from .image_deal_base_func import *
- from PIL import Image, ImageDraw
- from blend_modes import multiply
- import os
- import settings
- from functools import wraps
- from .multi_threaded_image_saving import ImageSaver
- from .get_mask_by_green import GetMask
- from middleware import UnicornException
- from logger import logger
- from custom_plugins.plugins_mode.pic_deal import PictureProcessing
- from service.remove_bg_ali import Segment
- def time_it(func):
- @wraps(func) # 使用wraps来保留原始函数的元数据信息
- def wrapper(*args, **kwargs):
- start_time = time.time() # 记录开始时间
- result = func(*args, **kwargs) # 调用原始函数
- end_time = time.time() # 记录结束时间
- print(
- f"Executing {func.__name__} took {end_time - start_time:.4f} seconds."
- ) # 打印耗时
- return result
- return wrapper
- class GeneratePic(object):
- def __init__(self, is_test=False):
- # self.logger = MyLogger()
- self.is_test = is_test
- self.saver = ImageSaver()
- pass
- @time_it
- def get_mask_and_config_v3(self, im_jpg: Image, im_png: Image, curve_mask: bool,
- grenerate_main_pic_brightness: int):
- """
- 步骤:
- 1、尺寸进行对应缩小
- 2、查找并设定鞋底阴影蒙版
- 3、自动色阶检查亮度
- 4、输出自动色阶参数、以及放大的尺寸蒙版
- """
- # ===================尺寸进行对应缩小(提升处理速度)
- im_jpg = to_resize(im_jpg, width=600)
- im_png = to_resize(im_png, width=600)
- # =========================两个蒙版叠加,删除上半部分的图
- # 获取透明图的左右点
- result = get_extremes_from_transparent(im_png)
- # 创建多边形mask(并进行左右偏移)
- left_point = (result["leftmost"][0], result["leftmost"][1] - 50)
- right_point = (result["rightmost"][0], result["rightmost"][1] - 50)
- mask_other_2 = create_polygon_mask_from_points(img=im_png, left_point=left_point, right_point=right_point)
- # 透明图转mask 将原图扩边一些,并填充白色
- mask_other_1 = transparent_to_mask_pil(im_png, is_invert=False)
- mask_other_1 = expand_or_shrink_mask(pil_image=mask_other_1, expansion_radius=40, blur_radius=0)
- new_image_1 = Image.new("RGBA", im_png.size, (255, 255, 255, 0))
- im_grey_jpg = im_jpg.convert("L").convert("RGB")
- inverted_mask_other_1 = ImageChops.invert(mask_other_1)
- # 两个mask 取交集
- mask_other_2 = mask_other_2.convert("L")
- # 返回的蒙版区域
- return_mask = mask_other_2
- new_mask = mask_intersection(inverted_mask_other_1, mask_other_2)
- # new_mask.show()
- # return_mask.show()
- # TODO 待移除
- # ====================生成新的图片
- print("84 生成新的图片")
- bg = Image.new(mode="RGB", size=im_png.size, color=(255, 255, 255))
- bg.paste(im=im_jpg, mask=new_mask) # 只粘贴有阴影的地方
- # bg.show()
- # ==================自动色阶处理======================
- # 对上述拼接后的图片进行自动色阶处理
- _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
- # 背景阴影
- im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
- print("copy.copy(im_shadow)")
- _im_shadow = copy.copy(im_shadow)
- Midtones = 0.7
- Highlight = 235
- k = copy.copy(settings.COLOR_GRADATION_CYCLES)
- print("开始循环识别")
- xunhuan = 0
- while k:
- xunhuan += 1
- k -= 1
- Midtones += 0.035
- if Midtones > 1.7:
- Midtones = 1.7
- Highlight -= 3
- _im_shadow = levels_adjust(img=im_shadow,
- Shadow=0,
- Midtones=Midtones,
- Highlight=Highlight,
- OutShadow=0,
- OutHighlight=255, Dim=3)
- brightness_value = brightness_check(img_gray=_im_shadow, mask=new_mask)
- print("循环识别:{},Midtones:{},Highlight:{},brightness_value:{}".format(xunhuan,
- Midtones,
- Highlight,
- brightness_value))
- if brightness_value >= grenerate_main_pic_brightness:
- # //GRENERATE_MAIN_PIC_BRIGHTNESS 亮度校验
- break
- im_shadow = cv2_to_pil(_im_shadow)
- # if self.is_test:
- # im_shadow.show()
- # ========================================================
- # 计算阴影的亮度,用于确保阴影不要太黑
- # 1、图片预处理,只保留阴影
- only_shadow_img = im_shadow.copy()
- only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)),
- mask=im_png)
- # only_shadow_img.show()
- average_brightness = calculated_shadow_brightness(only_shadow_img)
- print("average_brightness:", average_brightness)
- config = {
- "Midtones": Midtones,
- "Highlight": Highlight,
- "average_brightness": average_brightness,
- }
- return return_mask, config
- @time_it
- def fast_remove_black_dots(self, mask_img, max_area=50):
- """
- 极速去除黑白蒙版中面积 <= max_area 的黑色噪点
- """
- # 1. 确保是纯黑白二值图 (0 和 255)
- # 如果原图有灰度,先做二值化,否则连通域计算会出错
- if mask_img.mode != 'L':
- mask_img = mask_img.convert('L')
- img_array = np.array(mask_img)
- _, binary = cv2.threshold(img_array, 127, 255, cv2.THRESH_BINARY)
- # 2. 连通域分析 (C++底层,极快)
- # 注意:OpenCV 默认白色(255)是前景,黑色(0)是背景
- # 我们要找的是“黑色的噪点”,所以先取反
- inv_binary = cv2.bitwise_not(binary)
- num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(inv_binary, connectivity=8)
- # 3. 提取需要去除的黑色区域
- # stats 包含每个连通域的面积 (cv2.CC_STAT_AREA)
- # 第 0 个标签是背景(原图的大面积白色),从 1 开始是黑色噪点
- areas = stats[1:, cv2.CC_STAT_AREA]
- small_dots_indices = np.where(areas <= max_area)[0] + 1 # 索引要加1,对应回 labels
- # 4. 批量修改原图(向量化操作,无需 for 循环)
- mask_to_remove = np.isin(labels, small_dots_indices)
- binary[mask_to_remove] = 255 # 将小噪点涂白
- return Image.fromarray(binary, mode='L')
- @time_it
- def get_mask_and_config_v4_online(self, ori_im_jpg: Image, ori_im_png: Image, im_jpg: Image, im_png: Image):
- print("179------当前计算函数:get_mask_and_config_v4_online")
- """
- 步骤:
- 1、尺寸进行对应缩小
- 2、查找并设定鞋底阴影蒙版
- 3、自动色阶检查亮度
- 4、输出自动色阶参数、以及放大的尺寸蒙版
- """
- # ===================尺寸进行对应缩小(提升处理速度)
- ori_im_jpg = to_resize(ori_im_jpg, width=1200)
- ori_im_png = to_resize(ori_im_png, width=1200)
- im_jpg = to_resize(im_jpg, width=600)
- im_png = to_resize(im_png, width=600)
- segment = Segment()
- api_url = f"{settings.DOMAIN}/api/ai_image/segment_shadow/platform_shadow"
- bg_mask_image_url = segment.get_platform_shadow(ori_im_jpg, api_url=api_url)
- if bg_mask_image_url:
- response = requests.get(bg_mask_image_url)
- pic = response.content
- bg_mask = Image.open(BytesIO(pic)) # 阿里返回的抠图结果 已转PIL对象
- bg_mask = bg_mask.convert("L")
- bg_mask = ImageChops.invert(bg_mask)
- _, new_box = get_mini_crop_img(img=ori_im_png)
- bg_mask = bg_mask.crop(new_box) # 切图
- bg_mask = bg_mask.resize(im_png.size)
- bg_mask = self.fast_remove_black_dots(bg_mask, max_area=50)
- # bg_mask = expand_or_shrink_mask(pil_image=bg_mask, expansion_radius=6, blur_radius=0)
- else:
- bg_mask = False
- bg_mask = Image.new("RGB", im_png.size, (255, 255, 255))
- bg_mask = bg_mask.convert("L")
- # 透明图转mask 将原图扩边一些,并填充白色
- shoe_png_mask = transparent_to_mask_pil(im_png, is_invert=False)
- shoe_png_mask = expand_or_shrink_mask(pil_image=shoe_png_mask, expansion_radius=40, blur_radius=0)
- shoe_png_mask = ImageChops.invert(shoe_png_mask)
- # 两个mask 取交集
- if bg_mask is not False:
- # new_mask 背景+鞋子+鞋子阴影的mask
- new_mask = mask_intersection(shoe_png_mask, bg_mask)
- else:
- new_mask = shoe_png_mask
- # 黑色表示鞋子+背景
- # new_mask.show()
- # ====================生成图片(一张图减去背景、减去阴影、减去鞋子,即只有底盘的图片,其他区域为白色)
- bg = Image.new(mode="RGB", size=im_png.size, color=(255, 255, 255))
- bg.paste(im=im_jpg, mask=new_mask) # 只粘贴有阴影的地方
- # bg.show()
- # ==================自动色阶处理======================
- # 对上述拼接后的图片进行自动色阶处理
- _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
- # 背景阴影
- im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
- _im_shadow = copy.copy(im_shadow)
- Midtones = 0.8
- Highlight = 235
- k = copy.copy(settings.COLOR_GRADATION_CYCLES)
- print("开始循环识别")
- xunhuan = 0
- while k:
- xunhuan += 1
- k -= 1
- Midtones += 0.035
- if Midtones > 1.7:
- Midtones = 1.7
- Highlight -= 3
- _im_shadow = levels_adjust(img=im_shadow,
- Shadow=0,
- Midtones=Midtones,
- Highlight=Highlight,
- OutShadow=0,
- OutHighlight=255, Dim=3)
- brightness_value = get_png_brightness(img_gray=_im_shadow, mask=new_mask)
- print("128----循环识别:{},Midtones:{},Highlight:{},brightness_value:{},阀值:{}".format(xunhuan,
- Midtones,
- Highlight,
- brightness_value,
- settings.GRENERATE_MAIN_PIC_BRIGHTNESS
- ))
- if brightness_value >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
- break
- im_shadow = cv2_to_pil(_im_shadow)
- # ========================================================
- # 计算阴影的亮度,用于确保阴影不要太黑
- # 1、图片预处理,只保留阴影
- only_shadow_img = im_shadow.copy()
- only_shadow_img.paste(Image.new(mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)),
- mask=im_png)
- # only_shadow_img.show()
- average_brightness = calculated_shadow_brightness(only_shadow_img)
- print("average_brightness:", average_brightness)
- config = {
- "Midtones": Midtones,
- "Highlight": Highlight,
- "average_brightness": average_brightness,
- }
- return bg_mask, config
- @time_it
- def get_mask_and_config(self, im_jpg: Image, im_png: Image, curve_mask: bool):
- """
- 步骤:
- 1、尺寸进行对应缩小
- 2、查找并设定鞋底阴影蒙版
- 3、自动色阶检查亮度
- 4、输出自动色阶参数、以及放大的尺寸蒙版
- """
- # ===================尺寸进行对应缩小(提升处理速度)
- im_jpg = to_resize(im_jpg, width=800)
- im_png = to_resize(im_png, width=800)
- x1, y1, x2, y2 = im_png.getbbox()
- cv2_png = pil_to_cv2(im_png)
- # =====================设定鞋底阴影图的蒙版
- # 查找每列的最低非透明点
- min_y_values = find_lowest_non_transparent_points(cv2_png)
- # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
- image_high = im_jpg.height
- print("图片高度:", image_high)
- cv2_jpg = pil_to_cv2(im_jpg)
- # 返回线条图片,以及最低位置
- print("返回线条图片,以及最低位置")
- # crop_image_box=(x1, y1, x2, y2),
- if curve_mask:
- crop_image_box = None
- else:
- # 不需要曲线部分的蒙版
- crop_image_box = (x1, y1, x2, y2)
- img_with_shifted_line, lowest_y = draw_shifted_line(
- image=cv2_jpg,
- min_y_values=min_y_values,
- shift_amount=15,
- one_line_pos=(x1, x2),
- line_color=(0, 0, 0),
- line_thickness=20,
- app=None,
- crop_image_box=crop_image_box,
- )
- print("66 制作蒙版")
- # 制作蒙版
- mask_line = cv2_to_pil(img_with_shifted_line)
- mask = mask_line.convert("L") # 转换为灰度图
- mask = ImageOps.invert(mask)
- # 蒙版扩边
- print("72 蒙版扩边")
- # 默认expansion_radius 65 blur_radius 45
- mask = expand_or_shrink_mask(
- pil_image=mask, expansion_radius=50, blur_radius=35
- )
- # =============使用绿色蒙版进行处理
- if settings.IS_GET_GREEN_MASK:
- print("============使用绿色蒙版进行处理")
- mask = mask.convert("RGB")
- white_bg = Image.new(mode="RGB", size=im_png.size, color=(0, 0, 0))
- green_areas_mask_pil = GetMask().find_green_areas(cv2_jpg)
- green_areas_mask_pil = expand_or_shrink_mask(
- pil_image=green_areas_mask_pil, expansion_radius=15, blur_radius=5
- )
- mask.paste(white_bg, mask=green_areas_mask_pil.convert("L"))
- mask = mask.convert("L")
- # ====================生成新的图片
- print("84 生成新的图片")
- bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
- bg.paste(im_png, mask=im_png)
- bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方
- if image_high > y2 + 20:
- lowest_y = y2 + 20
- if self.is_test:
- _bg = bg.copy()
- draw = ImageDraw.Draw(_bg)
- # 定义直线的起点和终点坐标
- start_point = (0, lowest_y) # 直线的起始点
- end_point = (_bg.width, lowest_y) # 直线的结束点
- # 定义直线的颜色(R, G, B)
- line_color = (255, 0, 0) # 红色
- _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
- # mask_line = mask_line.convert('L') # 转换为灰度图
- # mask_line = ImageOps.invert(mask_line)
- # _bg.paste(_r, mask=mask)
- # 绘制直线
- draw.line([start_point, end_point], fill=line_color, width=1)
- _bg.show()
- # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
- # bg.show()
- # ==================自动色阶处理======================
- # 对上述拼接后的图片进行自动色阶处理
- bg = bg.convert("RGB")
- _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
- # 背景阴影
- im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
- print("image_high lowest_y", image_high, lowest_y)
- if lowest_y < 0 or lowest_y >= image_high:
- lowest_y = image_high - 1
- print("image_high lowest_y", image_high, lowest_y)
- rows = [lowest_y] # 需要检查的像素行
- print("copy.copy(im_shadow)")
- _im_shadow = copy.copy(im_shadow)
- Midtones = 0.7
- Highlight = 235
- k = copy.copy(settings.COLOR_GRADATION_CYCLES)
- print("循环识别")
- xunhuan = 0
- while k:
- xunhuan += 1
- # if settings.app:
- # settings.app.processEvents()
- k -= 1
- Midtones += 0.035
- if Midtones > 1.7:
- Midtones = 1.7
- Highlight -= 3
- _im_shadow = levels_adjust(
- img=im_shadow,
- Shadow=0,
- Midtones=Midtones,
- Highlight=Highlight,
- OutShadow=0,
- OutHighlight=255,
- Dim=3,
- )
- brightness_list = calculate_average_brightness_opencv(
- img_gray=_im_shadow, rows_to_check=rows
- )
- print(
- "循环识别:{},Midtones:{},Highlight:{},brightness_list:{}".format(
- xunhuan, Midtones, Highlight, brightness_list
- )
- )
- if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
- break
- im_shadow = cv2_to_pil(_im_shadow)
- # ========================================================
- # 计算阴影的亮度,用于确保阴影不要太黑
- # 1、图片预处理,只保留阴影
- only_shadow_img = im_shadow.copy()
- only_shadow_img.paste(
- Image.new(
- mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
- ),
- mask=im_png,
- )
- average_brightness = calculated_shadow_brightness(only_shadow_img)
- print("average_brightness:", average_brightness)
- config = {
- "Midtones": Midtones,
- "Highlight": Highlight,
- "average_brightness": average_brightness,
- }
- return mask, config
- def get_mask_and_config_1_2025_05_18(self, im_jpg: Image, im_png: Image):
- """
- 步骤:
- 1、尺寸进行对应缩小
- 2、查找并设定鞋底阴影蒙版
- 3、自动色阶检查亮度
- 4、输出自动色阶参数、以及放大的尺寸蒙版
- """
- # ===================尺寸进行对应缩小(提升处理速度)
- im_jpg = to_resize(im_jpg, width=800)
- im_png = to_resize(im_png, width=800)
- x1, y1, x2, y2 = im_png.getbbox()
- cv2_png = pil_to_cv2(im_png)
- # =====================设定鞋底阴影图的蒙版
- # 查找每列的最低非透明点
- min_y_values = find_lowest_non_transparent_points(cv2_png)
- # 在鞋底最低处增加一条直线蒙版,蒙版宽度为有效区域大小
- image_high = im_jpg.height
- print("图片高度:", image_high)
- cv2_jpg = pil_to_cv2(im_jpg)
- # 返回线条图片,以及最低位置
- print("返回线条图片,以及最低位置")
- img_with_shifted_line, lowest_y = draw_shifted_line(
- image=cv2_jpg,
- min_y_values=min_y_values,
- shift_amount=15,
- one_line_pos=(x1, x2),
- line_color=(0, 0, 0),
- line_thickness=20,
- app=None,
- crop_image_box=(x1, y1, x2, y2),
- )
- print("66 制作蒙版")
- # 制作蒙版
- mask_line = cv2_to_pil(img_with_shifted_line)
- mask = mask_line.convert("L") # 转换为灰度图
- mask = ImageOps.invert(mask)
- # 蒙版扩边
- print("72 蒙版扩边")
- # 默认expansion_radius 65 blur_radius 45
- mask = expand_or_shrink_mask(
- pil_image=mask, expansion_radius=50, blur_radius=35
- )
- # mask1 = expand_mask(mask, expansion_radius=30, blur_radius=10)
- # mask1.save("mask1.png")
- # mask2 = expand_or_shrink_mask(pil_image=mask, expansion_radius=60, blur_radius=30)
- # mask2.save("mask2.png")
- # raise 11
- # ====================生成新的图片
- print("84 生成新的图片")
- bg = Image.new(mode="RGBA", size=im_png.size, color=(255, 255, 255, 255))
- bg.paste(im_png, mask=im_png)
- bg.paste(im_jpg, mask=mask) # 粘贴有阴影的地方
- if self.is_test:
- _bg = bg.copy()
- draw = ImageDraw.Draw(_bg)
- # 定义直线的起点和终点坐标
- start_point = (0, lowest_y) # 直线的起始点
- end_point = (_bg.width, lowest_y) # 直线的结束点
- # 定义直线的颜色(R, G, B)
- line_color = (255, 0, 0) # 红色
- # 绘制直线
- draw.line([start_point, end_point], fill=line_color, width=1)
- # mask.show()
- # bg = pil_to_cv2(bg)
- # cv2.line(bg, (x1, lowest_y + 5), (x2, lowest_y + 5), color=(0, 0, 0),thickness=2)
- # bg = cv2_to_pil(bg)
- _r = Image.new(mode="RGBA", size=im_png.size, color=(246, 147, 100, 255))
- mask_line = mask_line.convert("L") # 转换为灰度图
- mask_line = ImageOps.invert(mask_line)
- _bg.paste(_r, mask=mask)
- _bg.show()
- # bg.save(r"C:\Users\gymmc\Desktop\data\bg.png")
- # bg.show()
- # ==================自动色阶处理======================
- # 对上述拼接后的图片进行自动色阶处理
- bg = bg.convert("RGB")
- _im = cv2.cvtColor(np.asarray(bg), cv2.COLOR_RGB2BGR)
- # 背景阴影
- im_shadow = cv2.cvtColor(_im, cv2.COLOR_BGR2GRAY)
- print("image_high lowest_y", image_high, lowest_y)
- if lowest_y < 0 or lowest_y >= image_high:
- lowest_y = image_high - 1
- print("image_high lowest_y", image_high, lowest_y)
- rows = [lowest_y] # 需要检查的像素行
- print("copy.copy(im_shadow)")
- _im_shadow = copy.copy(im_shadow)
- Midtones = 0.7
- Highlight = 235
- k = 12
- print("循环识别")
- while k:
- print("循环识别:{}".format(k))
- # if settings.app:
- # settings.app.processEvents()
- k -= 1
- Midtones += 0.1
- if Midtones > 1:
- Midtones = 1
- Highlight -= 3
- _im_shadow = levels_adjust(
- img=im_shadow,
- Shadow=0,
- Midtones=Midtones,
- Highlight=Highlight,
- OutShadow=0,
- OutHighlight=255,
- Dim=3,
- )
- brightness_list = calculate_average_brightness_opencv(
- img_gray=_im_shadow, rows_to_check=rows
- )
- print(brightness_list)
- if brightness_list[0] >= settings.GRENERATE_MAIN_PIC_BRIGHTNESS:
- break
- print("Midtones,Highlight:", Midtones, Highlight)
- im_shadow = cv2_to_pil(_im_shadow)
- # ========================================================
- # 计算阴影的亮度,用于确保阴影不要太黑
- # 1、图片预处理,只保留阴影
- only_shadow_img = im_shadow.copy()
- only_shadow_img.paste(
- Image.new(
- mode="RGBA", size=only_shadow_img.size, color=(255, 255, 255, 255)
- ),
- mask=im_png,
- )
- average_brightness = calculated_shadow_brightness(only_shadow_img)
- print("average_brightness:", average_brightness)
- config = {
- "Midtones": Midtones,
- "Highlight": Highlight,
- "average_brightness": average_brightness,
- }
- return mask, config
- def my_test(self, **kwargs):
- if "output_queue" in kwargs:
- output_queue = kwargs["output_queue"]
- else:
- output_queue = None
- time.sleep(3)
- if output_queue is not None:
- output_queue.put(True)
- def paste_img(self, image, top_img, base="nw", value=(0, 0), ):
- """
- {
- "command": "paste_img",
- "im": 需要粘贴的图片
- "pos": {"plugins_mode": "relative", # pixel
- "base": "center", # nw,nc,ne,ec ... 各个方向参考点
- "value": (100, 100),
- "percentage": (0.5, 0.5),
- },
- "margins": (0, 0, 0, 0), # 上下左右边距
- }
- """
- value = (int(value[0]), int(value[1]))
- # 处理默认值
- base = "nw" if not base else base
- top, down, left, right = 0, 0, 0, 0
- # 基于右边,上下居中
- if base == "ec" or base == "ce":
- p_x = int(image.width - (top_img.width + value[0]))
- p_y = int((image.height - top_img.height) / 2) + value[1]
- # 基于顶部,左右居中
- if base == "nc" or base == "cn":
- # 顶部对齐
- deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
- (image.height - top_img.height) / 2
- )
- p_x = deviation_x + value[0] + left
- p_y = value[1]
- # 基于右上角
- if base == "en" or base == "ne":
- p_x = int(image.width - (top_img.width + value[0])) + left
- p_y = value[1]
- # 基于左上角
- if base == "nw" or base == "wn":
- deviation_x, deviation_y = 0, 0
- p_x, p_y = value
- # 基于底部,左右居中
- if base == "cs" or base == "sc":
- deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
- (image.height - top_img.height) / 2
- )
- p_y = image.height - (top_img.height + value[1] + down)
- p_x = deviation_x + value[0] + left
- # 上下左右居中
- if base == "center" or base == "cc":
- deviation_x, deviation_y = int((image.width - top_img.width) / 2), int(
- (image.height - top_img.height) / 2
- )
- p_x = deviation_x + value[0] + left
- p_y = deviation_y + value[1] + top
- # 基于左下角
- if base == "sw" or base == "ws":
- # deviation_x, deviation_y = 0, int((img.height - img_1.height))
- p_x = value[0] + left
- p_y = image.height - (top_img.height + value[1] + down)
- # 基于左边,上下居中
- if base == "wc" or base == "cw":
- p_x = value[0] + left
- p_y = int((image.height - top_img.height) / 2) + value[1] + top
- # 基于右下角
- if base == "es" or base == "se":
- p_x = int(image.width - (top_img.width + value[0])) + left
- p_y = image.height - (top_img.height + value[1] + down) + top
- try:
- image.paste(top_img, box=(p_x, p_y), mask=top_img)
- except:
- image.paste(top_img, box=(p_x, p_y), mask=top_img.convert("RGBA"))
- return image
- @time_it
- def run(
- self,
- image_path,
- cut_image_path,
- out_path,
- image_deal_mode=0,
- image_index=99,
- out_pic_size=1024,
- is_logo=True,
- out_process_path_1=None,
- out_process_path_2=None,
- resize_mode=None,
- max_box=None,
- logo_path="",
- curve_mask=False,
- **kwargs,
- ): # im 为cv对象
- """
- image_path:原始图
- cut_image_path:抠图结果 与原始图尺寸相同
- out_path:输出主图路径
- image_deal_mode:图片处理模式,1表示需要镜像处理
- image_index:图片顺序索引
- out_pic_size:输出图片宽度大小
- is_logo=True 是否要添加logo水印
- out_process_path_1=None, 有阴影的图片,白底非透明
- out_process_path_2=None, 已抠图的图片
- resize_mode=0,1,2 主体缩小尺寸
- curve_mask 为True时,表示为对鞋曲线部分的mask,不做剪裁
- """
- if "output_queue" in kwargs:
- output_queue = kwargs["output_queue"]
- else:
- output_queue = None
- # image_deal_mode = 0#不翻转图像
- padding_800image = settings.getSysConfigs(
- "basic_configs", "padding_800image", 100
- )
- color_800image = settings.getSysConfigs(
- "basic_configs", "color_800image", "#FFFFFF"
- )
- rgb_color = settings.hex_to_rgb(color_800image)
- # ==========先进行剪切原图
- _s = time.time()
- orign_im = Image.open(image_path)
- print("242 need_time_1:{}".format(time.time() - _s))
- orign_x, orign_y = orign_im.size
- orign_im_cut = Image.open(cut_image_path) # 原始图的已扣图
- cut_image, new_box = get_mini_crop_img(img=orign_im_cut)
- im_shadow = orign_im.crop(new_box) # 切图
- new_x, new_y = im_shadow.size
- # ================自动色阶处理
- _s = time.time()
- image_mask_config = settings.getSysConfigs("basic_configs", "image_mask_config",
- {"mode": 0, "opacity": 0.5, "grenerate_main_pic_brightness": 254})
- print("阴影图处理参数===>>>", image_mask_config)
- image_mask_mode = image_mask_config.get("mode", 0)
- image_mask_opacity = float(image_mask_config.get("opacity", 0.5))
- image_mask_grenerate_main_pic_brightness = int(image_mask_config.get("grenerate_main_pic_brightness", 254))
- if image_mask_mode == 0:
- shadow_mask, config = self.get_mask_and_config(
- im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask
- )
- elif image_mask_mode == 1:
- shadow_mask, config = self.get_mask_and_config_v3(im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask,
- grenerate_main_pic_brightness=image_mask_grenerate_main_pic_brightness)
- elif image_mask_mode == 2:
- shadow_mask, config = self.get_mask_and_config_v4_online(ori_im_jpg=orign_im,
- ori_im_png=orign_im_cut,
- im_jpg=im_shadow,
- im_png=cut_image)
- else:
- shadow_mask, config = self.get_mask_and_config_v3(im_jpg=im_shadow, im_png=cut_image, curve_mask=curve_mask,
- grenerate_main_pic_brightness=image_mask_grenerate_main_pic_brightness)
- print("242 need_time_2:{}".format(time.time() - _s))
- shadow_mask = shadow_mask.resize(im_shadow.size)
- # =====抠图,形成新的阴影背景图=====
- _new_im_shadow = Image.new(
- mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255)
- )
- _new_im_shadow.paste(im_shadow, mask=shadow_mask) # 粘贴有阴影的地方
- # _new_im_shadow.show()
- _new_im_shadow = pil_to_cv2(_new_im_shadow)
- _new_im_shadow = cv2.cvtColor(_new_im_shadow, cv2.COLOR_BGR2GRAY)
- _new_im_shadow = levels_adjust(
- img=_new_im_shadow,
- Shadow=0,
- Midtones=config["Midtones"],
- Highlight=config["Highlight"],
- OutShadow=0,
- OutHighlight=255,
- Dim=3,
- )
- im_shadow = cv2_to_pil(_new_im_shadow)
- # ================处理阴影的亮度==================
- average_brightness = config["average_brightness"]
- if image_mask_mode == 0:
- if config["average_brightness"] < 180:
- # 调整阴影亮度
- backdrop_prepped = np.asfarray(
- Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
- )
- im_shadow = im_shadow.convert("RGBA")
- source_prepped = np.asfarray(im_shadow)
- # im_shadow.show()
- opacity = (average_brightness - 30) / 160
- opacity = max(0.5, min(opacity, 1))
- print("阴影透明度:{}%".format(int(opacity * 100)))
- blended_np = multiply(
- backdrop_prepped, source_prepped, opacity=int(opacity * 100) / 100
- )
- im_shadow = Image.fromarray(np.uint8(blended_np)).convert("RGB")
- # im_shadow.show()
- else:
- backdrop_prepped = np.asfarray(
- Image.new(mode="RGBA", size=im_shadow.size, color=(255, 255, 255, 255))
- )
- im_shadow = im_shadow.convert("RGBA")
- source_prepped = np.asfarray(im_shadow)
- opacity_params = int(image_mask_opacity * 100)
- print("阴影透明度:{}%".format(opacity_params))
- blended_np = multiply(
- backdrop_prepped, source_prepped, opacity=opacity_params / 100
- )
- im_shadow = Image.fromarray(np.uint8(blended_np)).convert("RGB")
- im_shadow = self.filter_2(im_shadow=im_shadow, feather=100)
- # 把原图粘贴回去,避免色差
- im_shadow.paste(cut_image, (0, 0), mask=cut_image)
- # _new_im_shadow.show()
- # ===========处理其他====================
- # 保存带有阴影的底图,没有logo
- if out_process_path_1:
- out_image_1 = im_shadow.copy()
- if image_deal_mode == 1:
- out_image_1 = out_image_1.transpose(Image.FLIP_LEFT_RIGHT)
- self.saver.save_image(
- image=out_image_1, file_path=out_process_path_1, quality=100, dpi=(350, 350), _format="PNG"
- )
- # save_image_by_thread(image=out_image_1, out_path=out_process_path_1)
- # out_image_1.save(out_process_path_1)
- # 保存抠图结果,没有底图,没有logo
- if out_process_path_2:
- out_image_2 = cut_image.copy()
- if image_deal_mode == 1:
- out_image_2 = out_image_2.transpose(Image.FLIP_LEFT_RIGHT)
- self.saver.save_image(
- image=out_image_2, file_path=out_process_path_2, quality=100, dpi=(350, 350), _format="PNG"
- )
- # save_image_by_thread(image=out_image_2, out_path=out_process_path_2, save_mode="png")
- # out_image_2.save(out_process_path_2)
- # 不生成主图时直接退出
- if not out_path:
- return True
- if image_deal_mode == 1:
- # 翻转
- im_shadow = im_shadow.transpose(Image.FLIP_LEFT_RIGHT)
- cut_image = cut_image.transpose(Image.FLIP_LEFT_RIGHT)
- image_margin = int(padding_800image)
- bg_size = (1600, 1600)
- _offset_x, _offset_y = 0, 0
- scale_rate = 1
- # im_shadow.show()
- # =====================主图物体的缩放依据大小
- if image_margin is not None:
- _bbox = cut_image.getbbox()
- _x, _y = _bbox[0], _bbox[1]
- _w, _h = _bbox[2] - _bbox[0], _bbox[3] - _bbox[1]
- # 中心偏移量
- offset_x, offset_y = _x - (cut_image.width - _w) / 2, _y - (cut_image.height - _h) / 2,
- # print("中心偏移量:", offset_x, offset_y)
- # 透明底最小矩形
- scale_rate = self.get_scale(base_by_box=(bg_size[0] - image_margin * 2, bg_size[1] - image_margin * 2),
- image_size=(_w, _h))
- # 计算缩放比例,以及顶点相对位置
- # print("缩放比例:", scale_rate)
- # 偏移量
- _offset_x, _offset_y = offset_x * scale_rate, offset_y * scale_rate
- # print("偏移量:", _offset_x, _offset_y)
- # 阴影图缩放尺寸
- cut_image = to_resize(_im=cut_image, width=cut_image.width * scale_rate)
- im_shadow = to_resize(_im=im_shadow, width=im_shadow.width * scale_rate)
- else:
- if max_box:
- im_shadow = to_resize(_im=im_shadow, width=max_box[0], high=max_box[1])
- cut_image = to_resize(_im=cut_image, width=max_box[0], high=max_box[1])
- else:
- size_defind = 1400
- if resize_mode is None:
- im_shadow = to_resize(_im=im_shadow, width=size_defind, high=size_defind)
- cut_image = to_resize(_im=cut_image, width=size_defind, high=size_defind)
- elif resize_mode == 1:
- im_shadow = to_resize(_im=im_shadow, width=size_defind, high=size_defind)
- cut_image = to_resize(_im=cut_image, width=size_defind, high=size_defind)
- elif resize_mode == 2:
- # todo 兼容长筒靴等,将图片大小限制在一个指定的box内
- im_shadow = to_resize(_im=im_shadow, width=650)
- cut_image = to_resize(_im=cut_image, width=650)
- # 再次检查需要约束缩小到一定高度,适应长筒靴
- _im_x, _im_y = cut_image.size
- if _im_y > 1400:
- im_shadow = to_resize(_im=im_shadow, high=1400)
- cut_image = to_resize(_im=cut_image, high=1400)
- # 创建底层背景
- # 用户可设置的颜色值参数
- # image_bg = Image.new("RGB", bg_size, rgb_color)
- # image_bg = self.paste_img(image=image_bg, top_img=im_shadow, base="cc", value=(_offset_x * -1, _offset_y * -1))
- # image_bg = self.paste_img(image=image_bg, top_img=cut_image, base="cc", value=(_offset_x * -1, _offset_y * -1))
- image_bg = PictureProcessing("RGB", bg_size, rgb_color)
- image_bg = image_bg.to_overlay_pic_advance(mode="pixel",
- top_img=PictureProcessing(im=im_shadow),
- base="cc",
- value=(_offset_x * -1, _offset_y * -1),
- top_png_img=PictureProcessing(im=cut_image), )
- image_bg = image_bg.im
- image_bg_x, image_bg_y = image_bg.size
- image_x, image_y = im_shadow.size
- _x = int((image_bg_x - image_x) / 2)
- _y = int((image_bg_y - image_y) / 2)
- # image_bg.paste(im_shadow, (_x, _y))
- # image_bg.paste(cut_image, (_x, _y), cut_image) # 再叠加原图避免色差
- if "小苏" in settings.Company:
- # 所有主图加logo
- is_logo = True
- if is_logo:
- if not logo_path:
- logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
- else:
- if os.path.exists(logo_path):
- logo_im = Image.open(logo_path)
- if logo_im.mode != 'RGBA':
- logo_im = logo_im.convert('RGBA')
- else:
- logo_im = Image.new("RGBA", (1600, 1600), (0, 0, 0, 0))
- try:
- image_bg.paste(logo_im, (0, 0), logo_im)
- except Exception as e:
- alpha_mask = logo_im.split()[3]
- image_bg.paste(logo_im, (0, 0), alpha_mask)
- out_pci_factor = float(
- 1
- if settings.getSysConfigs("basic_configs", "image_sharpening", "1") == ""
- else settings.getSysConfigs("basic_configs", "image_sharpening", "1")
- )
- if out_pci_factor > 1.0:
- print("图片锐化处理")
- image_bg = sharpen_image(image_bg, factor=out_pci_factor)
- out_pci_mode = "." + settings.getSysConfigs(
- "basic_configs", "image_out_format", "png"
- )
- for imageSize in out_pic_size:
- dot_index = out_path.rfind(".")
- if dot_index != -1:
- # 拆分文件路径和后缀
- file_without_suffix = out_path[:dot_index]
- suffix = out_path[dot_index + 1:]
- else:
- file_without_suffix = out_path
- suffix = ""
- # 单独拼接字符串示例
- image_size_int = int(imageSize)
- image_size_str = str(imageSize)
- new_file_path = f"{file_without_suffix}_{image_size_str}.{suffix}"
- image_bg = image_bg.resize(
- (image_size_int, image_size_int), resample=settings.RESIZE_IMAGE_MODE
- )
- if image_size_int < 3000:
- if out_pci_mode == ".jpg":
- self.saver.save_image(
- image=image_bg,
- file_path=new_file_path,
- save_mode="jpg",
- quality=100,
- dpi=(350, 350),
- _format="JPEG",
- )
- elif out_pci_mode == ".png":
- self.saver.save_image(
- image=image_bg,
- file_path=new_file_path,
- quality=100,
- dpi=(350, 350),
- _format="PNG",
- )
- else:
- new_format = out_pci_mode.split(".")[-1]
- self.saver.save_image(
- image=image_bg,
- file_path=new_file_path,
- save_mode=new_format,
- quality=100,
- dpi=(350, 350),
- _format=new_format,
- )
- else:
- new_format = out_pci_mode.split(".")[-1]
- self.saver.save_image(
- image=image_bg,
- file_path=new_file_path,
- save_mode=new_format,
- quality=100,
- dpi=(350, 350),
- _format=new_format,
- )
- # image_bg.save(out_path)
- # 在函数结束时使用更安全的关闭方式
- # 清理所有可能打开的图片对象
- for img_var in ['orign_im', 'cut_image', 'logo_im', 'out_image_1', 'out_image_2']:
- if img_var in locals():
- img = locals()[img_var]
- if hasattr(img, 'close'):
- try:
- img.close()
- except Exception as e:
- logger.warning(f"关闭图片对象 {img_var} 时出错: {e}")
- if output_queue is not None:
- output_queue.put(True)
- return True
- def get_scale(self, base_by_box, image_size):
- box_width, box_height = int(base_by_box[0]), int(base_by_box[1])
- width, height = image_size[0], image_size[1]
- if box_width / box_height < width / height:
- scale = box_width / width
- else:
- scale = box_height / height
- return scale
- def filter_2(self, im_shadow, feather=100):
- # feather 羽化宽度,400 图建议 40~120,越大越柔和
- background = Image.new(mode="RGB", size=im_shadow.size, color=(255, 255, 255))
- # 遮罩边缘做羽化处理,设计一个蒙版
- w, h = im_shadow.size
- mask = Image.new(mode="L", size=im_shadow.size, color=0)
- draw = ImageDraw.Draw(mask)
- # x0, y0:矩形左上角坐标
- # x1, y1:矩形右下角坐标
- draw.rectangle([feather, feather, w - feather, h - feather], fill=255)
- # 高斯模糊,让边缘从白到黑渐变
- mask = mask.filter(ImageFilter.GaussianBlur(feather / 2))
- # if self.is_test:
- # mask.show()
- background.paste(im=im_shadow, mask=mask)
- im_shadow = background
- return im_shadow
|