This commit is contained in:
Qing
2023-12-22 14:00:11 +08:00
parent 141936a937
commit eb9764176c
20 changed files with 738 additions and 255 deletions

View File

@@ -1,6 +1,6 @@
import { ModelInfo, Rect } from "@/lib/types"
import { Settings } from "@/lib/states"
import { dataURItoBlob, srcToFile } from "@/lib/utils"
import { srcToFile } from "@/lib/utils"
import axios from "axios"
export const API_ENDPOINT = import.meta.env.VITE_BACKEND
@@ -15,6 +15,7 @@ export default async function inpaint(
imageFile: File,
settings: Settings,
croperRect: Rect,
extenderState: Rect,
mask: File | Blob,
paintByExampleImage: File | null = null
) {
@@ -32,11 +33,18 @@ export default async function inpaint(
fd.append("prompt", settings.prompt)
fd.append("negativePrompt", settings.negativePrompt)
fd.append("useCroper", settings.showCropper ? "true" : "false")
fd.append("croperX", croperRect.x.toString())
fd.append("croperY", croperRect.y.toString())
fd.append("croperHeight", croperRect.height.toString())
fd.append("croperWidth", croperRect.width.toString())
fd.append("useCroper", settings.showCropper ? "true" : "false")
fd.append("useExtender", settings.showExtender ? "true" : "false")
fd.append("extenderX", extenderState.x.toString())
fd.append("extenderY", extenderState.y.toString())
fd.append("extenderHeight", extenderState.height.toString())
fd.append("extenderWidth", extenderState.width.toString())
fd.append("sdMaskBlur", settings.sdMaskBlur.toString())
fd.append("sdStrength", settings.sdStrength.toString())
@@ -82,7 +90,7 @@ export default async function inpaint(
})
if (res.ok) {
const blob = await res.blob()
const newSeed = res.headers.get("x-seed")
const newSeed = res.headers.get("X-seed")
return { blob: URL.createObjectURL(blob), seed: newSeed }
}
const errMsg = await res.text()

View File

@@ -8,6 +8,15 @@ export const MODEL_TYPE_DIFFUSERS_SDXL_INPAINT = "diffusers_sdxl_inpaint"
export const MODEL_TYPE_OTHER = "diffusers_other"
export const BRUSH_COLOR = "#ffcc00bb"
export const EXTENDER_X = "extender_x"
export const EXTENDER_Y = "extender_y"
export const EXTENDER_ALL = "extender_all"
export const EXTENDER_BUILTIN_X_LEFT = "extender_builtin_x_left"
export const EXTENDER_BUILTIN_X_RIGHT = "extender_builtin_x_right"
export const EXTENDER_BUILTIN_Y_TOP = "extender_builtin_y_top"
export const EXTENDER_BUILTIN_Y_BOTTOM = "extender_builtin_y_bottom"
export const EXTENDER_BUILTIN_ALL = "extender_builtin_all"
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"

View File

@@ -1,22 +0,0 @@
import mitt from "mitt"
export const EVENT_PROMPT = "prompt"
export const EVENT_CUSTOM_MASK = "custom_mask"
export interface CustomMaskEventData {
mask: File
}
export const EVENT_PAINT_BY_EXAMPLE = "paint_by_example"
export interface PaintByExampleEventData {
image: File
}
export const RERUN_LAST_MASK = "rerun_last_mask"
export const DREAM_BUTTON_MOUSE_ENTER = "dream_button_mouse_enter"
export const DREAM_BUTTON_MOUSE_LEAVE = "dream_btoon_mouse_leave"
const emitter = mitt()
export default emitter

View File

@@ -21,6 +21,14 @@ import {
BRUSH_COLOR,
DEFAULT_BRUSH_SIZE,
DEFAULT_NEGATIVE_PROMPT,
EXTENDER_ALL,
EXTENDER_BUILTIN_ALL,
EXTENDER_BUILTIN_X_LEFT,
EXTENDER_BUILTIN_X_RIGHT,
EXTENDER_BUILTIN_Y_BOTTOM,
EXTENDER_BUILTIN_Y_TOP,
EXTENDER_X,
EXTENDER_Y,
MODEL_TYPE_INPAINT,
PAINT_BY_EXAMPLE,
} from "./const"
@@ -56,7 +64,8 @@ export type Settings = {
enableManualInpainting: boolean
enableUploadMask: boolean
showCropper: boolean
showExpender: boolean
showExtender: boolean
extenderDirection: string
// For LDM
ldmSteps: number
@@ -168,6 +177,9 @@ type AppAction = {
setExtenderY: (newValue: number) => void
setExtenderWidth: (newValue: number) => void
setExtenderHeight: (newValue: number) => void
updateExtenderDirection: (newValue: string) => void
resetExtender: (width: number, height: number) => void
updateExtenderByBuiltIn: (direction: string, scale: number) => void
setServerConfig: (newValue: ServerConfig) => void
setSeed: (newValue: number) => void
@@ -281,7 +293,8 @@ const defaultValues: AppState = {
},
enableControlnet: false,
showCropper: false,
showExpender: false,
showExtender: false,
extenderDirection: EXTENDER_ALL,
enableDownloadMask: false,
enableManualInpainting: false,
enableUploadMask: false,
@@ -362,7 +375,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
}
return targetFile
},
// todo: 传入 custom mask单独逻辑
runInpainting: async () => {
const {
isInpainting,
@@ -372,6 +385,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
imageHeight,
settings,
cropperState,
extenderState,
} = get()
if (isInpainting) {
return
@@ -398,7 +412,11 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
// 2. 结果替换当前 render
let maskLineGroup: LineGroup = []
if (useLastLineGroup === true) {
if (lastLineGroup.length === 0 && maskImage === null) {
if (
lastLineGroup.length === 0 &&
maskImage === null &&
!settings.showExtender
) {
toast({
variant: "destructive",
description: "Please draw mask on picture",
@@ -407,7 +425,11 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
}
maskLineGroup = lastLineGroup
} else {
if (curLineGroup.length === 0 && maskImage === null) {
if (
curLineGroup.length === 0 &&
maskImage === null &&
!settings.showExtender
) {
toast({
variant: "destructive",
description: "Please draw mask on picture",
@@ -455,6 +477,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
targetFile,
settings,
cropperState,
extenderState,
dataURItoBlob(maskCanvas.toDataURL()),
paintByExampleFile
)
@@ -465,7 +488,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
const { blob, seed } = res
if (seed) {
set((state) => (state.settings.seed = parseInt(seed, 10)))
get().setSeed(parseInt(seed, 10))
}
const newRender = new Image()
await loadImage(newRender, blob)
@@ -794,7 +817,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
state.isPluginRunning = newValue
}),
setFile: (file: File) =>
setFile: (file: File) => {
set((state) => {
state.file = file
state.interactiveSegState = castDraft(
@@ -802,7 +825,8 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
)
state.editorState = castDraft(defaultValues.editorState)
state.cropperState = defaultValues.cropperState
}),
})
},
setCustomFile: (file: File) =>
set((state) => {
@@ -822,6 +846,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
state.editorState.brushSizeScale =
Math.max(Math.min(width, height), 512) / 512
})
get().resetExtender(width, height)
},
setCropperX: (newValue: number) =>
@@ -864,6 +889,68 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
state.extenderState.height = newValue
}),
updateExtenderDirection: (newValue: string) => {
console.log(
`updateExtenderDirection: ${JSON.stringify(get().extenderState)}`
)
set((state) => {
state.settings.extenderDirection = newValue
state.extenderState.x = 0
state.extenderState.y = 0
state.extenderState.width = state.imageWidth
state.extenderState.height = state.imageHeight
})
},
updateExtenderByBuiltIn: (direction: string, scale: number) => {
const newExtenderState = { ...defaultValues.extenderState }
let { x, y, width, height } = newExtenderState
const { imageWidth, imageHeight } = get()
width = imageWidth
height = imageHeight
switch (direction) {
case EXTENDER_BUILTIN_X_LEFT:
x = -Math.ceil(imageWidth * (scale - 1))
width = Math.ceil(imageWidth * scale)
break
case EXTENDER_BUILTIN_X_RIGHT:
width = Math.ceil(imageWidth * scale)
break
case EXTENDER_BUILTIN_Y_TOP:
y = -Math.ceil(imageHeight * (scale - 1))
height = Math.ceil(imageHeight * scale)
break
case EXTENDER_BUILTIN_Y_BOTTOM:
height = Math.ceil(imageHeight * scale)
break
case EXTENDER_BUILTIN_ALL:
x = -Math.ceil((imageWidth * (scale - 1)) / 2)
y = -Math.ceil((imageHeight * (scale - 1)) / 2)
width = Math.ceil(imageWidth * scale)
height = Math.ceil(imageHeight * scale)
break
default:
break
}
set((state) => {
state.extenderState.x = x
state.extenderState.y = y
state.extenderState.width = width
state.extenderState.height = height
})
},
resetExtender: (width: number, height: number) => {
set((state) => {
state.extenderState.x = 0
state.extenderState.y = 0
state.extenderState.width = width
state.extenderState.height = height
})
},
setSeed: (newValue: number) =>
set((state) => {
state.settings.seed = newValue
@@ -871,7 +958,7 @@ export const useStore = createWithEqualityFn<AppState & AppAction>()(
})),
{
name: "ZUSTAND_STATE", // name of the item in the storage (must be unique)
version: 1,
version: 0,
partialize: (state) =>
Object.fromEntries(
Object.entries(state).filter(([key]) =>

View File

@@ -67,7 +67,7 @@ export enum SDSampler {
kEulerA = "k_euler_a",
dpmPlusPlus = "dpm++",
uni_pc = "uni_pc",
lcm = "lcm",
// lcm = "lcm",
}
export interface FreeuConfig {

View File

@@ -71,6 +71,26 @@ export function canvasToImage(
})
}
export function fileToImage(file: File): Promise<HTMLImageElement> {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.onload = () => {
const image = new Image()
image.onload = () => {
resolve(image)
}
image.onerror = () => {
reject("无法加载图像。")
}
image.src = reader.result as string
}
reader.onerror = () => {
reject("无法读取文件。")
}
reader.readAsDataURL(file)
})
}
export function srcToFile(src: string, fileName: string, mimeType: string) {
return fetch(src)
.then(function (res) {