From 30886e0eb57fa35fc814310fbca2f1bd0eae577d Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Fri, 27 Mar 2020 18:43:35 +0300 Subject: [PATCH 1/9] Adding requirements.txt --- requirements.txt | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..feb4270 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,17 @@ +cycler==0.10.0 +decorator==4.4.2 +imageio==2.8.0 +kiwisolver==1.1.0 +matplotlib==3.2.1 +networkx==2.4 +numpy==1.18.2 +opencv-python==4.2.0.32 +Pillow==7.0.0 +pyparsing==2.4.6 +python-dateutil==2.8.1 +PyWavelets==1.1.1 +scikit-image==0.16.2 +scipy==1.4.1 +six==1.14.0 +torch==1.4.0 +torchvision==0.5.0 From addead1b206f9aa6599e75ff5b95b99f58b7fb1b Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Wed, 1 Apr 2020 00:45:27 +0300 Subject: [PATCH 2/9] Adding change_color function --- makeup.py | 173 ++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 137 insertions(+), 36 deletions(-) diff --git a/makeup.py b/makeup.py index 7d8dbee..3de31f5 100644 --- a/makeup.py +++ b/makeup.py @@ -1,15 +1,27 @@ import cv2 -import os -import numpy as np -from skimage.filters import gaussian -from test import evaluate import argparse +import numpy as np +import matplotlib.pyplot as plt +from test import evaluate +from skimage.filters import gaussian -def parse_args(): - parse = argparse.ArgumentParser() - parse.add_argument('--img-path', default='imgs/116.jpg') - return parse.parse_args() +# plt.switch_backend("qt5Agg") +plt.switch_backend("tkAgg") +# TODO: Fix the dictionry +SEGMENTS = { + 0: "background", + 1: "skin", + 2: "r_brow", + 3: "l_brow", + 4: "r_eye", + 5: "l_eye", + 10: "nose", + 12: "u_lip", + 13: "l_lip", + 14: "neck", + 18: "hat", +} def sharpen(img): @@ -31,8 +43,8 @@ def sharpen(img): return np.array(img_out, dtype=np.uint8) -def hair(image, parsing, part=17, color=[230, 50, 20]): - b, g, r = color #[10, 50, 250] # [10, 250, 10] +def hair(image, parsing, part=17, color=[230, 250, 250]): + b, g, r = color # [10, 50, 250] # [10, 250, 10] tar_color = np.zeros_like(image) tar_color[:, :, 0] = b tar_color[:, :, 1] = g @@ -52,56 +64,145 @@ def hair(image, parsing, part=17, color=[230, 50, 20]): changed = sharpen(changed) changed[parsing != part] = image[parsing != part] + return changed -if __name__ == '__main__': +def change_color(image, parsed_mask, **kwargs): + """ + + :param image: + :param parsed_mask: + :param query: + :return: + + Query (kwargs) example: + + { + 'background': (R, G, B) + 'neck': (R, G, B) + 'skin': (R, G, B) + 'hat': (R, G, B) + 'nose': (R, G, B) + 'l_eye': (R, G, B) + 'r_eye': (R, G, B) + 'u_lip': (R, G, B) + 'l_lip': (R, G, B) + 'l_brow': (R, G, B) + 'r_brow': (R, G, B) + } + """ + # Permuting color spaces form RGB to BGR + query = {SEGMENTS[key]: color for key, color in kwargs.items()} + image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + target_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + + for key, color in query.items(): + b, g, r = color + # Allocate mask + mask = np.zeros_like(image) + mask[:, :, 0] = b + mask[:, :, 1] = g + mask[:, :, 2] = r + + if key == 12 or key == 13: + image_hsv[:, :, 0:2] = target_hsv[:, :, 0:2] + + else: + image_hsv[:, :, 0:1] = target_hsv[:, :, 0:1] + + new_image = sharpen(cv2.cvtColor(image_hsv, cv2.COLOR_HSV2BGR)) + + if key == 17: + new_image = sharpen(new_image) + + + new_image[parsed_mask != key] = image[parsed_mask != key] + + return new_image + +if __name__ == "__main__": # 1 face # 11 teeth # 12 upper lip # 13 lower lip # 17 hair - args = parse_args() + """ + 0: 'background' + 14: 'neck' + 1: 'skin' + 18: 'hat' + 10: 'nose' + 5: 'l_eye' + 4: 'r_eye' + 12: 'u_lip' + 13: 'l_lip' + 3: 'l_brow' + 2: 'r_brow' + + + 8: 'l_ear' + 9: 'r_ear' + 10: 'mouth' + 17: 'hair' + 15: 'ear_r' + 16: 'neck_l' + 18: 'cloth' + 3: 'eye_g' + """ + parse = argparse.ArgumentParser() + parse.add_argument("--img-path", default="imgs/before.jpg") + args = parse.parse_args() table = { - 'hair': 17, - 'upper_lip': 12, - 'lower_lip': 13 + "hair": 17, + "upper_lip": 12, + "lower_lip": 13, } - image_path = args.img_path - cp = 'cp/79999_iter.pth' + image_path = "/home/aziz/Projects/face/imgs/6.jpg" + cp = "cp/79999_iter.pth" image = cv2.imread(image_path) ori = image.copy() parsing = evaluate(image_path, cp) parsing = cv2.resize(parsing, image.shape[0:2], interpolation=cv2.INTER_NEAREST) - parts = [table['hair'], table['upper_lip'], table['lower_lip']] - - colors = [[230, 50, 20], [20, 70, 180], [20, 70, 180]] - - for part, color in zip(parts, colors): - image = hair(image, parsing, part, color) - - cv2.imshow('image', cv2.resize(ori, (512, 512))) - cv2.imshow('color', cv2.resize(image, (512, 512))) - - cv2.waitKey(0) - cv2.destroyAllWindows() - - - - - - - + parts = [ + table["hair"], + table["lower_lip"], + table["upper_lip"], + ] + alpha_slider_max = 255 + title_window = "Linear Blend" + change_color(image, parsing, u_lip=(-1, 0, 255)) + for i in range(2): + image = cv2.imread(image_path) + lips = np.random.randint(1, 255, (3)) + hair_ = np.random.randint(1, 255, (3)) + colors = np.array([hair_, lips, lips]) + for part, color in zip(parts, colors): + image = hair(image, parsing, part, color) + # kernel = np.ones((5, 5), np.float32) / 25 + # dst = cv.filter2D(image, -1, kernel) + dst = cv2.bilateralFilter(image, 30, 75, 75) + img = np.hstack((ori, dst)) + plt.imshow(cv2.cvtColor(cv2.resize(img, (2048, 1024)), cv2.COLOR_BGR2RGB)) + plt.show() + # cv2.imwrite("makeup.jpg", cv2.resize(img, (1536, 512))) + # cv2.imshow('color', cv2.resize(image, (512, 512))) + # cv2.imwrite('image_1.jpg', cv2.resize(ori, (512, 512))) + # cv2.imwrite('makeup.jpg', cv2.resize(img, (1536, 512))) + k = cv2.waitKey(0) & 0xFF + if k == 27: + print("killed") + cv2.destroyAllWindows() From f09640ef7ad9a568f4d9ae794d1c09239d4d2164 Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Wed, 1 Apr 2020 23:31:07 +0300 Subject: [PATCH 3/9] Adding new function to color the face --- makeup.py | 46 ++++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/makeup.py b/makeup.py index 3de31f5..6c5eeb5 100644 --- a/makeup.py +++ b/makeup.py @@ -8,19 +8,20 @@ # plt.switch_backend("qt5Agg") plt.switch_backend("tkAgg") -# TODO: Fix the dictionry + SEGMENTS = { - 0: "background", - 1: "skin", - 2: "r_brow", - 3: "l_brow", - 4: "r_eye", - 5: "l_eye", - 10: "nose", - 12: "u_lip", - 13: "l_lip", - 14: "neck", - 18: "hat", + "background":0, + "skin": 1, + "r_brow": 2, + "l_brow": 3, + "r_eye": 4, + "l_eye": 5, + "nose": 10, + "u_lip": 12, + "l_lip": 13, + "neck": 14, + "hair": 17, + "hat": 18, } @@ -95,31 +96,32 @@ def change_color(image, parsed_mask, **kwargs): # Permuting color spaces form RGB to BGR query = {SEGMENTS[key]: color for key, color in kwargs.items()} image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) - target_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) for key, color in query.items(): b, g, r = color # Allocate mask - mask = np.zeros_like(image) + mask = np.zeros_like(image_hsv) mask[:, :, 0] = b mask[:, :, 1] = g mask[:, :, 2] = r + target_hsv = cv2.cvtColor(mask, cv2.COLOR_BGR2HSV) if key == 12 or key == 13: image_hsv[:, :, 0:2] = target_hsv[:, :, 0:2] + elif key == 17: + image_hsv = sharpen(image_hsv) + else: image_hsv[:, :, 0:1] = target_hsv[:, :, 0:1] - new_image = sharpen(cv2.cvtColor(image_hsv, cv2.COLOR_HSV2BGR)) - - if key == 17: - new_image = sharpen(new_image) - - + new_image = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2BGR) new_image[parsed_mask != key] = image[parsed_mask != key] - return new_image + image = new_image + plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB)) + plt.show() + return cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB) if __name__ == "__main__": # 1 face @@ -178,7 +180,7 @@ def change_color(image, parsed_mask, **kwargs): alpha_slider_max = 255 title_window = "Linear Blend" - change_color(image, parsing, u_lip=(-1, 0, 255)) + change_color(image, parsing, u_lip=(255, 0, 0), l_lip=(255, 0, 0)) for i in range(2): image = cv2.imread(image_path) From 51b1ecc86470f2c3d3991c409f4a6ffa8ebbabcd Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Wed, 1 Apr 2020 23:31:31 +0300 Subject: [PATCH 4/9] Change code style --- interface.py | 0 model.py | 81 +++++++++++++++----------- requirements.txt | 148 ++++++++++++++++++++++++++++++++++++++++++----- resnet.py | 28 ++++----- test.py | 76 ++++++++++++++++-------- 5 files changed, 246 insertions(+), 87 deletions(-) create mode 100644 interface.py diff --git a/interface.py b/interface.py new file mode 100644 index 0000000..e69de29 diff --git a/model.py b/model.py index 040f41f..58a4336 100644 --- a/model.py +++ b/model.py @@ -8,18 +8,21 @@ import torchvision from resnet import Resnet18 + # from modules.bn import InPlaceABNSync as BatchNorm2d class ConvBNReLU(nn.Module): def __init__(self, in_chan, out_chan, ks=3, stride=1, padding=1, *args, **kwargs): super(ConvBNReLU, self).__init__() - self.conv = nn.Conv2d(in_chan, - out_chan, - kernel_size = ks, - stride = stride, - padding = padding, - bias = False) + self.conv = nn.Conv2d( + in_chan, + out_chan, + kernel_size=ks, + stride=stride, + padding=padding, + bias=False, + ) self.bn = nn.BatchNorm2d(out_chan) self.init_weight() @@ -32,7 +35,9 @@ def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) + class BiSeNetOutput(nn.Module): def __init__(self, in_chan, mid_chan, n_classes, *args, **kwargs): @@ -50,7 +55,8 @@ def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) def get_params(self): wd_params, nowd_params = [], [] @@ -68,7 +74,7 @@ class AttentionRefinementModule(nn.Module): def __init__(self, in_chan, out_chan, *args, **kwargs): super(AttentionRefinementModule, self).__init__() self.conv = ConvBNReLU(in_chan, out_chan, ks=3, stride=1, padding=1) - self.conv_atten = nn.Conv2d(out_chan, out_chan, kernel_size= 1, bias=False) + self.conv_atten = nn.Conv2d(out_chan, out_chan, kernel_size=1, bias=False) self.bn_atten = nn.BatchNorm2d(out_chan) self.sigmoid_atten = nn.Sigmoid() self.init_weight() @@ -86,7 +92,8 @@ def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) class ContextPath(nn.Module): @@ -110,16 +117,16 @@ def forward(self, x): avg = F.avg_pool2d(feat32, feat32.size()[2:]) avg = self.conv_avg(avg) - avg_up = F.interpolate(avg, (H32, W32), mode='nearest') + avg_up = F.interpolate(avg, (H32, W32), mode="nearest") feat32_arm = self.arm32(feat32) feat32_sum = feat32_arm + avg_up - feat32_up = F.interpolate(feat32_sum, (H16, W16), mode='nearest') + feat32_up = F.interpolate(feat32_sum, (H16, W16), mode="nearest") feat32_up = self.conv_head32(feat32_up) feat16_arm = self.arm16(feat16) feat16_sum = feat16_arm + feat32_up - feat16_up = F.interpolate(feat16_sum, (H8, W8), mode='nearest') + feat16_up = F.interpolate(feat16_sum, (H8, W8), mode="nearest") feat16_up = self.conv_head16(feat16_up) return feat8, feat16_up, feat32_up # x8, x8, x16 @@ -128,7 +135,8 @@ def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) def get_params(self): wd_params, nowd_params = [], [] @@ -163,7 +171,8 @@ def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) def get_params(self): wd_params, nowd_params = [], [] @@ -181,18 +190,12 @@ class FeatureFusionModule(nn.Module): def __init__(self, in_chan, out_chan, *args, **kwargs): super(FeatureFusionModule, self).__init__() self.convblk = ConvBNReLU(in_chan, out_chan, ks=1, stride=1, padding=0) - self.conv1 = nn.Conv2d(out_chan, - out_chan//4, - kernel_size = 1, - stride = 1, - padding = 0, - bias = False) - self.conv2 = nn.Conv2d(out_chan//4, - out_chan, - kernel_size = 1, - stride = 1, - padding = 0, - bias = False) + self.conv1 = nn.Conv2d( + out_chan, out_chan // 4, kernel_size=1, stride=1, padding=0, bias=False + ) + self.conv2 = nn.Conv2d( + out_chan // 4, out_chan, kernel_size=1, stride=1, padding=0, bias=False + ) self.relu = nn.ReLU(inplace=True) self.sigmoid = nn.Sigmoid() self.init_weight() @@ -213,7 +216,8 @@ def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) def get_params(self): wd_params, nowd_params = [], [] @@ -248,22 +252,29 @@ def forward(self, x): feat_out16 = self.conv_out16(feat_cp8) feat_out32 = self.conv_out32(feat_cp16) - feat_out = F.interpolate(feat_out, (H, W), mode='bilinear', align_corners=True) - feat_out16 = F.interpolate(feat_out16, (H, W), mode='bilinear', align_corners=True) - feat_out32 = F.interpolate(feat_out32, (H, W), mode='bilinear', align_corners=True) + feat_out = F.interpolate(feat_out, (H, W), mode="bilinear", align_corners=True) + feat_out16 = F.interpolate( + feat_out16, (H, W), mode="bilinear", align_corners=True + ) + feat_out32 = F.interpolate( + feat_out32, (H, W), mode="bilinear", align_corners=True + ) return feat_out, feat_out16, feat_out32 def init_weight(self): for ly in self.children(): if isinstance(ly, nn.Conv2d): nn.init.kaiming_normal_(ly.weight, a=1) - if not ly.bias is None: nn.init.constant_(ly.bias, 0) + if not ly.bias is None: + nn.init.constant_(ly.bias, 0) def get_params(self): wd_params, nowd_params, lr_mul_wd_params, lr_mul_nowd_params = [], [], [], [] for name, child in self.named_children(): child_wd_params, child_nowd_params = child.get_params() - if isinstance(child, FeatureFusionModule) or isinstance(child, BiSeNetOutput): + if isinstance(child, FeatureFusionModule) or isinstance( + child, BiSeNetOutput + ): lr_mul_wd_params += child_wd_params lr_mul_nowd_params += child_nowd_params else: @@ -276,8 +287,8 @@ def get_params(self): net = BiSeNet(19) net.cuda() net.eval() - in_ten = torch.randn(16, 3, 640, 480).cuda() + in_ten = torch.randn(2, 3, 640, 480).cuda() out, out16, out32 = net(in_ten) print(out.shape) - net.get_params() + print(net.get_params()) diff --git a/requirements.txt b/requirements.txt index feb4270..d1c39ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,17 +1,139 @@ -cycler==0.10.0 +anytree==2.8.0 +appdirs==1.4.3 +arrow==0.15.5 +asn1crypto==1.3.0 +astroid==2.3.3 +attrs==19.3.0 +autopep8==1.5 +backcall==0.1.0 +Beaker==1.11.0 +beautifulsoup4==4.8.2 +binaryornot==0.4.4 +black==19.10b0 +bleach==3.1.1 +btrfsutil==1.1.1 +CacheControl==0.12.6 +certifi==2019.11.28 +cffi==1.14.0 +chardet==3.0.4 +Click==7.0 +colorama==0.4.3 +contextlib2==0.6.0 +cookiecutter==1.7.0 +crcmod==1.7 +cryptography==2.8 decorator==4.4.2 -imageio==2.8.0 -kiwisolver==1.1.0 -matplotlib==3.2.1 -networkx==2.4 -numpy==1.18.2 -opencv-python==4.2.0.32 -Pillow==7.0.0 +defusedxml==0.6.0 +distlib==0.3.0 +distro==1.4.0 +entrypoints==0.3 +flake8==3.7.9 +future==0.18.2 +greenlet==0.4.15 +html2text==2019.8.11 +html5lib==1.0.1 +idna==2.9 +importlib-metadata==1.5.0 +iotop==0.6 +ipykernel==5.1.4 +ipython==7.13.0 +ipython-genutils==0.1.0 +ipywidgets==7.5.1 +isc==2.0 +isort==4.3.21 +jedi==0.15.1 +Jinja2==2.11.1 +jinja2-time==0.2.0 +json5==0.9.2 +jsonschema==3.2.0 +jupyter-client==6.0.0 +jupyter-console==6.1.0 +jupyter-core==4.6.3 +jupyterlab==2.0.1 +jupyterlab-server==1.0.7 +kawaii-player==4.2.0 +lazy-object-proxy==1.4.3 +lensfun==0.3.95 +lit==0.9.1.dev0 +louis==3.13.0 +lxml==4.5.0 +Mako==1.1.2 +mallard-ducktype==1.0.2 +Markdown==3.1.1 +MarkupSafe==1.1.1 +mccabe==0.6.1 +meson==0.53.2 +mistune==0.8.4 +more-itertools==8.2.0 +msgpack==0.6.2 +mutagen==1.44.0 +nbconvert==5.6.1 +nbformat==4.4.0 +neovim-remote==2.4.0 +notebook==6.0.3 +numpy==1.18.1 +ordered-set==3.1.1 +packaging==20.3 +pandocfilters==1.4.2 +parso==0.6.1 +pathspec==0.7.0 +pep517==0.8.1 +pexpect==4.8.0 +pickleshare==0.7.5 +Pillow==6.2.1 +pluggy==0.13.1 +ply==3.11 +powerline-status==2.7 +poyo==0.5.0 +progress==1.5 +prometheus-client==0.7.1 +prompt-toolkit==3.0.4 +psutil==5.7.0 +ptyprocess==0.6.0 +pycodestyle==2.5.0 +pycparser==2.20 +pycurl==7.43.0.5 +pydocstyle==5.0.2 +pyflakes==2.1.1 +Pygments==2.6.1 +pylint==2.4.4 +pynvim==0.4.1 +PyOpenGL==3.1.5 +pyOpenSSL==19.1.0 pyparsing==2.4.6 +PyQt5==5.14.1 +PyQt5-sip==12.7.1 +PyQtWebEngine==5.14.0 +pyrsistent==0.15.7 +PySide2==5.14.2 python-dateutil==2.8.1 -PyWavelets==1.1.1 -scikit-image==0.16.2 -scipy==1.4.1 +python-jsonrpc-server==0.3.4 +python-language-server==0.31.8 +pytoml==0.1.21 +pyzmq==19.0.0 +regex==2020.2.20 +requests==2.23.0 +retrying==1.3.3 +rope==0.16.0 +Send2Trash==1.5.0 +shiboken2==5.14.2 six==1.14.0 -torch==1.4.0 -torchvision==0.5.0 +snowballstemmer==2.0.0 +soupsieve==1.9.5 +team==1.0 +terminado==0.8.3 +testpath==0.4.4 +toml==0.10.0 +tornado==6.0.4 +traitlets==4.3.3 +typed-ast==1.4.1 +ujson==1.35 +urllib3==1.25.8 +virtualenv==16.7.9 +wcwidth==0.1.8 +webencodings==0.5.1 +whichcraft==0.6.1 +widgetsnbextension==3.5.1 +wrapt==1.11.2 +yapf==0.29.0 +zipp==3.1.0 diff --git a/resnet.py b/resnet.py index aa2bf95..e74be59 100644 --- a/resnet.py +++ b/resnet.py @@ -8,13 +8,14 @@ # from modules.bn import InPlaceABNSync as BatchNorm2d -resnet18_url = 'https://download.pytorch.org/models/resnet18-5c106cde.pth' +resnet18_url = "https://download.pytorch.org/models/resnet18-5c106cde.pth" def conv3x3(in_planes, out_planes, stride=1): """3x3 convolution with padding""" - return nn.Conv2d(in_planes, out_planes, kernel_size=3, stride=stride, - padding=1, bias=False) + return nn.Conv2d( + in_planes, out_planes, kernel_size=3, stride=stride, padding=1, bias=False + ) class BasicBlock(nn.Module): @@ -28,10 +29,9 @@ def __init__(self, in_chan, out_chan, stride=1): self.downsample = None if in_chan != out_chan or stride != 1: self.downsample = nn.Sequential( - nn.Conv2d(in_chan, out_chan, - kernel_size=1, stride=stride, bias=False), + nn.Conv2d(in_chan, out_chan, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(out_chan), - ) + ) def forward(self, x): residual = self.conv1(x) @@ -50,7 +50,7 @@ def forward(self, x): def create_layer_basic(in_chan, out_chan, bnum, stride=1): layers = [BasicBlock(in_chan, out_chan, stride=stride)] - for i in range(bnum-1): + for i in range(bnum - 1): layers.append(BasicBlock(out_chan, out_chan, stride=1)) return nn.Sequential(*layers) @@ -58,8 +58,7 @@ def create_layer_basic(in_chan, out_chan, bnum, stride=1): class Resnet18(nn.Module): def __init__(self): super(Resnet18, self).__init__() - self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, - bias=False) + self.conv1 = nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False) self.bn1 = nn.BatchNorm2d(64) self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1) self.layer1 = create_layer_basic(64, 64, bnum=2, stride=1) @@ -74,16 +73,17 @@ def forward(self, x): x = self.maxpool(x) x = self.layer1(x) - feat8 = self.layer2(x) # 1/8 - feat16 = self.layer3(feat8) # 1/16 - feat32 = self.layer4(feat16) # 1/32 + feat8 = self.layer2(x) # 1/8 + feat16 = self.layer3(feat8) # 1/16 + feat32 = self.layer4(feat16) # 1/32 return feat8, feat16, feat32 def init_weight(self): state_dict = modelzoo.load_url(resnet18_url) self_state_dict = self.state_dict() for k, v in state_dict.items(): - if 'fc' in k: continue + if "fc" in k: + continue self_state_dict.update({k: v}) self.load_state_dict(self_state_dict) @@ -94,7 +94,7 @@ def get_params(self): wd_params.append(module.weight) if not module.bias is None: nowd_params.append(module.bias) - elif isinstance(module, nn.BatchNorm2d): + elif isinstance(module, nn.BatchNorm2d): nowd_params += list(module.parameters()) return wd_params, nowd_params diff --git a/test.py b/test.py index 7360a0c..e1f5ebd 100644 --- a/test.py +++ b/test.py @@ -11,23 +11,46 @@ import cv2 -def vis_parsing_maps(im, parsing_anno, stride, save_im=False, save_path='vis_results/parsing_map_on_im.jpg'): +def vis_parsing_maps( + im, parsing_anno, stride, save_im=True, save_path="output/parsing_map_on_im.jpg" +): # Colors for all 20 parts - part_colors = [[255, 0, 0], [255, 85, 0], [255, 170, 0], - [255, 0, 85], [255, 0, 170], - [0, 255, 0], [85, 255, 0], [170, 255, 0], - [0, 255, 85], [0, 255, 170], - [0, 0, 255], [85, 0, 255], [170, 0, 255], - [0, 85, 255], [0, 170, 255], - [255, 255, 0], [255, 255, 85], [255, 255, 170], - [255, 0, 255], [255, 85, 255], [255, 170, 255], - [0, 255, 255], [85, 255, 255], [170, 255, 255]] + part_colors = [ + [255, 0, 0], + [255, 85, 0], + [255, 170, 0], + [255, 0, 85], + [255, 0, 170], + [0, 255, 0], + [85, 255, 0], + [170, 255, 0], + [0, 255, 85], + [0, 255, 170], + [0, 0, 255], + [85, 0, 255], + [170, 0, 255], + [0, 85, 255], + [0, 170, 255], + [255, 255, 0], + [255, 255, 85], + [255, 255, 170], + [255, 0, 255], + [255, 85, 255], + [255, 170, 255], + [0, 255, 255], + [85, 255, 255], + [170, 255, 255], + ] im = np.array(im) vis_im = im.copy().astype(np.uint8) vis_parsing_anno = parsing_anno.copy().astype(np.uint8) - vis_parsing_anno = cv2.resize(vis_parsing_anno, None, fx=stride, fy=stride, interpolation=cv2.INTER_NEAREST) - vis_parsing_anno_color = np.zeros((vis_parsing_anno.shape[0], vis_parsing_anno.shape[1], 3)) + 255 + vis_parsing_anno = cv2.resize( + vis_parsing_anno, None, fx=stride, fy=stride, interpolation=cv2.INTER_NEAREST + ) + vis_parsing_anno_color = ( + np.zeros((vis_parsing_anno.shape[0], vis_parsing_anno.shape[1], 3)) + 255 + ) num_of_class = np.max(vis_parsing_anno) @@ -37,17 +60,19 @@ def vis_parsing_maps(im, parsing_anno, stride, save_im=False, save_path='vis_res vis_parsing_anno_color = vis_parsing_anno_color.astype(np.uint8) # print(vis_parsing_anno_color.shape, vis_im.shape) - vis_im = cv2.addWeighted(cv2.cvtColor(vis_im, cv2.COLOR_RGB2BGR), 0.4, vis_parsing_anno_color, 0.6, 0) + vis_im = cv2.addWeighted( + cv2.cvtColor(vis_im, cv2.COLOR_RGB2BGR), 0.4, vis_parsing_anno_color, 0.6, 0 + ) # Save result or not if save_im: - cv2.imwrite(save_path[:-4] +'.png', vis_parsing_anno) + cv2.imwrite(save_path[:-4] + ".png", vis_parsing_anno) cv2.imwrite(save_path, vis_im, [int(cv2.IMWRITE_JPEG_QUALITY), 100]) return vis_parsing_anno # return vis_im -def evaluate(image_path='./imgs/116.jpg', cp='cp/79999_iter.pth'): +def evaluate(image_path="./imgs/116.jpg", cp="cp/79999_iter.pth"): # if not os.path.exists(respth): # os.makedirs(respth) @@ -58,10 +83,12 @@ def evaluate(image_path='./imgs/116.jpg', cp='cp/79999_iter.pth'): net.load_state_dict(torch.load(cp)) net.eval() - to_tensor = transforms.Compose([ - transforms.ToTensor(), - transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)), - ]) + to_tensor = transforms.Compose( + [ + transforms.ToTensor(), + transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)), + ] + ) with torch.no_grad(): img = Image.open(image_path) @@ -71,13 +98,12 @@ def evaluate(image_path='./imgs/116.jpg', cp='cp/79999_iter.pth'): img = img.cuda() out = net(img)[0] parsing = out.squeeze(0).cpu().numpy().argmax(0) - # print(parsing) - # print(np.unique(parsing)) + print(parsing) + print(np.unique(parsing)) - # vis_parsing_maps(image, parsing, stride=1, save_im=False, save_path=osp.join(respth, dspth)) + vis_parsing_maps(image, parsing, stride=1) return parsing -if __name__ == "__main__": - evaluate(dspth='/home/zll/data/CelebAMask-HQ/test-img/116.jpg', cp='79999_iter.pth') - +if __name__ == "__main__": + evaluate(image_path="./imgs/before.jpg", cp="./cp/79999_iter.pth") From 20aefb26aabbc9fb044dfb6c3c7b4180ad5983ee Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Thu, 2 Apr 2020 19:19:17 +0300 Subject: [PATCH 5/9] Working demo --- interface.py | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++ makeup.py | 12 ++++++-- test.py | 4 ++- 3 files changed, 97 insertions(+), 4 deletions(-) diff --git a/interface.py b/interface.py index e69de29..9a3277c 100644 --- a/interface.py +++ b/interface.py @@ -0,0 +1,85 @@ +from flask import Flask, request, Response, send_file + +import jsonpickle +import numpy as np +import cv2 +import io + + +from makeup import change_color +from test import evaluate +from PIL import Image + +import matplotlib.pyplot as plt + +app = Flask(__name__) + +@app.route("/demo", methods=['GET']) +def apply_makeup(): + r = request + np_array = np.frombuffer(r.data, np.uint8) + img = cv2.imdecode(np_array, cv2.IMREAD_COLOR) + + cp = "cp/79999_iter.pth" + + parsing = evaluate(img, cp) + parsing = cv2.resize(parsing, img.shape[0:2], interpolation=cv2.INTER_NEAREST) + # print([key for key, value in **request.args.get().items()]) + modified_img = change_color(img, parsing, **request.args) + + cv2.imwrite("img.jpg", cv2.cvtColor(modified_img, cv2.COLOR_BGR2RGB)) + response = {'message': 'image received. size={}x{}'.format(img.shape[1], img.shape[0]) + } + + # encode response using jsonpickle + response_pickled = jsonpickle.encode(response) + + #pil_image = Image.fromarray(modified_img) + # pil_image = io.StringIO(Image.fromarray(modified_img)) + + + return send_file("img.jpg", mimetype="image/jpeg", attachment_filename="new.jpeg", as_attachment=False) + + + +if __name__ == "__main__": + app.debug = True + app.run() + + + +# +# from flask import Flask, request, Response, send_file +# import jsonpickle +# import numpy as np +# import cv2 +# +# import ImageProcessingFlask +# +# # Initialize the Flask application +# app = Flask(__name__) +# +# +# # route http posts to this method +# @app.route('/api/test', methods=['POST']) +# def test(): +# r = request +# # convert string of image data to uint8 +# nparr = np.fromstring(r.data, np.uint8) +# # decode image +# img = cv2.imdecode(nparr, cv2.IMREAD_COLOR) +# +# # do some fancy processing here.... +# +# img = ImageProcessingFlask.render(img) +# +# +# #_, img_encoded = cv2.imencode('.jpg', img) +# #print ( img_encoded) +# +# cv2.imwrite( 'new.jpeg', img) +# +# +# #response_pickled = jsonpickle.encode(response) +# #return Response(response=response_pickled, status=200, mimetype="application/json") +# return send_file( 'new.jpeg', mimetype="image/jpeg", attachment_filename="new.jpeg", as_attachment=True) diff --git a/makeup.py b/makeup.py index 6c5eeb5..bfa679a 100644 --- a/makeup.py +++ b/makeup.py @@ -97,7 +97,12 @@ def change_color(image, parsed_mask, **kwargs): query = {SEGMENTS[key]: color for key, color in kwargs.items()} image_hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV) + changed_image = None + if not query: + return image for key, color in query.items(): + if not isinstance(color, tuple): + color = tuple(color.split(',')) b, g, r = color # Allocate mask mask = np.zeros_like(image_hsv) @@ -119,9 +124,10 @@ def change_color(image, parsed_mask, **kwargs): new_image[parsed_mask != key] = image[parsed_mask != key] image = new_image - plt.imshow(cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB)) - plt.show() - return cv2.cvtColor(new_image, cv2.COLOR_BGR2RGB) + changed_image = new_image.copy() + + + return cv2.cvtColor(changed_image, cv2.COLOR_BGR2RGB) if __name__ == "__main__": # 1 face diff --git a/test.py b/test.py index e1f5ebd..e05aa5f 100644 --- a/test.py +++ b/test.py @@ -91,7 +91,9 @@ def evaluate(image_path="./imgs/116.jpg", cp="cp/79999_iter.pth"): ) with torch.no_grad(): - img = Image.open(image_path) + img = Image.fromarray(image_path) + # img = Image.open(image_path) + image = img.resize((512, 512), Image.BILINEAR) img = to_tensor(image) img = torch.unsqueeze(img, 0) From a60590ac56b4c5fe5d612d27dd977a2792a88d17 Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Thu, 2 Apr 2020 20:38:34 +0300 Subject: [PATCH 6/9] Update requirements --- interface.py | 22 ++++--- makeup.py | 28 ++++----- requirements.txt | 146 +++-------------------------------------------- 3 files changed, 35 insertions(+), 161 deletions(-) diff --git a/interface.py b/interface.py index 9a3277c..6c88bf4 100644 --- a/interface.py +++ b/interface.py @@ -14,7 +14,10 @@ app = Flask(__name__) -@app.route("/demo", methods=['GET']) +# localhost:5000/demo?u_lip=0,0,255&l_lip=0,0,255&hair=0,0,255&skin=255,255,255 + + +@app.route("/demo", methods=["GET"]) def apply_makeup(): r = request np_array = np.frombuffer(r.data, np.uint8) @@ -28,18 +31,22 @@ def apply_makeup(): modified_img = change_color(img, parsing, **request.args) cv2.imwrite("img.jpg", cv2.cvtColor(modified_img, cv2.COLOR_BGR2RGB)) - response = {'message': 'image received. size={}x{}'.format(img.shape[1], img.shape[0]) - } + response = { + "message": "image received. size={}x{}".format(img.shape[1], img.shape[0]) + } # encode response using jsonpickle response_pickled = jsonpickle.encode(response) - #pil_image = Image.fromarray(modified_img) + # pil_image = Image.fromarray(modified_img) # pil_image = io.StringIO(Image.fromarray(modified_img)) - - return send_file("img.jpg", mimetype="image/jpeg", attachment_filename="new.jpeg", as_attachment=False) - + return send_file( + "img.jpg", + mimetype="image/jpeg", + attachment_filename="new.jpeg", + as_attachment=False, + ) if __name__ == "__main__": @@ -47,7 +54,6 @@ def apply_makeup(): app.run() - # # from flask import Flask, request, Response, send_file # import jsonpickle diff --git a/makeup.py b/makeup.py index bfa679a..14d5410 100644 --- a/makeup.py +++ b/makeup.py @@ -10,18 +10,18 @@ plt.switch_backend("tkAgg") SEGMENTS = { - "background":0, - "skin": 1, - "r_brow": 2, - "l_brow": 3, - "r_eye": 4, - "l_eye": 5, - "nose": 10, - "u_lip": 12, - "l_lip": 13, - "neck": 14, - "hair": 17, - "hat": 18, + "background": 0, + "skin": 1, + "r_brow": 2, + "l_brow": 3, + "r_eye": 4, + "l_eye": 5, + "nose": 10, + "u_lip": 12, + "l_lip": 13, + "neck": 14, + "hair": 17, + "hat": 18, } @@ -102,7 +102,7 @@ def change_color(image, parsed_mask, **kwargs): return image for key, color in query.items(): if not isinstance(color, tuple): - color = tuple(color.split(',')) + color = tuple(color.split(",")) b, g, r = color # Allocate mask mask = np.zeros_like(image_hsv) @@ -126,9 +126,9 @@ def change_color(image, parsed_mask, **kwargs): image = new_image changed_image = new_image.copy() - return cv2.cvtColor(changed_image, cv2.COLOR_BGR2RGB) + if __name__ == "__main__": # 1 face # 11 teeth diff --git a/requirements.txt b/requirements.txt index d1c39ba..9a95762 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,139 +1,7 @@ -anytree==2.8.0 -appdirs==1.4.3 -arrow==0.15.5 -asn1crypto==1.3.0 -astroid==2.3.3 -attrs==19.3.0 -autopep8==1.5 -backcall==0.1.0 -Beaker==1.11.0 -beautifulsoup4==4.8.2 -binaryornot==0.4.4 -black==19.10b0 -bleach==3.1.1 -btrfsutil==1.1.1 -CacheControl==0.12.6 -certifi==2019.11.28 -cffi==1.14.0 -chardet==3.0.4 -Click==7.0 -colorama==0.4.3 -contextlib2==0.6.0 -cookiecutter==1.7.0 -crcmod==1.7 -cryptography==2.8 -decorator==4.4.2 -defusedxml==0.6.0 -distlib==0.3.0 -distro==1.4.0 -entrypoints==0.3 -flake8==3.7.9 -future==0.18.2 -greenlet==0.4.15 -html2text==2019.8.11 -html5lib==1.0.1 -idna==2.9 -importlib-metadata==1.5.0 -iotop==0.6 -ipykernel==5.1.4 -ipython==7.13.0 -ipython-genutils==0.1.0 -ipywidgets==7.5.1 -isc==2.0 -isort==4.3.21 -jedi==0.15.1 -Jinja2==2.11.1 -jinja2-time==0.2.0 -json5==0.9.2 -jsonschema==3.2.0 -jupyter-client==6.0.0 -jupyter-console==6.1.0 -jupyter-core==4.6.3 -jupyterlab==2.0.1 -jupyterlab-server==1.0.7 -kawaii-player==4.2.0 -lazy-object-proxy==1.4.3 -lensfun==0.3.95 -lit==0.9.1.dev0 -louis==3.13.0 -lxml==4.5.0 -Mako==1.1.2 -mallard-ducktype==1.0.2 -Markdown==3.1.1 -MarkupSafe==1.1.1 -mccabe==0.6.1 -meson==0.53.2 -mistune==0.8.4 -more-itertools==8.2.0 -msgpack==0.6.2 -mutagen==1.44.0 -nbconvert==5.6.1 -nbformat==4.4.0 -neovim-remote==2.4.0 -notebook==6.0.3 -numpy==1.18.1 -ordered-set==3.1.1 -packaging==20.3 -pandocfilters==1.4.2 -parso==0.6.1 -pathspec==0.7.0 -pep517==0.8.1 -pexpect==4.8.0 -pickleshare==0.7.5 -Pillow==6.2.1 -pluggy==0.13.1 -ply==3.11 -powerline-status==2.7 -poyo==0.5.0 -progress==1.5 -prometheus-client==0.7.1 -prompt-toolkit==3.0.4 -psutil==5.7.0 -ptyprocess==0.6.0 -pycodestyle==2.5.0 -pycparser==2.20 -pycurl==7.43.0.5 -pydocstyle==5.0.2 -pyflakes==2.1.1 -Pygments==2.6.1 -pylint==2.4.4 -pynvim==0.4.1 -PyOpenGL==3.1.5 -pyOpenSSL==19.1.0 -pyparsing==2.4.6 -PyQt5==5.14.1 -PyQt5-sip==12.7.1 -PyQtWebEngine==5.14.0 -pyrsistent==0.15.7 -PySide2==5.14.2 -python-dateutil==2.8.1 -python-jsonrpc-server==0.3.4 -python-language-server==0.31.8 -pytoml==0.1.21 -pyzmq==19.0.0 -regex==2020.2.20 -requests==2.23.0 -retrying==1.3.3 -rope==0.16.0 -Send2Trash==1.5.0 -shiboken2==5.14.2 -six==1.14.0 -snowballstemmer==2.0.0 -soupsieve==1.9.5 -team==1.0 -terminado==0.8.3 -testpath==0.4.4 -toml==0.10.0 -tornado==6.0.4 -traitlets==4.3.3 -typed-ast==1.4.1 -ujson==1.35 -urllib3==1.25.8 -virtualenv==16.7.9 -wcwidth==0.1.8 -webencodings==0.5.1 -whichcraft==0.6.1 -widgetsnbextension==3.5.1 -wrapt==1.11.2 -yapf==0.29.0 -zipp==3.1.0 +torch +torchvision +flask +numpy +opencv-python +matplotlib +scikit-image \ No newline at end of file From f9440298da80136f4febaf28832304c7d0d962ad Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Thu, 2 Apr 2020 21:59:39 +0300 Subject: [PATCH 7/9] Running the model on CPU --- requirements.txt | 3 ++- test.py | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9a95762..ba72307 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ flask numpy opencv-python matplotlib -scikit-image \ No newline at end of file +scikit-image +jsonpickle \ No newline at end of file diff --git a/test.py b/test.py index e05aa5f..06ab5f5 100644 --- a/test.py +++ b/test.py @@ -10,6 +10,7 @@ import torchvision.transforms as transforms import cv2 +CUDA = False def vis_parsing_maps( im, parsing_anno, stride, save_im=True, save_path="output/parsing_map_on_im.jpg" @@ -79,7 +80,8 @@ def evaluate(image_path="./imgs/116.jpg", cp="cp/79999_iter.pth"): n_classes = 19 net = BiSeNet(n_classes=n_classes) - net.cuda() + if CUDA: + net.cuda() net.load_state_dict(torch.load(cp)) net.eval() @@ -91,13 +93,15 @@ def evaluate(image_path="./imgs/116.jpg", cp="cp/79999_iter.pth"): ) with torch.no_grad(): + img = Image.fromarray(image_path) # img = Image.open(image_path) image = img.resize((512, 512), Image.BILINEAR) img = to_tensor(image) img = torch.unsqueeze(img, 0) - img = img.cuda() + if CUDA: + img = img.cuda() out = net(img)[0] parsing = out.squeeze(0).cpu().numpy().argmax(0) print(parsing) From ca3b38d7fd1d2d2c0d85d9f0ee2fbbda66226e91 Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Thu, 2 Apr 2020 21:59:39 +0300 Subject: [PATCH 8/9] Running the model on CPU --- requirements.txt | 3 ++- test.py | 14 +++++++++----- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/requirements.txt b/requirements.txt index 9a95762..ba72307 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,5 @@ flask numpy opencv-python matplotlib -scikit-image \ No newline at end of file +scikit-image +jsonpickle \ No newline at end of file diff --git a/test.py b/test.py index e05aa5f..0ca479b 100644 --- a/test.py +++ b/test.py @@ -10,6 +10,7 @@ import torchvision.transforms as transforms import cv2 +CUDA = False def vis_parsing_maps( im, parsing_anno, stride, save_im=True, save_path="output/parsing_map_on_im.jpg" @@ -79,8 +80,9 @@ def evaluate(image_path="./imgs/116.jpg", cp="cp/79999_iter.pth"): n_classes = 19 net = BiSeNet(n_classes=n_classes) - net.cuda() - net.load_state_dict(torch.load(cp)) + if CUDA: + net.cuda() + net.load_state_dict(torch.load(cp, map_location=torch.device("cpu"))) net.eval() to_tensor = transforms.Compose( @@ -91,13 +93,15 @@ def evaluate(image_path="./imgs/116.jpg", cp="cp/79999_iter.pth"): ) with torch.no_grad(): - img = Image.fromarray(image_path) - # img = Image.open(image_path) + + #img = Image.fromarray(image_path) + img = Image.open(image_path) image = img.resize((512, 512), Image.BILINEAR) img = to_tensor(image) img = torch.unsqueeze(img, 0) - img = img.cuda() + if CUDA: + img = img.cuda() out = net(img)[0] parsing = out.squeeze(0).cpu().numpy().argmax(0) print(parsing) From 1760a4ec6681d6a45a3cbd1b9f19f7ac6b2c64be Mon Sep 17 00:00:00 2001 From: Abdalaziz Rashid Date: Wed, 4 Aug 2021 05:22:56 +0300 Subject: [PATCH 9/9] Update: fix hair color channel --- makeup.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/makeup.py b/makeup.py index 14d5410..a19fc7f 100644 --- a/makeup.py +++ b/makeup.py @@ -57,7 +57,7 @@ def hair(image, parsing, part=17, color=[230, 250, 250]): if part == 12 or part == 13: image_hsv[:, :, 0:2] = tar_hsv[:, :, 0:2] else: - image_hsv[:, :, 0:1] = tar_hsv[:, :, 0:1] + image_hsv[:, :, :] = tar_hsv[:, :, :] changed = cv2.cvtColor(image_hsv, cv2.COLOR_HSV2BGR) @@ -169,14 +169,13 @@ def change_color(image, parsed_mask, **kwargs): "lower_lip": 13, } - image_path = "/home/aziz/Projects/face/imgs/6.jpg" + image_path = "./imgs/6.jpg" cp = "cp/79999_iter.pth" image = cv2.imread(image_path) ori = image.copy() parsing = evaluate(image_path, cp) parsing = cv2.resize(parsing, image.shape[0:2], interpolation=cv2.INTER_NEAREST) - parts = [ table["hair"], table["lower_lip"], @@ -186,8 +185,8 @@ def change_color(image, parsed_mask, **kwargs): alpha_slider_max = 255 title_window = "Linear Blend" - change_color(image, parsing, u_lip=(255, 0, 0), l_lip=(255, 0, 0)) - for i in range(2): + #change_color(image, parsing, u_lip=(255, 0, 0), l_lip=(255, 0, 0)) + for i in range(1): image = cv2.imread(image_path) lips = np.random.randint(1, 255, (3)) @@ -195,7 +194,7 @@ def change_color(image, parsed_mask, **kwargs): colors = np.array([hair_, lips, lips]) for part, color in zip(parts, colors): - image = hair(image, parsing, part, color) + image = hair(image, parsing, part, np.array([0,0,0])) # kernel = np.ones((5, 5), np.float32) / 25 # dst = cv.filter2D(image, -1, kernel)