feat: add fill color control to inpaint
This commit is contained in:
parent
08dbc0c738
commit
3679735d86
|
@ -14,7 +14,7 @@ def get_pixel_index(x: int, y: int, width: int) -> int:
|
||||||
return (y * width) + x
|
return (y * width) + x
|
||||||
|
|
||||||
|
|
||||||
def mask_filter_none(mask_image: Image, dims: Point, origin: Point, fill='white') -> Image:
|
def mask_filter_none(mask_image: Image, dims: Point, origin: Point, fill='white', **kw) -> Image:
|
||||||
width, height = dims
|
width, height = dims
|
||||||
|
|
||||||
noise = Image.new('RGB', (width, height), fill)
|
noise = Image.new('RGB', (width, height), fill)
|
||||||
|
@ -23,7 +23,7 @@ def mask_filter_none(mask_image: Image, dims: Point, origin: Point, fill='white'
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def mask_filter_gaussian_multiply(mask_image: Image, dims: Point, origin: Point, rounds=3) -> Image:
|
def mask_filter_gaussian_multiply(mask_image: Image, dims: Point, origin: Point, rounds=3, **kw) -> Image:
|
||||||
'''
|
'''
|
||||||
Gaussian blur with multiply, source image centered on white canvas.
|
Gaussian blur with multiply, source image centered on white canvas.
|
||||||
'''
|
'''
|
||||||
|
@ -36,7 +36,7 @@ def mask_filter_gaussian_multiply(mask_image: Image, dims: Point, origin: Point,
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def mask_filter_gaussian_screen(mask_image: Image, dims: Point, origin: Point, rounds=3) -> Image:
|
def mask_filter_gaussian_screen(mask_image: Image, dims: Point, origin: Point, rounds=3, **kw) -> Image:
|
||||||
'''
|
'''
|
||||||
Gaussian blur, source image centered on white canvas.
|
Gaussian blur, source image centered on white canvas.
|
||||||
'''
|
'''
|
||||||
|
@ -49,7 +49,7 @@ def mask_filter_gaussian_screen(mask_image: Image, dims: Point, origin: Point, r
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def noise_source_fill_edge(source_image: Image, dims: Point, origin: Point, fill='white') -> Image:
|
def noise_source_fill_edge(source_image: Image, dims: Point, origin: Point, fill='white', **kw) -> Image:
|
||||||
'''
|
'''
|
||||||
Identity transform, source image centered on white canvas.
|
Identity transform, source image centered on white canvas.
|
||||||
'''
|
'''
|
||||||
|
@ -61,7 +61,7 @@ def noise_source_fill_edge(source_image: Image, dims: Point, origin: Point, fill
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def noise_source_fill_mask(source_image: Image, dims: Point, origin: Point, fill='white') -> Image:
|
def noise_source_fill_mask(source_image: Image, dims: Point, origin: Point, fill='white', **kw) -> Image:
|
||||||
'''
|
'''
|
||||||
Fill the whole canvas, no source or noise.
|
Fill the whole canvas, no source or noise.
|
||||||
'''
|
'''
|
||||||
|
@ -72,7 +72,7 @@ def noise_source_fill_mask(source_image: Image, dims: Point, origin: Point, fill
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def noise_source_gaussian(source_image: Image, dims: Point, origin: Point, rounds=3) -> Image:
|
def noise_source_gaussian(source_image: Image, dims: Point, origin: Point, rounds=3, **kw) -> Image:
|
||||||
'''
|
'''
|
||||||
Gaussian blur, source image centered on white canvas.
|
Gaussian blur, source image centered on white canvas.
|
||||||
'''
|
'''
|
||||||
|
@ -85,7 +85,7 @@ def noise_source_gaussian(source_image: Image, dims: Point, origin: Point, round
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def noise_source_uniform(source_image: Image, dims: Point, origin: Point) -> Image:
|
def noise_source_uniform(source_image: Image, dims: Point, origin: Point, **kw) -> Image:
|
||||||
width, height = dims
|
width, height = dims
|
||||||
size = width * height
|
size = width * height
|
||||||
|
|
||||||
|
@ -107,7 +107,7 @@ def noise_source_uniform(source_image: Image, dims: Point, origin: Point) -> Ima
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def noise_source_normal(source_image: Image, dims: Point, origin: Point) -> Image:
|
def noise_source_normal(source_image: Image, dims: Point, origin: Point, **kw) -> Image:
|
||||||
width, height = dims
|
width, height = dims
|
||||||
size = width * height
|
size = width * height
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ def noise_source_normal(source_image: Image, dims: Point, origin: Point) -> Imag
|
||||||
return noise
|
return noise
|
||||||
|
|
||||||
|
|
||||||
def noise_source_histogram(source_image: Image, dims: Point, origin: Point) -> Image:
|
def noise_source_histogram(source_image: Image, dims: Point, origin: Point, **kw) -> Image:
|
||||||
r, g, b = source_image.split()
|
r, g, b = source_image.split()
|
||||||
width, height = dims
|
width, height = dims
|
||||||
size = width * height
|
size = width * height
|
||||||
|
@ -177,8 +177,8 @@ def expand_image(
|
||||||
full_source = Image.new('RGB', dims, fill)
|
full_source = Image.new('RGB', dims, fill)
|
||||||
full_source.paste(source_image, origin)
|
full_source.paste(source_image, origin)
|
||||||
|
|
||||||
full_mask = mask_filter(mask_image, dims, origin)
|
full_mask = mask_filter(mask_image, dims, origin, fill=fill)
|
||||||
full_noise = noise_source(source_image, dims, origin)
|
full_noise = noise_source(source_image, dims, origin, fill=fill)
|
||||||
full_noise = ImageChops.multiply(full_noise, full_mask)
|
full_noise = ImageChops.multiply(full_noise, full_mask)
|
||||||
|
|
||||||
full_source = Image.composite(full_noise, full_source, full_mask.convert('L'))
|
full_source = Image.composite(full_noise, full_source, full_mask.convert('L'))
|
||||||
|
|
|
@ -150,6 +150,7 @@ def run_inpaint_pipeline(
|
||||||
noise_source: Any,
|
noise_source: Any,
|
||||||
mask_filter: Any,
|
mask_filter: Any,
|
||||||
strength: float,
|
strength: float,
|
||||||
|
fill_color: str,
|
||||||
):
|
):
|
||||||
pipe = load_pipeline(OnnxStableDiffusionInpaintPipeline,
|
pipe = load_pipeline(OnnxStableDiffusionInpaintPipeline,
|
||||||
params.model, params.provider, params.scheduler)
|
params.model, params.provider, params.scheduler)
|
||||||
|
@ -162,6 +163,7 @@ def run_inpaint_pipeline(
|
||||||
source_image,
|
source_image,
|
||||||
mask_image,
|
mask_image,
|
||||||
expand,
|
expand,
|
||||||
|
fill=fill_color,
|
||||||
noise_source=noise_source,
|
noise_source=noise_source,
|
||||||
mask_filter=mask_filter)
|
mask_filter=mask_filter)
|
||||||
|
|
||||||
|
|
|
@ -390,6 +390,7 @@ def inpaint():
|
||||||
expand = border_from_request()
|
expand = border_from_request()
|
||||||
upscale = upscale_from_request()
|
upscale = upscale_from_request()
|
||||||
|
|
||||||
|
fill_color = request.args.get('fillColor', 'white')
|
||||||
mask_filter = get_from_map(request.args, 'filter', mask_filters, 'none')
|
mask_filter = get_from_map(request.args, 'filter', mask_filters, 'none')
|
||||||
noise_source = get_from_map(
|
noise_source = get_from_map(
|
||||||
request.args, 'noise', noise_sources, 'histogram')
|
request.args, 'noise', noise_sources, 'histogram')
|
||||||
|
@ -411,6 +412,7 @@ def inpaint():
|
||||||
mask_filter.__name__,
|
mask_filter.__name__,
|
||||||
noise_source.__name__,
|
noise_source.__name__,
|
||||||
strength,
|
strength,
|
||||||
|
fill_color,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
print("inpaint output: %s" % output)
|
print("inpaint output: %s" % output)
|
||||||
|
@ -430,7 +432,8 @@ def inpaint():
|
||||||
expand,
|
expand,
|
||||||
noise_source,
|
noise_source,
|
||||||
mask_filter,
|
mask_filter,
|
||||||
strength)
|
strength,
|
||||||
|
fill_color)
|
||||||
|
|
||||||
return jsonify({
|
return jsonify({
|
||||||
'output': output,
|
'output': output,
|
||||||
|
|
|
@ -48,6 +48,7 @@ export interface InpaintParams extends BaseImgParams {
|
||||||
filter: string;
|
filter: string;
|
||||||
noise: string;
|
noise: string;
|
||||||
strength: number;
|
strength: number;
|
||||||
|
fillColor: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface OutpaintPixels {
|
export interface OutpaintPixels {
|
||||||
|
|
|
@ -44,7 +44,6 @@ export function ImageInput(props: ImageInputProps) {
|
||||||
const { files } = event.target;
|
const { files } = event.target;
|
||||||
if (doesExist(files) && files.length > 0) {
|
if (doesExist(files) && files.length > 0) {
|
||||||
const file = mustExist(files[0]);
|
const file = mustExist(files[0]);
|
||||||
|
|
||||||
props.onChange(file);
|
props.onChange(file);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -53,6 +53,7 @@ export function Inpaint() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const state = mustExist(useContext(StateContext));
|
const state = mustExist(useContext(StateContext));
|
||||||
|
const fillColor = useStore(state, (s) => s.inpaint.fillColor);
|
||||||
const filter = useStore(state, (s) => s.inpaint.filter);
|
const filter = useStore(state, (s) => s.inpaint.filter);
|
||||||
const noise = useStore(state, (s) => s.inpaint.noise);
|
const noise = useStore(state, (s) => s.inpaint.noise);
|
||||||
const mask = useStore(state, (s) => s.inpaint.mask);
|
const mask = useStore(state, (s) => s.inpaint.mask);
|
||||||
|
@ -109,19 +110,6 @@ export function Inpaint() {
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Stack direction='row' spacing={2}>
|
<Stack direction='row' spacing={2}>
|
||||||
<NumericField
|
|
||||||
label='Strength'
|
|
||||||
min={params.strength.min}
|
|
||||||
max={params.strength.max}
|
|
||||||
step={params.strength.step}
|
|
||||||
value={strength}
|
|
||||||
onChange={(value) => {
|
|
||||||
setInpaint({
|
|
||||||
strength: value,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
{/* TODO: numeric input for blend strength */}
|
|
||||||
<QueryList
|
<QueryList
|
||||||
id='masks'
|
id='masks'
|
||||||
labels={MASK_LABELS}
|
labels={MASK_LABELS}
|
||||||
|
@ -150,6 +138,26 @@ export function Inpaint() {
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
<NumericField
|
||||||
|
label='Strength'
|
||||||
|
min={params.strength.min}
|
||||||
|
max={params.strength.max}
|
||||||
|
step={params.strength.step}
|
||||||
|
value={strength}
|
||||||
|
onChange={(value) => {
|
||||||
|
setInpaint({
|
||||||
|
strength: value,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Stack direction='row' spacing={2}>
|
||||||
|
<input name='fill-color' type='color' value={fillColor} onChange={(event) => {
|
||||||
|
setInpaint({
|
||||||
|
fillColor: event.target.value,
|
||||||
|
});
|
||||||
|
}} />
|
||||||
|
<label htmlFor='fill-color'>Fill Color</label>
|
||||||
|
</Stack>
|
||||||
</Stack>
|
</Stack>
|
||||||
<OutpaintControl />
|
<OutpaintControl />
|
||||||
<UpscaleControl />
|
<UpscaleControl />
|
||||||
|
|
|
@ -153,6 +153,7 @@ export function createStateSlices(base: ServerParams) {
|
||||||
const createInpaintSlice: StateCreator<OnnxState, [], [], InpaintSlice> = (set) => ({
|
const createInpaintSlice: StateCreator<OnnxState, [], [], InpaintSlice> = (set) => ({
|
||||||
inpaint: {
|
inpaint: {
|
||||||
...defaults,
|
...defaults,
|
||||||
|
fillColor: '',
|
||||||
filter: 'none',
|
filter: 'none',
|
||||||
mask: null,
|
mask: null,
|
||||||
noise: 'histogram',
|
noise: 'histogram',
|
||||||
|
@ -171,6 +172,7 @@ export function createStateSlices(base: ServerParams) {
|
||||||
set({
|
set({
|
||||||
inpaint: {
|
inpaint: {
|
||||||
...defaults,
|
...defaults,
|
||||||
|
fillColor: '',
|
||||||
filter: 'none',
|
filter: 'none',
|
||||||
mask: null,
|
mask: null,
|
||||||
noise: 'histogram',
|
noise: 'histogram',
|
||||||
|
|
Loading…
Reference in New Issue