mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
experimental: Add CV2 Infill
This commit is contained in:
parent
7a295cbfd5
commit
1a9f552a75
@ -1,19 +1,19 @@
|
|||||||
import typing
|
import typing
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
from fastapi import Body
|
from fastapi import Body
|
||||||
from fastapi.routing import APIRouter
|
from fastapi.routing import APIRouter
|
||||||
from pathlib import Path
|
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
|
|
||||||
|
from invokeai.app.invocations.upscale import ESRGAN_MODELS
|
||||||
|
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
||||||
from invokeai.backend.image_util.patchmatch import PatchMatch
|
from invokeai.backend.image_util.patchmatch import PatchMatch
|
||||||
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
from invokeai.backend.image_util.safety_checker import SafetyChecker
|
||||||
from invokeai.backend.image_util.invisible_watermark import InvisibleWatermark
|
from invokeai.backend.util.logging import logging
|
||||||
from invokeai.app.invocations.upscale import ESRGAN_MODELS
|
|
||||||
|
|
||||||
from invokeai.version import __version__
|
from invokeai.version import __version__
|
||||||
|
|
||||||
from ..dependencies import ApiDependencies
|
from ..dependencies import ApiDependencies
|
||||||
from invokeai.backend.util.logging import logging
|
|
||||||
|
|
||||||
|
|
||||||
class LogLevel(int, Enum):
|
class LogLevel(int, Enum):
|
||||||
@ -55,7 +55,7 @@ async def get_version() -> AppVersion:
|
|||||||
|
|
||||||
@app_router.get("/config", operation_id="get_config", status_code=200, response_model=AppConfig)
|
@app_router.get("/config", operation_id="get_config", status_code=200, response_model=AppConfig)
|
||||||
async def get_config() -> AppConfig:
|
async def get_config() -> AppConfig:
|
||||||
infill_methods = ["tile", "lama"]
|
infill_methods = ["tile", "lama", "cv2"]
|
||||||
if PatchMatch.patchmatch_available():
|
if PatchMatch.patchmatch_available():
|
||||||
infill_methods.append("patchmatch")
|
infill_methods.append("patchmatch")
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ from PIL import Image, ImageOps
|
|||||||
|
|
||||||
from invokeai.app.invocations.primitives import ColorField, ImageField, ImageOutput
|
from invokeai.app.invocations.primitives import ColorField, ImageField, ImageOutput
|
||||||
from invokeai.app.util.misc import SEED_MAX, get_random_seed
|
from invokeai.app.util.misc import SEED_MAX, get_random_seed
|
||||||
|
from invokeai.backend.image_util.cv2_inpaint import cv2_inpaint
|
||||||
from invokeai.backend.image_util.lama import LaMA
|
from invokeai.backend.image_util.lama import LaMA
|
||||||
from invokeai.backend.image_util.patchmatch import PatchMatch
|
from invokeai.backend.image_util.patchmatch import PatchMatch
|
||||||
|
|
||||||
@ -16,11 +17,7 @@ from .baseinvocation import BaseInvocation, InputField, InvocationContext, invoc
|
|||||||
|
|
||||||
|
|
||||||
def infill_methods() -> list[str]:
|
def infill_methods() -> list[str]:
|
||||||
methods = [
|
methods = ["tile", "solid", "lama", "cv2"]
|
||||||
"tile",
|
|
||||||
"solid",
|
|
||||||
"lama",
|
|
||||||
]
|
|
||||||
if PatchMatch.patchmatch_available():
|
if PatchMatch.patchmatch_available():
|
||||||
methods.insert(0, "patchmatch")
|
methods.insert(0, "patchmatch")
|
||||||
return methods
|
return methods
|
||||||
@ -49,6 +46,10 @@ def infill_patchmatch(im: Image.Image) -> Image.Image:
|
|||||||
return im_patched
|
return im_patched
|
||||||
|
|
||||||
|
|
||||||
|
def infill_cv2(im: Image.Image) -> Image.Image:
|
||||||
|
return cv2_inpaint(im)
|
||||||
|
|
||||||
|
|
||||||
def get_tile_images(image: np.ndarray, width=8, height=8):
|
def get_tile_images(image: np.ndarray, width=8, height=8):
|
||||||
_nrows, _ncols, depth = image.shape
|
_nrows, _ncols, depth = image.shape
|
||||||
_strides = image.strides
|
_strides = image.strides
|
||||||
@ -243,3 +244,30 @@ class LaMaInfillInvocation(BaseInvocation):
|
|||||||
width=image_dto.width,
|
width=image_dto.width,
|
||||||
height=image_dto.height,
|
height=image_dto.height,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@invocation("infill_cv2", title="CV2 Infill", tags=["image", "inpaint"], category="inpaint")
|
||||||
|
class CV2InfillInvocation(BaseInvocation):
|
||||||
|
"""Infills transparent areas of an image using OpenCV Inpainting"""
|
||||||
|
|
||||||
|
image: ImageField = InputField(description="The image to infill")
|
||||||
|
|
||||||
|
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||||
|
image = context.services.images.get_pil_image(self.image.image_name)
|
||||||
|
|
||||||
|
infilled = infill_cv2(image.copy())
|
||||||
|
|
||||||
|
image_dto = context.services.images.create(
|
||||||
|
image=infilled,
|
||||||
|
image_origin=ResourceOrigin.INTERNAL,
|
||||||
|
image_category=ImageCategory.GENERAL,
|
||||||
|
node_id=self.id,
|
||||||
|
session_id=context.graph_execution_state_id,
|
||||||
|
is_intermediate=self.is_intermediate,
|
||||||
|
)
|
||||||
|
|
||||||
|
return ImageOutput(
|
||||||
|
image=ImageField(image_name=image_dto.image_name),
|
||||||
|
width=image_dto.width,
|
||||||
|
height=image_dto.height,
|
||||||
|
)
|
||||||
|
20
invokeai/backend/image_util/cv2_inpaint.py
Normal file
20
invokeai/backend/image_util/cv2_inpaint.py
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
|
||||||
|
def cv2_inpaint(image: Image.Image) -> Image.Image:
|
||||||
|
# Prepare Image
|
||||||
|
image_array = np.array(image.convert("RGB"))
|
||||||
|
image_cv = cv2.cvtColor(image_array, cv2.COLOR_RGB2BGR)
|
||||||
|
|
||||||
|
# Prepare Mask From Alpha Channel
|
||||||
|
mask = image.split()[3].convert("RGB")
|
||||||
|
mask_array = np.array(mask)
|
||||||
|
mask_cv = cv2.cvtColor(mask_array, cv2.COLOR_BGR2GRAY)
|
||||||
|
mask_inv = cv2.bitwise_not(mask_cv)
|
||||||
|
|
||||||
|
# Inpaint Image
|
||||||
|
inpainted_result = cv2.inpaint(image_cv, mask_inv, 3, cv2.INPAINT_TELEA)
|
||||||
|
inpainted_image = Image.fromarray(cv2.cvtColor(inpainted_result, cv2.COLOR_BGR2RGB))
|
||||||
|
return inpainted_image
|
@ -506,6 +506,14 @@ export const buildCanvasOutpaintGraph = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (infillMethod === 'cv2') {
|
||||||
|
graph.nodes[INPAINT_INFILL] = {
|
||||||
|
type: 'infill_cv2',
|
||||||
|
id: INPAINT_INFILL,
|
||||||
|
is_intermediate: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (infillMethod === 'tile') {
|
if (infillMethod === 'tile') {
|
||||||
graph.nodes[INPAINT_INFILL] = {
|
graph.nodes[INPAINT_INFILL] = {
|
||||||
type: 'infill_tile',
|
type: 'infill_tile',
|
||||||
|
@ -521,6 +521,14 @@ export const buildCanvasSDXLOutpaintGraph = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (infillMethod === 'cv2') {
|
||||||
|
graph.nodes[INPAINT_INFILL] = {
|
||||||
|
type: 'infill_cv2',
|
||||||
|
id: INPAINT_INFILL,
|
||||||
|
is_intermediate: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (infillMethod === 'tile') {
|
if (infillMethod === 'tile') {
|
||||||
graph.nodes[INPAINT_INFILL] = {
|
graph.nodes[INPAINT_INFILL] = {
|
||||||
type: 'infill_tile',
|
type: 'infill_tile',
|
||||||
|
121
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
121
invokeai/frontend/web/src/services/api/schema.d.ts
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user