Main Repository

pull/125/head
alexbofa 2023-10-22 00:55:16 +03:00 committed by GitHub
parent 0c77dfc0ef
commit 07747ed844
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 2156 additions and 581 deletions

145
README.md
View File

@ -1,18 +1,58 @@
# ebsynth_utility_lite
Fork was created to facilitate the creation of videos via img2img based on the original [ebsynth_utility](https://github.com/s9roll7/ebsynth_utility)
<br>
## TODO
- [x] Delete script for img2img
- [ ] Add configuration → stage 5
- [ ] Stage 0 — changing the video size, for example from 1080x1920 to 512x904
- [ ] Stage 2 — manually add **custom_gap**
- [ ] Change Stage 3 for create a grid (min 1x1 max 3x3)
- [ ] Change Stage 4 for disassemble the grid back
- [ ] Stage 0 — add Presets (with changes via .json)
- [ ] Stage 5 — automatisation with Ebsynth? (Is it possible?)
- [ ] Edit **Readme.md**
# ebsynth_utility
#### If you want to help, feel free to create the [PR](https://github.com/alexbofa/ebsynth_utility_lite/pulls)
## Overview
#### AUTOMATIC1111 UI extension for creating videos using img2img and ebsynth.
#### This extension allows you to output edited videos using ebsynth.(AE is not required)
##### With [Controlnet](https://github.com/Mikubill/sd-webui-controlnet) installed, I have confirmed that all features of this extension are working properly!
##### [Controlnet](https://github.com/Mikubill/sd-webui-controlnet) is a must for video editing, so I recommend installing it.
##### Multi ControlNet("canny" + "normal map") would be suitable for video editing.
<br>
###### I modified animatediff-cli to create a txt2video tool that allows flexible prompt specification. You can use it if you like.
###### [animatediff-cli-prompt-travel](https://github.com/s9roll7/animatediff-cli-prompt-travel)
<div><video controls src="https://github.com/s9roll7/animatediff-cli-prompt-travel/assets/118420657/2f1de542-7084-417b-9baa-59a55fdd0e1b" muted="false"></video></div>
<br>
## Example
- The following sample is raw output of this extension.
#### sample 1 mask with [clipseg](https://github.com/timojl/clipseg)
- first from left : original
- second from left : masking "cat" exclude "finger"
- third from left : masking "cat head"
- right : color corrected with [color-matcher](https://github.com/hahnec/color-matcher) (see stage 3.5)
- Multiple targets can also be specified.(e.g. cat,dog,boy,girl)
<div><video controls src="https://user-images.githubusercontent.com/118420657/223008549-167beaee-1453-43fa-85ce-fe3982466c26.mp4" muted="false"></video></div>
#### sample 2 blend background
- person : masterpiece, best quality, masterpiece, 1girl, masterpiece, best quality,anime screencap, anime style
- background : cyberpunk, factory, room ,anime screencap, anime style
- It is also possible to blend with your favorite videos.
<div><video controls src="https://user-images.githubusercontent.com/118420657/214592811-9677634f-93bb-40dd-95b6-1c97c8e7bb63.mp4" muted="false"></video></div>
#### sample 3 auto tagging
- left : original
- center : apply the same prompts in all keyframes
- right : apply auto tagging by deepdanbooru in all keyframes
- This function improves the detailed changes in facial expressions, hand expressions, etc.
In the sample video, the "closed_eyes" and "hands_on_own_face" tags have been added to better represent eye blinks and hands brought in front of the face.
<div><video controls src="https://user-images.githubusercontent.com/118420657/218247502-6c8e04fe-859b-4739-8c9d-0bf459d04e3b.mp4" muted="false"></video></div>
#### sample 4 auto tagging (apply lora dynamically)
- left : apply auto tagging by deepdanbooru in all keyframes
- right : apply auto tagging by deepdanbooru in all keyframes + apply "anyahehface" lora dynamically
- Added the function to dynamically apply TI, hypernet, Lora, and additional prompts according to automatically attached tags.
In the sample video, if the "smile" tag is given, the lora and lora trigger keywords are set to be added according to the strength of the "smile" tag.
Also, since automatically added tags are sometimes incorrect, unnecessary tags are listed in the blacklist.
[Here](sample/) is the actual configuration file used. placed in "Project directory" for use.
<div><video controls src="https://user-images.githubusercontent.com/118420657/218247633-ab2b1e6b-d81c-4f1d-af8a-6a97df23be0e.mp4" muted="false"></video></div>
<br>
## Installation
- Install [ffmpeg](https://ffmpeg.org/) for your operating system
@ -20,9 +60,12 @@ Fork was created to facilitate the creation of videos via img2img based on the o
- Install [Ebsynth](https://ebsynth.com/)
- Use the Extensions tab of the webui to [Install from URL]
<br>
<br>
## Usage
- Go to [Ebsynth Utility] tab.
- Create an empty directory somewhere, and fill in the «Project directory» field.
- Create an empty directory somewhere, and fill in the "Project directory" field.
- Place the video you want to edit from somewhere, and fill in the "Original Movie Path" field.
Use short videos of a few seconds at first.
- Select stage 1 and Generate.
@ -32,6 +75,7 @@ Fork was created to facilitate the creation of videos via img2img based on the o
(In the current latest webui, it seems to cause an error if you do not drop the image on the main screen of img2img.
Please drop the image as it does not affect the result.)
<br>
<br>
## Note 1
@ -45,9 +89,51 @@ In the implementation of this extension, the keyframe interval is chosen to be s
If the animation breaks up, increase the keyframe, if it flickers, decrease the keyframe.
First, generate one time with the default settings and go straight ahead without worrying about the result.
#### Stage 3 (In development)
#### Stage 4 (Will be changed in the future)
#### Stage 3
Select one of the keyframes, throw it to img2img, and run [Interrogate DeepBooru].
Delete unwanted words such as blur from the displayed prompt.
Fill in the rest of the settings as you would normally do for image generation.
Here is the settings I used.
- Sampling method : Euler a
- Sampling Steps : 50
- Width : 960
- Height : 512
- CFG Scale : 20
- Denoising strength : 0.2
Here is the settings for extension.
- Mask Mode(Override img2img Mask mode) : Normal
- Img2Img Repeat Count (Loop Back) : 5
- Add N to seed when repeating : 1
- use Face Crop img2img : True
- Face Detection Method : YuNet
- Max Crop Size : 1024
- Face Denoising Strength : 0.25
- Face Area Magnification : 1.5 (The larger the number, the closer to the model's painting style, but the more likely it is to shift when merged with the body.)
- Enable Face Prompt : False
Trial and error in this process is the most time-consuming part.
Monitor the destination folder and if you do not like results, interrupt and change the settings.
[Prompt][Denoising strength] and [Face Denoising Strength] settings when using Face Crop img2img will greatly affect the result.
For more information on Face Crop img2img, check [here](https://github.com/s9roll7/face_crop_img2img)
If you have lots of memory to spare, increasing the width and height values while maintaining the aspect ratio may greatly improve results.
This extension may help with the adjustment.
https://github.com/s9roll7/img2img_for_all_method
<br>
**The information above is from a time when there was no controlnet.
When controlnet are used together (especially multi-controlnets),
Even setting "Denoising strength" to a high value works well, and even setting it to 1.0 produces meaningful results.
If "Denoising strength" is set to a high value, "Loop Back" can be set to 1.**
<br>
#### Stage 4
Scale it up or down and process it to exactly the same size as the original video.
This process should only need to be done once.
@ -74,3 +160,28 @@ In my case, the entire process from 1 to 7 took about 30 minutes.
- Crossfade blend rate : 1.0
- Export type : mp4
<br>
<br>
## Note 2 : How to use multi-controlnet together
#### in webui setting
![controlnet_setting](imgs/controlnet_setting.png "controlnet_setting")
<br>
#### In controlnet settings in img2img tab(for controlnet 0)
![controlnet_0](imgs/controlnet_0.png "controlnet_0")
<br>
#### In controlnet settings in img2img tab(for controlnet 1)
![controlnet_1](imgs/controlnet_1.png "controlnet_1")
<br>
#### In ebsynth_utility settings in img2img tab
**Warning : "Weight" in the controlnet settings is overridden by the following values**
![controlnet_option_in_ebsynthutil](imgs/controlnet_option_in_ebsynthutil.png "controlnet_option_in_ebsynthutil")
<br>
<br>
## Note 3 : How to use clipseg
![clipseg](imgs/clipseg.png "How to use clipseg")

237
calculator.py Normal file
View File

@ -0,0 +1,237 @@
# https://www.mycompiler.io/view/3TFZagC
class ParseError(Exception):
def __init__(self, pos, msg, *args):
self.pos = pos
self.msg = msg
self.args = args
def __str__(self):
return '%s at position %s' % (self.msg % self.args, self.pos)
class Parser:
def __init__(self):
self.cache = {}
def parse(self, text):
self.text = text
self.pos = -1
self.len = len(text) - 1
rv = self.start()
self.assert_end()
return rv
def assert_end(self):
if self.pos < self.len:
raise ParseError(
self.pos + 1,
'Expected end of string but got %s',
self.text[self.pos + 1]
)
def eat_whitespace(self):
while self.pos < self.len and self.text[self.pos + 1] in " \f\v\r\t\n":
self.pos += 1
def split_char_ranges(self, chars):
try:
return self.cache[chars]
except KeyError:
pass
rv = []
index = 0
length = len(chars)
while index < length:
if index + 2 < length and chars[index + 1] == '-':
if chars[index] >= chars[index + 2]:
raise ValueError('Bad character range')
rv.append(chars[index:index + 3])
index += 3
else:
rv.append(chars[index])
index += 1
self.cache[chars] = rv
return rv
def char(self, chars=None):
if self.pos >= self.len:
raise ParseError(
self.pos + 1,
'Expected %s but got end of string',
'character' if chars is None else '[%s]' % chars
)
next_char = self.text[self.pos + 1]
if chars == None:
self.pos += 1
return next_char
for char_range in self.split_char_ranges(chars):
if len(char_range) == 1:
if next_char == char_range:
self.pos += 1
return next_char
elif char_range[0] <= next_char <= char_range[2]:
self.pos += 1
return next_char
raise ParseError(
self.pos + 1,
'Expected %s but got %s',
'character' if chars is None else '[%s]' % chars,
next_char
)
def keyword(self, *keywords):
self.eat_whitespace()
if self.pos >= self.len:
raise ParseError(
self.pos + 1,
'Expected %s but got end of string',
','.join(keywords)
)
for keyword in keywords:
low = self.pos + 1
high = low + len(keyword)
if self.text[low:high] == keyword:
self.pos += len(keyword)
self.eat_whitespace()
return keyword
raise ParseError(
self.pos + 1,
'Expected %s but got %s',
','.join(keywords),
self.text[self.pos + 1],
)
def match(self, *rules):
self.eat_whitespace()
last_error_pos = -1
last_exception = None
last_error_rules = []
for rule in rules:
initial_pos = self.pos
try:
rv = getattr(self, rule)()
self.eat_whitespace()
return rv
except ParseError as e:
self.pos = initial_pos
if e.pos > last_error_pos:
last_exception = e
last_error_pos = e.pos
last_error_rules.clear()
last_error_rules.append(rule)
elif e.pos == last_error_pos:
last_error_rules.append(rule)
if len(last_error_rules) == 1:
raise last_exception
else:
raise ParseError(
last_error_pos,
'Expected %s but got %s',
','.join(last_error_rules),
self.text[last_error_pos]
)
def maybe_char(self, chars=None):
try:
return self.char(chars)
except ParseError:
return None
def maybe_match(self, *rules):
try:
return self.match(*rules)
except ParseError:
return None
def maybe_keyword(self, *keywords):
try:
return self.keyword(*keywords)
except ParseError:
return None
class CalcParser(Parser):
def start(self):
return self.expression()
def expression(self):
rv = self.match('term')
while True:
op = self.maybe_keyword('+', '-')
if op is None:
break
term = self.match('term')
if op == '+':
rv += term
else:
rv -= term
return rv
def term(self):
rv = self.match('factor')
while True:
op = self.maybe_keyword('*', '/')
if op is None:
break
term = self.match('factor')
if op == '*':
rv *= term
else:
rv /= term
return rv
def factor(self):
if self.maybe_keyword('('):
rv = self.match('expression')
self.keyword(')')
return rv
return self.match('number')
def number(self):
chars = []
sign = self.maybe_keyword('+', '-')
if sign is not None:
chars.append(sign)
chars.append(self.char('0-9'))
while True:
char = self.maybe_char('0-9')
if char is None:
break
chars.append(char)
if self.maybe_char('.'):
chars.append('.')
chars.append(self.char('0-9'))
while True:
char = self.maybe_char('0-9')
if char is None:
break
chars.append(char)
rv = float(''.join(chars))
return rv

View File

@ -6,11 +6,12 @@ import cv2
import glob
from PIL import Image
from extensions.ebsynth_utility_lite.stage1 import ebsynth_utility_stage1,ebsynth_utility_stage1_invert
from extensions.ebsynth_utility_lite.stage2 import ebsynth_utility_stage2
from extensions.ebsynth_utility_lite.stage5 import ebsynth_utility_stage5
from extensions.ebsynth_utility_lite.stage7 import ebsynth_utility_stage7
from extensions.ebsynth_utility_lite.stage8 import ebsynth_utility_stage8
from extensions.ebsynth_utility.stage1 import ebsynth_utility_stage1,ebsynth_utility_stage1_invert
from extensions.ebsynth_utility.stage2 import ebsynth_utility_stage2
from extensions.ebsynth_utility.stage5 import ebsynth_utility_stage5
from extensions.ebsynth_utility.stage7 import ebsynth_utility_stage7
from extensions.ebsynth_utility.stage8 import ebsynth_utility_stage8
from extensions.ebsynth_utility.stage3_5 import ebsynth_utility_stage3_5
def x_ceiling(value, step):
@ -29,7 +30,7 @@ class debug_string:
def to_string(self):
return self.txt
def ebsynth_utility_process(stage_index: int, project_dir:str, original_movie_path:str, frame_width:int, frame_height:int, st1_masking_method_index:int, st1_mask_threshold:float, tb_use_fast_mode:bool, tb_use_jit:bool, clipseg_mask_prompt:str, clipseg_exclude_prompt:str, clipseg_mask_threshold:int, clipseg_mask_blur_size:int, clipseg_mask_blur_size2:int, key_min_gap:int, key_max_gap:int, key_th:float, key_add_last_frame:bool, blend_rate:float, export_type:str, bg_src:str, bg_type:str, mask_blur_size:int, mask_threshold:float, fg_transparency:float, mask_mode:str):
def ebsynth_utility_process(stage_index: int, project_dir:str, original_movie_path:str, frame_width:int, frame_height:int, st1_masking_method_index:int, st1_mask_threshold:float, tb_use_fast_mode:bool, tb_use_jit:bool, clipseg_mask_prompt:str, clipseg_exclude_prompt:str, clipseg_mask_threshold:int, clipseg_mask_blur_size:int, clipseg_mask_blur_size2:int, key_min_gap:int, key_max_gap:int, key_th:float, key_add_last_frame:bool, color_matcher_method:str, st3_5_use_mask:bool, st3_5_use_mask_ref:bool, st3_5_use_mask_org:bool, color_matcher_ref_type:int, color_matcher_ref_image:Image, blend_rate:float, export_type:str, bg_src:str, bg_type:str, mask_blur_size:int, mask_threshold:float, fg_transparency:float, mask_mode:str):
args = locals()
info = ""
info = dump_dict(info, args)
@ -97,17 +98,25 @@ def ebsynth_utility_process(stage_index: int, project_dir:str, original_movie_pa
dbg.print("stage 3")
dbg.print("")
dbg.print("This is an information button, it does not do anything")
dbg.print("")
dbg.print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
dbg.print("1. Go to img2img tab")
dbg.print("2. Generate")
dbg.print("(Images are output to " + img2img_key_path + ")")
dbg.print("")
dbg.print("This tab will be changed to create a GRID (min 1x1 — max 3x3)")
dbg.print("")
dbg.print("If you know how to do it and want to help, create the PR")
dbg.print("2. Select [ebsynth utility] in the script combo box")
dbg.print("3. Fill in the \"Project directory\" field with [" + project_dir + "]" )
dbg.print("4. Select in the \"Mask Mode(Override img2img Mask mode)\" field with [" + ("Invert" if is_invert_mask else "Normal") + "]" )
dbg.print("5. I recommend to fill in the \"Width\" field with [" + str(img_width) + "]" )
dbg.print("6. I recommend to fill in the \"Height\" field with [" + str(img_height) + "]" )
dbg.print("7. I recommend to fill in the \"Denoising strength\" field with lower than 0.35" )
dbg.print(" (When using controlnet together, you can put in large values (even 1.0 is possible).)")
dbg.print("8. Fill in the remaining configuration fields of img2img. No image and mask settings are required.")
dbg.print("9. Drop any image onto the img2img main screen. This is necessary to avoid errors, but does not affect the results of img2img.")
dbg.print("10. Generate")
dbg.print("(Images are output to [" + img2img_key_path + "])")
dbg.print("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!")
return process_end( dbg, "" )
elif stage_index == 3:
ebsynth_utility_stage3_5(dbg, project_args, color_matcher_method, st3_5_use_mask, st3_5_use_mask_ref, st3_5_use_mask_org, color_matcher_ref_type, color_matcher_ref_image)
elif stage_index == 4:
sample_image = glob.glob( os.path.join(frame_path , "*.png" ) )[0]
img_height, img_width, _ = cv2.imread(sample_image).shape

View File

@ -7,22 +7,25 @@ def update_transparent_background():
v = meta_version("transparent-background")
print("current transparent-background " + v)
if version.parse(v) < version.parse('1.2.3'):
launch.run_pip("install -U transparent-background", "update transparent-background version for Ebsynth Utility Lite")
launch.run_pip("install -U transparent-background", "update transparent-background version for Ebsynth Utility")
# Check if user is running an M1/M2 device and, if so, install pyvirtualcam, which is required for updating the transparent_background package
# Note that we have to directly install from source because the prebuilt PyPl wheel does not support ARM64 machines such as M1/M2 Macs
if platform.system() == "Darwin" and platform.machine() == "arm64":
if not launch.is_installed("pyvirtualcam"):
launch.run_pip("install git+https://github.com/letmaik/pyvirtualcam", "requirements for Ebsynth Utility Lite")
launch.run_pip("install git+https://github.com/letmaik/pyvirtualcam", "requirements for Ebsynth Utility")
if not launch.is_installed("transparent_background"):
launch.run_pip("install transparent-background", "requirements for Ebsynth Utility Lite")
launch.run_pip("install transparent-background", "requirements for Ebsynth Utility")
update_transparent_background()
if not launch.is_installed("IPython"):
launch.run_pip("install ipython", "requirements for Ebsynth Utility Lite")
launch.run_pip("install ipython", "requirements for Ebsynth Utility")
if not launch.is_installed("seaborn"):
launch.run_pip("install ""seaborn>=0.11.0""", "requirements for Ebsynth Utility Lite")
launch.run_pip("install ""seaborn>=0.11.0""", "requirements for Ebsynth Utility")
if not launch.is_installed("color_matcher"):
launch.run_pip("install color-matcher", "requirements for Ebsynth Utility")

1012
scripts/custom_script.py Normal file

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ from modules.call_queue import wrap_gradio_gpu_call
def on_ui_tabs():
with gr.Blocks(analytics_enabled=False) as ebs_interface:
with gr.Row().style(equal_height=False):
with gr.Row(equal_height=False):
with gr.Column(variant='panel'):
with gr.Row():
@ -22,7 +22,7 @@ def on_ui_tabs():
return video
org_video.upload(fn_upload_org_video, org_video, original_movie_path)
gr.HTML(value="<p style='margin-bottom: 1.2em'>\
If you have trouble entering the video path manually, you can also use drag and drop. \
If you have trouble entering the video path manually, you can also use drag and drop.For large videos, please enter the path manually. \
</p>")
with gr.TabItem('configuration', elem_id='ebs_configuration'):
@ -61,6 +61,22 @@ def on_ui_tabs():
key_th = gr.Slider(minimum=0.0, maximum=100.0, step=0.1, label='Threshold of delta frame edge', value=8.5)
key_add_last_frame = gr.Checkbox(label="Add last frame to keyframes", value=True)
with gr.TabItem(label="stage 3.5", elem_id='ebs_configuration_tab3_5'):
gr.HTML(value="<p style='margin-bottom: 0.7em'>\
<font color=\"blue\"><a href=\"https://github.com/hahnec/color-matcher\">[color-matcher]</a></font>\
</p>")
color_matcher_method = gr.Radio(label='Color Transfer Method', choices=['default', 'hm', 'reinhard', 'mvgd', 'mkl', 'hm-mvgd-hm', 'hm-mkl-hm'], value="hm-mkl-hm", type="value")
color_matcher_ref_type = gr.Radio(label='Color Matcher Ref Image Type', choices=['original video frame', 'first frame of img2img result'], value="original video frame", type="index")
gr.HTML(value="<p style='margin-bottom: 0.7em'>\
<font color=\"red\">If an image is specified below, it will be used with highest priority.</font>\
</p>")
color_matcher_ref_image = gr.Image(label="Color Matcher Ref Image", source='upload', mirror_webcam=False, type='pil')
st3_5_use_mask = gr.Checkbox(label="Apply mask to the result", value=True)
st3_5_use_mask_ref = gr.Checkbox(label="Apply mask to the Ref Image", value=False)
st3_5_use_mask_org = gr.Checkbox(label="Apply mask to original image", value=False)
#st3_5_number_of_itr = gr.Slider(minimum=1, maximum=10, step=1, label='Number of iterations', value=1)
with gr.TabItem(label="stage 7", elem_id='ebs_configuration_tab7'):
blend_rate = gr.Slider(minimum=0.0, maximum=1.0, step=0.01, label='Crossfade blend rate', value=1.0)
export_type = gr.Dropdown(choices=["mp4","webm","gif","rawvideo"], value="mp4" ,label="Export type")
@ -86,6 +102,8 @@ def on_ui_tabs():
Select keyframes to be given to ebsynth.<br><br>\
<b>stage 3</b> <br>\
img2img keyframes.<br><br>\
<b>stage 3.5</b> <br>\
(this is optional. Perform color correction on the img2img results and expect flickering to decrease. Or, you can simply change the color tone from the generated result.)<br><br>\
<b>stage 4</b> <br>\
and upscale to the size of the original video.<br><br>\
<b>stage 5</b> <br>\
@ -110,7 +128,7 @@ def on_ui_tabs():
with gr.Column(variant='panel'):
with gr.Column(scale=1):
with gr.Row():
stage_index = gr.Radio(label='Process Stage', choices=["stage 1","stage 2","stage 3","stage 4","stage 5","stage 6","stage 7","stage 8"], value="stage 1", type="index", elem_id='ebs_stages')
stage_index = gr.Radio(label='Process Stage', choices=["stage 1","stage 2","stage 3","stage 3.5","stage 4","stage 5","stage 6","stage 7","stage 8"], value="stage 1", type="index", elem_id='ebs_stages')
with gr.Row():
generate_btn = gr.Button('Generate', elem_id="ebs_generate_btn", variant='primary')
@ -146,6 +164,13 @@ def on_ui_tabs():
key_th,
key_add_last_frame,
color_matcher_method,
st3_5_use_mask,
st3_5_use_mask_ref,
st3_5_use_mask_org,
color_matcher_ref_type,
color_matcher_ref_image,
blend_rate,
export_type,
@ -166,6 +191,6 @@ def on_ui_tabs():
)
generate_btn.click(**ebs_args)
return (ebs_interface, "Ebsynth Utility Lite", "ebs_interface"),
return (ebs_interface, "Ebsynth Utility", "ebs_interface"),
script_callbacks.on_ui_tabs(on_ui_tabs)

178
stage3_5.py Normal file
View File

@ -0,0 +1,178 @@
import cv2
import os
import glob
import shutil
import numpy as np
from PIL import Image
from color_matcher import ColorMatcher
from color_matcher.normalizer import Normalizer
def resize_img(img, w, h):
if img.shape[0] + img.shape[1] < h + w:
interpolation = interpolation=cv2.INTER_CUBIC
else:
interpolation = interpolation=cv2.INTER_AREA
return cv2.resize(img, (w, h), interpolation=interpolation)
def get_pair_of_img(img_path, target_dir):
img_basename = os.path.basename(img_path)
target_path = os.path.join( target_dir , img_basename )
return target_path if os.path.isfile( target_path ) else None
def remove_pngs_in_dir(path):
if not os.path.isdir(path):
return
pngs = glob.glob( os.path.join(path, "*.png") )
for png in pngs:
os.remove(png)
def get_pair_of_img(img, target_dir):
img_basename = os.path.basename(img)
pair_path = os.path.join( target_dir , img_basename )
if os.path.isfile( pair_path ):
return pair_path
print("!!! pair of "+ img + " not in " + target_dir)
return ""
def get_mask_array(mask_path):
if not mask_path:
return None
mask_array = np.asarray(Image.open( mask_path ))
if mask_array.ndim == 2:
mask_array = mask_array[:, :, np.newaxis]
mask_array = mask_array[:,:,:1]
mask_array = mask_array/255
return mask_array
def color_match(imgs, ref_image, color_matcher_method, dst_path):
cm = ColorMatcher(method=color_matcher_method)
i = 0
total = len(imgs)
for fname in imgs:
img_src = Image.open(fname)
img_src = Normalizer(np.asarray(img_src)).type_norm()
img_src = cm.transfer(src=img_src, ref=ref_image, method=color_matcher_method)
img_src = Normalizer(img_src).uint8_norm()
Image.fromarray(img_src).save(os.path.join(dst_path, os.path.basename(fname)))
i += 1
print("{0}/{1}".format(i, total))
imgs = sorted( glob.glob( os.path.join(dst_path, "*.png") ) )
def ebsynth_utility_stage3_5(dbg, project_args, color_matcher_method, st3_5_use_mask, st3_5_use_mask_ref, st3_5_use_mask_org, color_matcher_ref_type, color_matcher_ref_image):
dbg.print("stage3.5")
dbg.print("")
_, _, frame_path, frame_mask_path, org_key_path, img2img_key_path, _ = project_args
backup_path = os.path.join( os.path.join( img2img_key_path, "..") , "st3_5_backup_img2img_key")
backup_path = os.path.normpath(backup_path)
if not os.path.isdir( backup_path ):
dbg.print("{0} not found -> create backup.".format(backup_path))
os.makedirs(backup_path, exist_ok=True)
imgs = glob.glob( os.path.join(img2img_key_path, "*.png") )
for img in imgs:
img_basename = os.path.basename(img)
pair_path = os.path.join( backup_path , img_basename )
shutil.copy( img , pair_path)
else:
dbg.print("{0} found -> Treat the images here as originals.".format(backup_path))
org_imgs = sorted( glob.glob( os.path.join(backup_path, "*.png") ) )
head_of_keyframe = org_imgs[0]
# open ref img
ref_image = color_matcher_ref_image
if not ref_image:
dbg.print("color_matcher_ref_image not set")
if color_matcher_ref_type == 0:
#'original video frame'
dbg.print("select -> original video frame")
ref_image = Image.open( get_pair_of_img(head_of_keyframe, frame_path) )
else:
#'first frame of img2img result'
dbg.print("select -> first frame of img2img result")
ref_image = Image.open( get_pair_of_img(head_of_keyframe, backup_path) )
ref_image = np.asarray(ref_image)
if st3_5_use_mask_ref:
mask = get_pair_of_img(head_of_keyframe, frame_mask_path)
if mask:
mask_array = get_mask_array( mask )
ref_image = ref_image * mask_array
ref_image = ref_image.astype(np.uint8)
else:
dbg.print("select -> color_matcher_ref_image")
ref_image = np.asarray(ref_image)
if color_matcher_method in ('mvgd', 'hm-mvgd-hm'):
sample_img = Image.open(head_of_keyframe)
ref_image = resize_img( ref_image, sample_img.width, sample_img.height )
ref_image = Normalizer(ref_image).type_norm()
if st3_5_use_mask_org:
tmp_path = os.path.join( os.path.join( img2img_key_path, "..") , "st3_5_tmp")
tmp_path = os.path.normpath(tmp_path)
dbg.print("create {0} for masked original image".format(tmp_path))
remove_pngs_in_dir(tmp_path)
os.makedirs(tmp_path, exist_ok=True)
for org_img in org_imgs:
image_basename = os.path.basename(org_img)
org_image = np.asarray(Image.open(org_img))
mask = get_pair_of_img(org_img, frame_mask_path)
if mask:
mask_array = get_mask_array( mask )
org_image = org_image * mask_array
org_image = org_image.astype(np.uint8)
Image.fromarray(org_image).save( os.path.join( tmp_path, image_basename ) )
org_imgs = sorted( glob.glob( os.path.join(tmp_path, "*.png") ) )
color_match(org_imgs, ref_image, color_matcher_method, img2img_key_path)
if st3_5_use_mask or st3_5_use_mask_org:
imgs = sorted( glob.glob( os.path.join(img2img_key_path, "*.png") ) )
for img in imgs:
mask = get_pair_of_img(img, frame_mask_path)
if mask:
mask_array = get_mask_array( mask )
bg = get_pair_of_img(img, frame_path)
bg_image = np.asarray(Image.open( bg ))
fg_image = np.asarray(Image.open( img ))
final_img = fg_image * mask_array + bg_image * (1-mask_array)
final_img = final_img.astype(np.uint8)
Image.fromarray(final_img).save(img)
dbg.print("")
dbg.print("completed.")

View File

@ -8,7 +8,7 @@ from sys import byteorder
import binascii
import numpy as np
SYNTHS_PER_PROJECT = 25
SYNTHS_PER_PROJECT = 15
def to_float_bytes(f):
if byteorder == 'little':

View File

@ -7,7 +7,7 @@ import time
import cv2
import numpy as np
import itertools
from extensions.ebsynth_utility_lite.stage7 import create_movie_from_frames, get_ext, trying_to_add_audio
from extensions.ebsynth_utility.stage7 import create_movie_from_frames, get_ext, trying_to_add_audio
def clamp(n, smallest, largest):
return sorted([smallest, n, largest])[1]

View File

@ -1,5 +1,5 @@
#ebs_info_area {
border: #374151 2px solid;
border: #0B0F19 2px solid;
border-radius: 5px;
font-size: 15px;
margin: 10px;