diff --git a/lama_cleaner/benchmark.py b/lama_cleaner/benchmark.py
index a0a170e..f30915d 100644
--- a/lama_cleaner/benchmark.py
+++ b/lama_cleaner/benchmark.py
@@ -101,7 +101,6 @@ if __name__ == "__main__":
model = ModelManager(
name=args.name,
device=device,
- sd_run_local=True,
disable_nsfw=True,
sd_cpu_textencoder=True,
hf_access_token="123"
diff --git a/lama_cleaner/const.py b/lama_cleaner/const.py
index 27a1ecd..ee436f9 100644
--- a/lama_cleaner/const.py
+++ b/lama_cleaner/const.py
@@ -65,7 +65,7 @@ Run Stable Diffusion text encoder model on CPU to save GPU memory.
SD_CONTROLNET_HELP = """
Run Stable Diffusion normal or inpainting model with ControlNet.
"""
-DEFAULT_SD_CONTROLNET_METHOD = "thibaud/controlnet-sd21-openpose-diffusers"
+DEFAULT_SD_CONTROLNET_METHOD = "lllyasviel/control_v11p_sd15_canny"
SD_CONTROLNET_CHOICES = [
"lllyasviel/control_v11p_sd15_canny",
# "lllyasviel/control_v11p_sd15_seg",
diff --git a/lama_cleaner/model/instruct_pix2pix.py b/lama_cleaner/model/instruct_pix2pix.py
index 2b99abb..4645fa5 100644
--- a/lama_cleaner/model/instruct_pix2pix.py
+++ b/lama_cleaner/model/instruct_pix2pix.py
@@ -66,9 +66,9 @@ class InstructPix2Pix(DiffusionInpaintModel):
image=PIL.Image.fromarray(image),
prompt=config.prompt,
negative_prompt=config.negative_prompt,
- num_inference_steps=config.p2p_steps,
+ num_inference_steps=config.sd_steps,
image_guidance_scale=config.p2p_image_guidance_scale,
- guidance_scale=config.p2p_guidance_scale,
+ guidance_scale=config.sd_guidance_scale,
output_type="np",
generator=torch.manual_seed(config.sd_seed),
).images[0]
diff --git a/lama_cleaner/model/paint_by_example.py b/lama_cleaner/model/paint_by_example.py
index 8237cbd..c8417d9 100644
--- a/lama_cleaner/model/paint_by_example.py
+++ b/lama_cleaner/model/paint_by_example.py
@@ -19,7 +19,7 @@ class PaintByExample(DiffusionInpaintModel):
fp16 = not kwargs.get("no_half", False)
use_gpu = device == torch.device("cuda") and torch.cuda.is_available()
torch_dtype = torch.float16 if use_gpu and fp16 else torch.float32
- model_kwargs = {"local_files_only": kwargs.get("local_files_only", False)}
+ model_kwargs = {}
if kwargs["disable_nsfw"] or kwargs.get("cpu_offload", False):
logger.info("Disable Paint By Example Model NSFW checker")
@@ -58,24 +58,17 @@ class PaintByExample(DiffusionInpaintModel):
image=PIL.Image.fromarray(image),
mask_image=PIL.Image.fromarray(mask[:, :, -1], mode="L"),
example_image=config.paint_by_example_example_image,
- num_inference_steps=config.paint_by_example_steps,
+ num_inference_steps=config.sd_steps,
+ guidance_scale=config.sd_guidance_scale,
+ negative_prompt="out of frame, lowres, error, cropped, worst quality, low quality, jpeg artifacts, ugly, duplicate, morbid, mutilated, out of frame, mutation, deformed, blurry, dehydrated, bad anatomy, bad proportions, extra limbs, disfigured, gross proportions, malformed limbs, watermark, signature",
output_type="np.array",
- generator=torch.manual_seed(config.paint_by_example_seed),
+ generator=torch.manual_seed(config.sd_seed),
).images[0]
output = (output * 255).round().astype("uint8")
output = cv2.cvtColor(output, cv2.COLOR_RGB2BGR)
return output
- def forward_post_process(self, result, image, mask, config):
- if config.paint_by_example_match_histograms:
- result = self._match_histograms(result, image[:, :, ::-1], mask)
-
- if config.paint_by_example_mask_blur != 0:
- k = 2 * config.paint_by_example_mask_blur + 1
- mask = cv2.GaussianBlur(mask, (k, k), 0)
- return result, image, mask
-
@staticmethod
def is_downloaded() -> bool:
# model will be downloaded when app start, and can't switch in frontend settings
diff --git a/lama_cleaner/model/utils.py b/lama_cleaner/model/utils.py
index e1e9927..1b10075 100644
--- a/lama_cleaner/model/utils.py
+++ b/lama_cleaner/model/utils.py
@@ -1,3 +1,4 @@
+import gc
import math
import random
from typing import Any
@@ -913,6 +914,7 @@ def torch_gc():
if torch.cuda.is_available():
torch.cuda.empty_cache()
torch.cuda.ipc_collect()
+ gc.collect()
def set_seed(seed: int):
diff --git a/lama_cleaner/tests/test_model_switch.py b/lama_cleaner/tests/test_model_switch.py
new file mode 100644
index 0000000..e69de29
diff --git a/web_app/index.html b/web_app/index.html
index e4b78ea..e92402c 100644
--- a/web_app/index.html
+++ b/web_app/index.html
@@ -2,9 +2,8 @@
-
- Vite + React + TS
+ Lama Cleaner
diff --git a/web_app/src/components/Editor.tsx b/web_app/src/components/Editor.tsx
index 0bdce28..c98d089 100644
--- a/web_app/src/components/Editor.tsx
+++ b/web_app/src/components/Editor.tsx
@@ -50,22 +50,12 @@ export default function Editor(props: EditorProps) {
imageHeight,
settings,
enableAutoSaving,
- cropperRect,
- enableManualInpainting,
setImageSize,
setBaseBrushSize,
- setIsInpainting,
- setSeed,
interactiveSegState,
updateInteractiveSegState,
- resetInteractiveSegState,
- isPluginRunning,
- setIsPluginRunning,
handleCanvasMouseDown,
handleCanvasMouseMove,
- cleanCurLineGroup,
- updateEditorState,
- resetRedoState,
undo,
redo,
undoDisabled,
@@ -82,22 +72,12 @@ export default function Editor(props: EditorProps) {
state.imageHeight,
state.settings,
state.serverConfig.enableAutoSaving,
- state.cropperState,
- state.settings.enableManualInpainting,
state.setImageSize,
state.setBaseBrushSize,
- state.setIsInpainting,
- state.setSeed,
state.interactiveSegState,
state.updateInteractiveSegState,
- state.resetInteractiveSegState,
- state.isPluginRunning,
- state.setIsPluginRunning,
state.handleCanvasMouseDown,
state.handleCanvasMouseMove,
- state.cleanCurLineGroup,
- state.updateEditorState,
- state.resetRedoState,
state.undo,
state.redo,
state.undoDisabled(),
@@ -112,9 +92,7 @@ export default function Editor(props: EditorProps) {
const renders = useStore((state) => state.editorState.renders)
const extraMasks = useStore((state) => state.editorState.extraMasks)
const lineGroups = useStore((state) => state.editorState.lineGroups)
- const lastLineGroup = useStore((state) => state.editorState.lastLineGroup)
const curLineGroup = useStore((state) => state.editorState.curLineGroup)
- const redoLineGroups = useStore((state) => state.editorState.redoLineGroups)
// Local State
const [showOriginal, setShowOriginal] = useState(false)
@@ -338,8 +316,6 @@ export default function Editor(props: EditorProps) {
if (isDraging) {
setIsDraging(false)
- // setCurLineGroup([])
- // drawOnCurrentRender([])
} else {
resetZoom()
}
@@ -434,22 +410,6 @@ export default function Editor(props: EditorProps) {
}
}
- const isOutsideCroper = (clickPnt: { x: number; y: number }) => {
- if (clickPnt.x < cropperRect.x) {
- return true
- }
- if (clickPnt.y < cropperRect.y) {
- return true
- }
- if (clickPnt.x > cropperRect.x + cropperRect.width) {
- return true
- }
- if (clickPnt.y > cropperRect.y + cropperRect.height) {
- return true
- }
- return false
- }
-
const onCanvasMouseUp = (ev: SyntheticEvent) => {
if (interactiveSegState.isInteractiveSeg) {
const xy = mouseXY(ev)
@@ -491,15 +451,6 @@ export default function Editor(props: EditorProps) {
return
}
- // if (
- // isDiffusionModels &&
- // settings.showCroper &&
- // isOutsideCroper(mouseXY(ev))
- // ) {
- // // TODO: 去掉这个逻辑,在 cropper 层截断 click 点击?
- // return
- // }
-
setIsDraging(true)
handleCanvasMouseDown(mouseXY(ev))
}
@@ -850,15 +801,6 @@ export default function Editor(props: EditorProps) {
)
}
- // const onInteractiveAccept = () => {
- // setInteractiveSegMask(tmpInteractiveSegMask)
- // setTmpInteractiveSegMask(null)
-
- // if (!enableManualInpainting && tmpInteractiveSegMask) {
- // runInpainting(false, undefined, tmpInteractiveSegMask)
- // }
- // }
-
return (
(
{children}
)
const SidePanel = () => {
- const [settings, updateSettings, showSidePanel, runInpainting] = useStore(
- (state) => [
- state.settings,
- state.updateSettings,
- state.showSidePanel(),
- state.runInpainting,
- ]
- )
+ const [
+ settings,
+ windowSize,
+ paintByExampleFile,
+ isProcessing,
+ updateSettings,
+ showSidePanel,
+ runInpainting,
+ updateAppState,
+ ] = useStore((state) => [
+ state.settings,
+ state.windowSize,
+ state.paintByExampleFile,
+ state.getIsProcessing(),
+ state.updateSettings,
+ state.showSidePanel(),
+ state.runInpainting,
+ state.updateAppState,
+ ])
+ const [exampleImage, isExampleImageLoaded] = useImage(paintByExampleFile)
const [open, toggleOpen] = useToggle(false)
useHotKey("c", () => {
toggleOpen()
})
- const windowSize = useWindowSize()
-
if (!showSidePanel) {
return null
}
@@ -72,20 +78,47 @@ const SidePanel = () => {
{
- updateSettings({ enableControlNet: value })
+ updateSettings({ enableControlnet: value })
}}
/>
-
+
+
+
+ updateSettings({ controlnetConditioningScale: vals[0] / 100 })
+ }
+ />
+ {
+ updateSettings({ controlnetConditioningScale: val })
+ }}
+ />
+
+
+
+
-
-
-
- {
- updateSettings({ controlnetConditioningScale: value })
- }}
- />
-
-
)
@@ -166,74 +179,79 @@ const SidePanel = () => {
}}
/>
-
-
-
-
{
- updateSettings({
- freeuConfig: { ...settings.freeuConfig, s1: value },
- })
- }}
- />
+
+
+
+
+ {
+ updateSettings({
+ freeuConfig: { ...settings.freeuConfig, s1: value },
+ })
+ }}
+ />
+
+
+
+ {
+ updateSettings({
+ freeuConfig: { ...settings.freeuConfig, s2: value },
+ })
+ }}
+ />
+
-
-
- {
- updateSettings({
- freeuConfig: { ...settings.freeuConfig, s2: value },
- })
- }}
- />
-
-
-
- {
- updateSettings({
- freeuConfig: { ...settings.freeuConfig, b1: value },
- })
- }}
- />
-
-
-
-
{
- updateSettings({
- freeuConfig: { ...settings.freeuConfig, b2: value },
- })
- }}
- />
+
+
+
+
+ {
+ updateSettings({
+ freeuConfig: { ...settings.freeuConfig, b1: value },
+ })
+ }}
+ />
+
+
+
+ {
+ updateSettings({
+ freeuConfig: { ...settings.freeuConfig, b2: value },
+ })
+ }}
+ />
+
@@ -241,6 +259,110 @@ const SidePanel = () => {
)
}
+ const renderNegativePrompt = () => {
+ if (!settings.model.need_prompt) {
+ return null
+ }
+
+ return (
+
+ )
+ }
+
+ const renderPaintByExample = () => {
+ if (settings.model.name !== PAINT_BY_EXAMPLE) {
+ return null
+ }
+
+ return (
+
+
+ Example Image
+ {
+ updateAppState({ paintByExampleFile: file })
+ }}
+ >
+
+
+
+ {isExampleImageLoaded ? (
+
+
+
+ ) : (
+ <>>
+ )}
+
+
+ )
+ }
+
+ const renderP2PImageGuidanceScale = () => {
+ if (settings.model.name !== INSTRUCT_PIX2PIX) {
+ return null
+ }
+ return (
+
+
+
+
+ updateSettings({ p2pImageGuidanceScale: vals[0] / 100 })
+ }
+ />
+ {
+ updateSettings({ p2pImageGuidanceScale: val })
+ }}
+ />
+
+
+ )
+ }
+
return (
{
onOpenAutoFocus={(event) => event.preventDefault()}
onPointerDownOutside={(event) => event.preventDefault()}
>
-
+
{
@@ -287,7 +409,7 @@ const SidePanel = () => {
style={{ height: windowSize.height - 160 }}
className="pr-3"
>
-
+
{
/>
-
+
- {
- updateSettings({ sdSteps: value })
- }}
- />
-
+
+ updateSettings({ sdSteps: vals[0] })}
+ />
+ {
+ updateSettings({ sdSteps: val })
+ }}
+ />
+
+
-
+
- {
- updateSettings({ sdGuidanceScale: value })
- }}
- />
-
+
+
+ updateSettings({ sdGuidanceScale: vals[0] / 100 })
+ }
+ />
+ {
+ updateSettings({ sdGuidanceScale: val })
+ }}
+ />
+
+
-
-
-
-
({settings.sdStrength})
-
-
- updateSettings({ sdStrength: vals[0] / 100 })
- }
- />
-
+ {renderP2PImageGuidanceScale()}
+
+
+
+
+
+ updateSettings({ sdStrength: vals[0] / 100 })
+ }
+ />
+ {
+ updateSettings({ sdStrength: val })
+ }}
+ />
+
+
@@ -383,7 +538,7 @@ const SidePanel = () => {
}}
/>
{
-
+ {renderNegativePrompt()}
-
- {renderConterNetSetting()}
-
+ {renderConterNetSetting()}
{renderFreeu()}
{renderLCMLora()}
-
+
- {
- updateSettings({ sdMaskBlur: value })
- }}
- />
-
+
+
+ updateSettings({ sdMaskBlur: vals[0] })
+ }
+ />
+ {
+ updateSettings({ sdMaskBlur: value })
+ }}
+ />
+
+
@@ -446,6 +594,10 @@ const SidePanel = () => {
}}
/>
+
+
+
+ {renderPaintByExample()}
diff --git a/web_app/src/components/ui/select.tsx b/web_app/src/components/ui/select.tsx
index cdfb8ce..8a20ee7 100644
--- a/web_app/src/components/ui/select.tsx
+++ b/web_app/src/components/ui/select.tsx
@@ -25,6 +25,7 @@ const SelectTrigger = React.forwardRef<
"flex h-9 w-full items-center justify-between whitespace-nowrap rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-1 focus:ring-ring disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
className
)}
+ tabIndex={-1}
{...props}
>
{children}
@@ -84,6 +85,7 @@ const SelectContent = React.forwardRef<
className
)}
position={position}
+ onCloseAutoFocus={(event) => event.preventDefault()}
{...props}
>
diff --git a/web_app/src/components/ui/slider.tsx b/web_app/src/components/ui/slider.tsx
index cc1e05f..22a8afc 100644
--- a/web_app/src/components/ui/slider.tsx
+++ b/web_app/src/components/ui/slider.tsx
@@ -13,14 +13,15 @@ const Slider = React.forwardRef<
"relative flex w-full touch-none select-none items-center",
className
)}
+ tabIndex={-1}
{...props}
>
-
-
+
+
))
diff --git a/web_app/src/components/ui/switch.tsx b/web_app/src/components/ui/switch.tsx
index 455c23b..5ed8fd8 100644
--- a/web_app/src/components/ui/switch.tsx
+++ b/web_app/src/components/ui/switch.tsx
@@ -12,6 +12,7 @@ const Switch = React.forwardRef<
"peer inline-flex h-5 w-9 shrink-0 cursor-pointer items-center rounded-full border-2 border-transparent shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=unchecked]:bg-input",
className
)}
+ tabIndex={-1}
{...props}
ref={ref}
>
diff --git a/web_app/src/components/ui/textarea.tsx b/web_app/src/components/ui/textarea.tsx
index 93eb260..b53b030 100644
--- a/web_app/src/components/ui/textarea.tsx
+++ b/web_app/src/components/ui/textarea.tsx
@@ -11,7 +11,8 @@ const Textarea = React.forwardRef(