update
This commit is contained in:
parent
cbe6577890
commit
24e95daac1
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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]
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
0
lama_cleaner/tests/test_model_switch.py
Normal file
0
lama_cleaner/tests/test_model_switch.py
Normal file
@ -2,9 +2,8 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>Lama Cleaner</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
@ -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 (
|
||||
<div
|
||||
className="flex w-screen h-screen justify-center items-center"
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { FormEvent, useState } from "react"
|
||||
import { useToggle, useWindowSize } from "react-use"
|
||||
import { FormEvent } from "react"
|
||||
import { useToggle } from "react-use"
|
||||
import { useStore } from "@/lib/states"
|
||||
import { Switch } from "./ui/switch"
|
||||
import { Label } from "./ui/label"
|
||||
@ -16,39 +16,45 @@ import { Textarea } from "./ui/textarea"
|
||||
import { SDSampler } from "@/lib/types"
|
||||
import { Separator } from "./ui/separator"
|
||||
import { ScrollArea } from "./ui/scroll-area"
|
||||
import {
|
||||
Sheet,
|
||||
SheetContent,
|
||||
SheetHeader,
|
||||
SheetTitle,
|
||||
SheetTrigger,
|
||||
} from "./ui/sheet"
|
||||
import { ChevronLeft, ChevronRight } from "lucide-react"
|
||||
import { Button } from "./ui/button"
|
||||
import { Sheet, SheetContent, SheetHeader, SheetTrigger } from "./ui/sheet"
|
||||
import { ChevronLeft, ChevronRight, Upload } from "lucide-react"
|
||||
import { Button, ImageUploadButton } from "./ui/button"
|
||||
import useHotKey from "@/hooks/useHotkey"
|
||||
import { Slider } from "./ui/slider"
|
||||
import { useImage } from "@/hooks/useImage"
|
||||
import { INSTRUCT_PIX2PIX, PAINT_BY_EXAMPLE } from "@/lib/const"
|
||||
|
||||
const RowContainer = ({ children }: { children: React.ReactNode }) => (
|
||||
<div className="flex justify-between items-center pr-2">{children}</div>
|
||||
)
|
||||
|
||||
const SidePanel = () => {
|
||||
const [settings, updateSettings, showSidePanel, runInpainting] = useStore(
|
||||
(state) => [
|
||||
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 = () => {
|
||||
<Label htmlFor="controlnet">Controlnet</Label>
|
||||
<Switch
|
||||
id="controlnet"
|
||||
checked={settings.enableControlNet}
|
||||
checked={settings.enableControlnet}
|
||||
onCheckedChange={(value) => {
|
||||
updateSettings({ enableControlNet: value })
|
||||
updateSettings({ enableControlnet: value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="pl-1 pr-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<RowContainer>
|
||||
<Slider
|
||||
className="w-[180px]"
|
||||
defaultValue={[100]}
|
||||
min={1}
|
||||
max={100}
|
||||
step={1}
|
||||
disabled={!settings.enableControlnet}
|
||||
value={[Math.floor(settings.controlnetConditioningScale * 100)]}
|
||||
onValueChange={(vals) =>
|
||||
updateSettings({ controlnetConditioningScale: vals[0] / 100 })
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
id="controlnet-weight"
|
||||
className="w-[60px] rounded-full"
|
||||
disabled={!settings.enableControlnet}
|
||||
numberValue={settings.controlnetConditioningScale}
|
||||
allowFloat={false}
|
||||
onNumberValueChange={(val) => {
|
||||
updateSettings({ controlnetConditioningScale: val })
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
</div>
|
||||
|
||||
<div className="pr-2">
|
||||
<Select
|
||||
value={settings.controlnetMethod}
|
||||
onValueChange={(value) => {
|
||||
updateSettings({ controlnetMethod: value })
|
||||
}}
|
||||
disabled={!settings.enableControlNet}
|
||||
disabled={!settings.enableControlnet}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select control method" />
|
||||
@ -102,26 +135,6 @@ const SidePanel = () => {
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RowContainer>
|
||||
<Label
|
||||
htmlFor="controlnet-weight"
|
||||
disabled={!settings.enableControlNet}
|
||||
>
|
||||
weight
|
||||
</Label>
|
||||
<NumberInput
|
||||
id="controlnet-weight"
|
||||
className="w-14"
|
||||
disabled={!settings.enableControlNet}
|
||||
numberValue={settings.controlnetConditioningScale}
|
||||
allowFloat
|
||||
onNumberValueChange={(value) => {
|
||||
updateSettings({ controlnetConditioningScale: value })
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
|
||||
<Separator />
|
||||
</div>
|
||||
)
|
||||
@ -166,8 +179,9 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
<div className="flex flex-col gap-2 items-start">
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex justify-center gap-6">
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Label htmlFor="freeu-s1" disabled={!settings.enableFreeu}>
|
||||
s1
|
||||
</Label>
|
||||
@ -184,7 +198,7 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 items-start">
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Label htmlFor="freeu-s2" disabled={!settings.enableFreeu}>
|
||||
s2
|
||||
</Label>
|
||||
@ -201,7 +215,10 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 items-start">
|
||||
</div>
|
||||
|
||||
<div className="flex justify-center gap-6">
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Label htmlFor="freeu-b1" disabled={!settings.enableFreeu}>
|
||||
b1
|
||||
</Label>
|
||||
@ -218,7 +235,7 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 items-start">
|
||||
<div className="flex gap-2 items-center justify-center">
|
||||
<Label htmlFor="freeu-b2" disabled={!settings.enableFreeu}>
|
||||
b2
|
||||
</Label>
|
||||
@ -236,11 +253,116 @@ const SidePanel = () => {
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const renderNegativePrompt = () => {
|
||||
if (!settings.model.need_prompt) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<Label htmlFor="negative-prompt">Negative prompt</Label>
|
||||
<div className="pl-2 pr-4">
|
||||
<Textarea
|
||||
rows={4}
|
||||
onKeyUp={onKeyUp}
|
||||
className="max-h-[8rem] overflow-y-auto mb-2"
|
||||
placeholder=""
|
||||
id="negative-prompt"
|
||||
value={settings.negativePrompt}
|
||||
onInput={(evt: FormEvent<HTMLTextAreaElement>) => {
|
||||
evt.preventDefault()
|
||||
evt.stopPropagation()
|
||||
const target = evt.target as HTMLTextAreaElement
|
||||
updateSettings({ negativePrompt: target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const renderPaintByExample = () => {
|
||||
if (settings.model.name !== PAINT_BY_EXAMPLE) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<RowContainer>
|
||||
<div>Example Image</div>
|
||||
<ImageUploadButton
|
||||
tooltip="Upload example image"
|
||||
onFileUpload={(file) => {
|
||||
updateAppState({ paintByExampleFile: file })
|
||||
}}
|
||||
>
|
||||
<Upload />
|
||||
</ImageUploadButton>
|
||||
</RowContainer>
|
||||
{isExampleImageLoaded ? (
|
||||
<div className="flex justify-center items-center">
|
||||
<img
|
||||
src={exampleImage.src}
|
||||
alt="example"
|
||||
className="max-w-[200px] max-h-[200px] m-3"
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<Button
|
||||
variant="default"
|
||||
className="w-full"
|
||||
disabled={isProcessing || !isExampleImageLoaded}
|
||||
onClick={() => {
|
||||
runInpainting()
|
||||
}}
|
||||
>
|
||||
Paint
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const renderP2PImageGuidanceScale = () => {
|
||||
if (settings.model.name !== INSTRUCT_PIX2PIX) {
|
||||
return null
|
||||
}
|
||||
return (
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="image-guidance-scale">Image guidance scale</Label>
|
||||
<RowContainer>
|
||||
<Slider
|
||||
className="w-[180px]"
|
||||
defaultValue={[150]}
|
||||
min={100}
|
||||
max={1000}
|
||||
step={1}
|
||||
value={[Math.floor(settings.p2pImageGuidanceScale * 100)]}
|
||||
onValueChange={(vals) =>
|
||||
updateSettings({ p2pImageGuidanceScale: vals[0] / 100 })
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
id="image-guidance-scale"
|
||||
className="w-[60px] rounded-full"
|
||||
numberValue={settings.p2pImageGuidanceScale}
|
||||
allowFloat
|
||||
onNumberValueChange={(val) => {
|
||||
updateSettings({ p2pImageGuidanceScale: val })
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Sheet open={open} modal={false}>
|
||||
<SheetTrigger
|
||||
@ -263,7 +385,7 @@ const SidePanel = () => {
|
||||
onOpenAutoFocus={(event) => event.preventDefault()}
|
||||
onPointerDownOutside={(event) => event.preventDefault()}
|
||||
>
|
||||
<SheetHeader className="mb-4">
|
||||
<SheetHeader>
|
||||
<RowContainer>
|
||||
<div className="overflow-hidden mr-8">
|
||||
{
|
||||
@ -287,7 +409,7 @@ const SidePanel = () => {
|
||||
style={{ height: windowSize.height - 160 }}
|
||||
className="pr-3"
|
||||
>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex flex-col gap-4 mt-4">
|
||||
<RowContainer>
|
||||
<Label htmlFor="cropper">Cropper</Label>
|
||||
<Switch
|
||||
@ -299,50 +421,83 @@ const SidePanel = () => {
|
||||
/>
|
||||
</RowContainer>
|
||||
|
||||
<RowContainer>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="steps">Steps</Label>
|
||||
<RowContainer>
|
||||
<Slider
|
||||
className="w-[180px]"
|
||||
defaultValue={[30]}
|
||||
min={1}
|
||||
max={100}
|
||||
step={1}
|
||||
value={[Math.floor(settings.sdSteps)]}
|
||||
onValueChange={(vals) => updateSettings({ sdSteps: vals[0] })}
|
||||
/>
|
||||
<NumberInput
|
||||
id="steps"
|
||||
className="w-14"
|
||||
className="w-[60px] rounded-full"
|
||||
numberValue={settings.sdSteps}
|
||||
allowFloat={false}
|
||||
onNumberValueChange={(value) => {
|
||||
updateSettings({ sdSteps: value })
|
||||
onNumberValueChange={(val) => {
|
||||
updateSettings({ sdSteps: val })
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
</div>
|
||||
|
||||
<RowContainer>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="guidance-scale">Guidance scale</Label>
|
||||
<RowContainer>
|
||||
<Slider
|
||||
className="w-[180px]"
|
||||
defaultValue={[750]}
|
||||
min={100}
|
||||
max={1500}
|
||||
step={1}
|
||||
value={[Math.floor(settings.sdGuidanceScale * 100)]}
|
||||
onValueChange={(vals) =>
|
||||
updateSettings({ sdGuidanceScale: vals[0] / 100 })
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
id="guidance-scale"
|
||||
className="w-14"
|
||||
className="w-[60px] rounded-full"
|
||||
numberValue={settings.sdGuidanceScale}
|
||||
allowFloat
|
||||
onNumberValueChange={(value) => {
|
||||
updateSettings({ sdGuidanceScale: value })
|
||||
onNumberValueChange={(val) => {
|
||||
updateSettings({ sdGuidanceScale: val })
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
|
||||
<RowContainer>
|
||||
<div className="flex gap-2 items-center">
|
||||
<Label htmlFor="strength">Strength</Label>
|
||||
<div className="text-sm">({settings.sdStrength})</div>
|
||||
</div>
|
||||
|
||||
{renderP2PImageGuidanceScale()}
|
||||
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="strength">Strength</Label>
|
||||
<RowContainer>
|
||||
<Slider
|
||||
className="w-24"
|
||||
className="w-[180px]"
|
||||
defaultValue={[100]}
|
||||
min={10}
|
||||
max={100}
|
||||
step={1}
|
||||
tabIndex={-1}
|
||||
value={[Math.floor(settings.sdStrength * 100)]}
|
||||
onValueChange={(vals) =>
|
||||
updateSettings({ sdStrength: vals[0] / 100 })
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
id="strength"
|
||||
className="w-[60px] rounded-full"
|
||||
numberValue={settings.sdStrength}
|
||||
allowFloat
|
||||
onNumberValueChange={(val) => {
|
||||
updateSettings({ sdStrength: val })
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
</div>
|
||||
|
||||
<RowContainer>
|
||||
<Label htmlFor="sampler">Sampler</Label>
|
||||
@ -383,7 +538,7 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
<NumberInput
|
||||
title="Seed"
|
||||
id="seed"
|
||||
className="w-[100px]"
|
||||
disabled={!settings.seedFixed}
|
||||
numberValue={settings.seed}
|
||||
@ -395,39 +550,31 @@ const SidePanel = () => {
|
||||
</div>
|
||||
</RowContainer>
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
<Label htmlFor="negative-prompt">Negative prompt</Label>
|
||||
<div className="pl-2 pr-4">
|
||||
<Textarea
|
||||
rows={4}
|
||||
onKeyUp={onKeyUp}
|
||||
className="max-h-[8rem] overflow-y-auto mb-2"
|
||||
placeholder=""
|
||||
id="negative-prompt"
|
||||
value={settings.negativePrompt}
|
||||
onInput={(evt: FormEvent<HTMLTextAreaElement>) => {
|
||||
evt.preventDefault()
|
||||
evt.stopPropagation()
|
||||
const target = evt.target as HTMLTextAreaElement
|
||||
updateSettings({ negativePrompt: target.value })
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{renderNegativePrompt()}
|
||||
|
||||
<Separator />
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{renderConterNetSetting()}
|
||||
</div>
|
||||
{renderFreeu()}
|
||||
{renderLCMLora()}
|
||||
|
||||
<RowContainer>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Label htmlFor="mask-blur">Mask blur</Label>
|
||||
<RowContainer>
|
||||
<Slider
|
||||
className="w-[180px]"
|
||||
defaultValue={[5]}
|
||||
min={0}
|
||||
max={35}
|
||||
step={1}
|
||||
value={[Math.floor(settings.sdMaskBlur)]}
|
||||
onValueChange={(vals) =>
|
||||
updateSettings({ sdMaskBlur: vals[0] })
|
||||
}
|
||||
/>
|
||||
<NumberInput
|
||||
id="mask-blur"
|
||||
className="w-14"
|
||||
className="w-[60px] rounded-full"
|
||||
numberValue={settings.sdMaskBlur}
|
||||
allowFloat={false}
|
||||
onNumberValueChange={(value) => {
|
||||
@ -435,6 +582,7 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
</div>
|
||||
|
||||
<RowContainer>
|
||||
<Label htmlFor="match-histograms">Match histograms</Label>
|
||||
@ -446,6 +594,10 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
</RowContainer>
|
||||
|
||||
<Separator />
|
||||
|
||||
{renderPaintByExample()}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</SheetContent>
|
||||
|
@ -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}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
|
@ -13,14 +13,15 @@ const Slider = React.forwardRef<
|
||||
"relative flex w-full touch-none select-none items-center",
|
||||
className
|
||||
)}
|
||||
tabIndex={-1}
|
||||
{...props}
|
||||
>
|
||||
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary" />
|
||||
<SliderPrimitive.Track className="relative h-1.5 w-full grow overflow-hidden rounded-full bg-primary/20 data-[disabled]:cursor-not-allowed">
|
||||
<SliderPrimitive.Range className="absolute h-full bg-primary data-[disabled]:cursor-not-allowed " />
|
||||
</SliderPrimitive.Track>
|
||||
<SliderPrimitive.Thumb
|
||||
tabIndex={-1}
|
||||
className="block h-4 w-4 rounded-full border border-primary/60 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50"
|
||||
className="block h-4 w-4 rounded-full border border-primary/60 bg-background shadow transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring data-[disabled]:cursor-not-allowed"
|
||||
/>
|
||||
</SliderPrimitive.Root>
|
||||
))
|
||||
|
@ -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}
|
||||
>
|
||||
|
@ -11,7 +11,8 @@ const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
|
||||
<textarea
|
||||
className={cn(
|
||||
"flex min-h-[60px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
className,
|
||||
"overflow-auto"
|
||||
)}
|
||||
tabIndex={-1}
|
||||
ref={ref}
|
||||
|
@ -51,20 +51,6 @@ export default async function inpaint(
|
||||
fd.append("cv2Radius", settings.cv2Radius.toString())
|
||||
fd.append("cv2Flag", settings.cv2Flag.toString())
|
||||
|
||||
fd.append("paintByExampleSteps", settings.paintByExampleSteps.toString())
|
||||
fd.append(
|
||||
"paintByExampleGuidanceScale",
|
||||
settings.paintByExampleGuidanceScale.toString()
|
||||
)
|
||||
fd.append("paintByExampleSeed", settings.seed.toString())
|
||||
fd.append(
|
||||
"paintByExampleMaskBlur",
|
||||
settings.paintByExampleMaskBlur.toString()
|
||||
)
|
||||
fd.append(
|
||||
"paintByExampleMatchHistograms",
|
||||
settings.paintByExampleMatchHistograms ? "true" : "false"
|
||||
)
|
||||
// TODO: resize image's shortest_edge to 224 before pass to backend, save network time?
|
||||
// https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPImageProcessor
|
||||
if (paintByExampleImage) {
|
||||
@ -72,9 +58,7 @@ export default async function inpaint(
|
||||
}
|
||||
|
||||
// InstructPix2Pix
|
||||
fd.append("p2pSteps", settings.p2pSteps.toString())
|
||||
fd.append("p2pImageGuidanceScale", settings.p2pImageGuidanceScale.toString())
|
||||
fd.append("p2pGuidanceScale", settings.p2pGuidanceScale.toString())
|
||||
|
||||
// ControlNet
|
||||
fd.append(
|
||||
|
@ -11,3 +11,5 @@ export const BRUSH_COLOR = "#ffcc00bb"
|
||||
export const PAINT_BY_EXAMPLE = "Fantasy-Studio/Paint-by-Example"
|
||||
export const INSTRUCT_PIX2PIX = "timbrooks/instruct-pix2pix"
|
||||
export const KANDINSKY_2_2 = "kandinsky-community/kandinsky-2-2-decoder-inpaint"
|
||||
export const DEFAULT_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"
|
||||
|
@ -2,7 +2,6 @@ import { persist } from "zustand/middleware"
|
||||
import { shallow } from "zustand/shallow"
|
||||
import { immer } from "zustand/middleware/immer"
|
||||
import { castDraft } from "immer"
|
||||
import { nanoid } from "nanoid"
|
||||
import { createWithEqualityFn } from "zustand/traditional"
|
||||
import {
|
||||
CV2Flag,
|
||||
@ -20,14 +19,13 @@ import {
|
||||
} from "./types"
|
||||
import {
|
||||
DEFAULT_BRUSH_SIZE,
|
||||
INSTRUCT_PIX2PIX,
|
||||
DEFAULT_NEGATIVE_PROMPT,
|
||||
MODEL_TYPE_INPAINT,
|
||||
MODEL_TYPE_OTHER,
|
||||
PAINT_BY_EXAMPLE,
|
||||
} from "./const"
|
||||
import { dataURItoBlob, generateMask, loadImage, srcToFile } from "./utils"
|
||||
import inpaint, { runPlugin } from "./api"
|
||||
import { toast, useToast } from "@/components/ui/use-toast"
|
||||
import { toast } from "@/components/ui/use-toast"
|
||||
|
||||
type FileManagerState = {
|
||||
sortBy: SortBy
|
||||
@ -78,19 +76,11 @@ export type Settings = {
|
||||
sdMatchHistograms: boolean
|
||||
sdScale: number
|
||||
|
||||
// Paint by Example
|
||||
paintByExampleSteps: number
|
||||
paintByExampleGuidanceScale: number
|
||||
paintByExampleMaskBlur: number
|
||||
paintByExampleMatchHistograms: boolean
|
||||
|
||||
// InstructPix2Pix
|
||||
p2pSteps: number
|
||||
// Pix2Pix
|
||||
p2pImageGuidanceScale: number
|
||||
p2pGuidanceScale: number
|
||||
|
||||
// ControlNet
|
||||
enableControlNet: boolean
|
||||
enableControlnet: boolean
|
||||
controlnetConditioningScale: number
|
||||
controlnetMethod: string
|
||||
|
||||
@ -103,6 +93,8 @@ type ServerConfig = {
|
||||
plugins: string[]
|
||||
enableFileManager: boolean
|
||||
enableAutoSaving: boolean
|
||||
enableControlnet: boolean
|
||||
controlnetMethod: string
|
||||
}
|
||||
|
||||
type InteractiveSegState = {
|
||||
@ -117,7 +109,6 @@ type EditorState = {
|
||||
baseBrushSize: number
|
||||
brushSizeScale: number
|
||||
renders: HTMLImageElement[]
|
||||
paintByExampleImage: File | null
|
||||
lineGroups: LineGroup[]
|
||||
lastLineGroup: LineGroup
|
||||
curLineGroup: LineGroup
|
||||
@ -130,6 +121,7 @@ type EditorState = {
|
||||
|
||||
type AppState = {
|
||||
file: File | null
|
||||
paintByExampleFile: File | null
|
||||
customMask: File | null
|
||||
imageHeight: number
|
||||
imageWidth: number
|
||||
@ -195,6 +187,7 @@ type AppAction = {
|
||||
|
||||
const defaultValues: AppState = {
|
||||
file: null,
|
||||
paintByExampleFile: null,
|
||||
customMask: null,
|
||||
imageHeight: 0,
|
||||
imageWidth: 0,
|
||||
@ -210,7 +203,6 @@ const defaultValues: AppState = {
|
||||
baseBrushSize: DEFAULT_BRUSH_SIZE,
|
||||
brushSizeScale: 1,
|
||||
renders: [],
|
||||
paintByExampleImage: null,
|
||||
extraMasks: [],
|
||||
lineGroups: [],
|
||||
lastLineGroup: [],
|
||||
@ -246,6 +238,8 @@ const defaultValues: AppState = {
|
||||
plugins: [],
|
||||
enableFileManager: false,
|
||||
enableAutoSaving: false,
|
||||
enableControlnet: false,
|
||||
controlnetMethod: "lllyasviel/control_v11p_sd15_canny",
|
||||
},
|
||||
settings: {
|
||||
model: {
|
||||
@ -259,7 +253,7 @@ const defaultValues: AppState = {
|
||||
is_single_file_diffusers: false,
|
||||
need_prompt: false,
|
||||
},
|
||||
enableControlNet: false,
|
||||
enableControlnet: false,
|
||||
showCroper: false,
|
||||
enableDownloadMask: false,
|
||||
enableManualInpainting: false,
|
||||
@ -270,7 +264,7 @@ const defaultValues: AppState = {
|
||||
cv2Radius: 5,
|
||||
cv2Flag: CV2Flag.INPAINT_NS,
|
||||
prompt: "",
|
||||
negativePrompt: "",
|
||||
negativePrompt: DEFAULT_NEGATIVE_PROMPT,
|
||||
seed: 42,
|
||||
seedFixed: false,
|
||||
sdMaskBlur: 5,
|
||||
@ -280,13 +274,7 @@ const defaultValues: AppState = {
|
||||
sdSampler: SDSampler.uni_pc,
|
||||
sdMatchHistograms: false,
|
||||
sdScale: 100,
|
||||
paintByExampleSteps: 50,
|
||||
paintByExampleGuidanceScale: 7.5,
|
||||
paintByExampleMaskBlur: 5,
|
||||
paintByExampleMatchHistograms: false,
|
||||
p2pSteps: 50,
|
||||
p2pImageGuidanceScale: 1.5,
|
||||
p2pGuidanceScale: 7.5,
|
||||
controlnetConditioningScale: 0.4,
|
||||
controlnetMethod: "lllyasviel/control_v11p_sd15_canny",
|
||||
enableLCMLora: false,
|
||||
@ -320,6 +308,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
|
||||
const {
|
||||
isInpainting,
|
||||
file,
|
||||
paintByExampleFile,
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
settings,
|
||||
@ -332,13 +321,8 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
|
||||
if (file === null) {
|
||||
return
|
||||
}
|
||||
const {
|
||||
lastLineGroup,
|
||||
curLineGroup,
|
||||
lineGroups,
|
||||
renders,
|
||||
paintByExampleImage,
|
||||
} = get().editorState
|
||||
const { lastLineGroup, curLineGroup, lineGroups, renders } =
|
||||
get().editorState
|
||||
|
||||
const { interactiveSegMask, prevInteractiveSegMask } =
|
||||
get().interactiveSegState
|
||||
@ -413,7 +397,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
|
||||
settings,
|
||||
cropperState,
|
||||
dataURItoBlob(maskCanvas.toDataURL()),
|
||||
paintByExampleImage
|
||||
paintByExampleFile
|
||||
)
|
||||
|
||||
if (!res) {
|
||||
@ -687,6 +671,8 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
|
||||
setServerConfig: (newValue: ServerConfig) => {
|
||||
set((state) => {
|
||||
state.serverConfig = newValue
|
||||
state.settings.enableControlnet = newValue.enableControlnet
|
||||
state.settings.controlnetMethod = newValue.controlnetMethod
|
||||
})
|
||||
},
|
||||
|
||||
@ -804,7 +790,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
|
||||
})),
|
||||
{
|
||||
name: "ZUSTAND_STATE", // name of the item in the storage (must be unique)
|
||||
version: 0,
|
||||
version: 1,
|
||||
partialize: (state) =>
|
||||
Object.fromEntries(
|
||||
Object.entries(state).filter(([key]) =>
|
||||
@ -815,9 +801,3 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
|
||||
),
|
||||
shallow
|
||||
)
|
||||
|
||||
// export const useStore = <U>(selector: (state: AppState & AppAction) => U) => {
|
||||
// return createWithEqualityFn(selector, shallow)
|
||||
// }
|
||||
|
||||
// export const useStore = createWithEqualityFn(useBaseStore, shallow)
|
||||
|
Loading…
Reference in New Issue
Block a user