wip
This commit is contained in:
parent
ef1179a858
commit
53aea791c5
@ -84,7 +84,6 @@
|
||||
"socket.io-client": "^4.5.4",
|
||||
"source-map-loader": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
"tailwindcss": "^3.0.2",
|
||||
"terser-webpack-plugin": "^5.2.5",
|
||||
"typescript": "4.x",
|
||||
"webpack": "^5.64.4",
|
||||
|
File diff suppressed because it is too large
Load Diff
166
lama_cleaner/app/src/components/Extender/Extender.scss
Normal file
166
lama_cleaner/app/src/components/Extender/Extender.scss
Normal file
@ -0,0 +1,166 @@
|
||||
@use 'sass:math';
|
||||
|
||||
$drag-handle-shortside: 12px;
|
||||
$drag-handle-longside: 40px;
|
||||
$drag-bar-size: 12px;
|
||||
|
||||
$half-handle-shortside: math.div($drag-handle-shortside, 2);
|
||||
$half-handle-longside: math.div($drag-handle-longside, 2);
|
||||
$half-drag-bar-size: math.div($drag-bar-size, 2);
|
||||
|
||||
.extender-border {
|
||||
outline-color: var(--yellow-accent);
|
||||
outline-style: dashed;
|
||||
}
|
||||
|
||||
.info-bar {
|
||||
position: absolute;
|
||||
pointer-events: auto;
|
||||
font-size: 1rem;
|
||||
padding: 0.2rem 0.8rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12px;
|
||||
color: var(--text-color);
|
||||
background-color: var(--page-bg);
|
||||
border-radius: 9999px;
|
||||
|
||||
border: var(--editor-toolkit-panel-border);
|
||||
box-shadow: 0 0 0 1px #0000001a, 0 3px 16px #00000014, 0 2px 6px 1px #00000017;
|
||||
|
||||
&:hover {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
.extender-wrapper {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.extender {
|
||||
position: relative;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 2;
|
||||
pointer-events: none;
|
||||
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// align-items: center;
|
||||
|
||||
box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.drag-bar {
|
||||
position: absolute;
|
||||
pointer-events: auto;
|
||||
// display: none;
|
||||
|
||||
&.ord-top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: $drag-bar-size;
|
||||
margin-top: -$half-drag-bar-size;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
&.ord-right {
|
||||
right: 0;
|
||||
top: 0;
|
||||
width: $drag-bar-size;
|
||||
height: 100%;
|
||||
margin-right: -$half-drag-bar-size;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
&.ord-bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: $drag-bar-size;
|
||||
margin-bottom: -$half-drag-bar-size;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
&.ord-left {
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: $drag-bar-size;
|
||||
height: 100%;
|
||||
margin-left: -$half-drag-bar-size;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
}
|
||||
|
||||
.drag-handle {
|
||||
width: $drag-handle-shortside;
|
||||
height: $drag-handle-shortside;
|
||||
z-index: 4;
|
||||
position: absolute;
|
||||
display: block;
|
||||
content: '';
|
||||
border: 2px solid var(--yellow-accent);
|
||||
background-color: var(--yellow-accent-light);
|
||||
pointer-events: auto;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--yellow-accent);
|
||||
}
|
||||
|
||||
&.ord-topleft {
|
||||
cursor: nw-resize;
|
||||
top: (-$half-handle-shortside)-1px;
|
||||
left: (-$half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-topright {
|
||||
cursor: ne-resize;
|
||||
top: -($half-handle-shortside)-1px;
|
||||
right: -($half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-bottomright {
|
||||
cursor: se-resize;
|
||||
bottom: -($half-handle-shortside)-1px;
|
||||
right: -($half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-bottomleft {
|
||||
cursor: sw-resize;
|
||||
bottom: -($half-handle-shortside)-1px;
|
||||
left: -($half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-top,
|
||||
&.ord-bottom {
|
||||
left: calc(50% - $half-handle-shortside);
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
&.ord-top {
|
||||
top: (-$half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-bottom {
|
||||
bottom: -($half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-left,
|
||||
&.ord-right {
|
||||
top: calc(50% - $half-handle-shortside);
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
&.ord-left {
|
||||
left: (-$half-handle-shortside)-1px;
|
||||
}
|
||||
|
||||
&.ord-right {
|
||||
right: -($half-handle-shortside)-1px;
|
||||
}
|
||||
}
|
408
lama_cleaner/app/src/components/Extender/Extender.tsx
Normal file
408
lama_cleaner/app/src/components/Extender/Extender.tsx
Normal file
@ -0,0 +1,408 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import {
|
||||
extenderX,
|
||||
extenderY,
|
||||
extenderHeight,
|
||||
extenderWidth,
|
||||
isInpaintingState,
|
||||
} from '../../store/Atoms'
|
||||
|
||||
const DOC_MOVE_OPTS = { capture: true, passive: false }
|
||||
|
||||
const DRAG_HANDLE_BORDER = 2
|
||||
const DRAG_HANDLE_SHORT = 12
|
||||
const DRAG_HANDLE_LONG = 40
|
||||
|
||||
interface EVData {
|
||||
initX: number
|
||||
initY: number
|
||||
initHeight: number
|
||||
initWidth: number
|
||||
startResizeX: number
|
||||
startResizeY: number
|
||||
ord: string // top/right/bottom/left
|
||||
}
|
||||
|
||||
interface Props {
|
||||
maxHeight: number
|
||||
maxWidth: number
|
||||
scale: number
|
||||
minHeight: number
|
||||
minWidth: number
|
||||
show: boolean
|
||||
}
|
||||
|
||||
const clamp = (
|
||||
newPos: number,
|
||||
newLength: number,
|
||||
oldPos: number,
|
||||
oldLength: number,
|
||||
minLength: number,
|
||||
maxLength: number
|
||||
) => {
|
||||
if (newPos !== oldPos && newLength === oldLength) {
|
||||
if (newPos < 0) {
|
||||
return [0, oldLength]
|
||||
}
|
||||
if (newPos + newLength > maxLength) {
|
||||
return [maxLength - oldLength, oldLength]
|
||||
}
|
||||
} else {
|
||||
if (newLength < minLength) {
|
||||
if (newPos === oldPos) {
|
||||
return [newPos, minLength]
|
||||
}
|
||||
return [newPos + newLength - minLength, minLength]
|
||||
}
|
||||
if (newPos < 0) {
|
||||
return [0, newPos + newLength]
|
||||
}
|
||||
if (newPos + newLength > maxLength) {
|
||||
return [newPos, maxLength - newPos]
|
||||
}
|
||||
}
|
||||
|
||||
return [newPos, newLength]
|
||||
}
|
||||
|
||||
const Extender = (props: Props) => {
|
||||
const { minHeight, minWidth, maxHeight, maxWidth, scale, show } = props
|
||||
const [x, setX] = useRecoilState(extenderX)
|
||||
const [y, setY] = useRecoilState(extenderY)
|
||||
const [height, setHeight] = useRecoilState(extenderHeight)
|
||||
const [width, setWidth] = useRecoilState(extenderWidth)
|
||||
const isInpainting = useRecoilValue(isInpaintingState)
|
||||
|
||||
const [isResizing, setIsResizing] = useState(false)
|
||||
const [isMoving, setIsMoving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
setX(Math.round((maxWidth - 512) / 2))
|
||||
setY(Math.round((maxHeight - 512) / 2))
|
||||
}, [maxHeight, maxWidth])
|
||||
|
||||
const [evData, setEVData] = useState<EVData>({
|
||||
initX: 0,
|
||||
initY: 0,
|
||||
initHeight: 0,
|
||||
initWidth: 0,
|
||||
startResizeX: 0,
|
||||
startResizeY: 0,
|
||||
ord: 'top',
|
||||
})
|
||||
|
||||
const onDragFocus = () => {
|
||||
console.log('focus')
|
||||
}
|
||||
|
||||
const clampLeftRight = (newX: number, newWidth: number) => {
|
||||
return clamp(newX, newWidth, x, width, minWidth, maxWidth)
|
||||
}
|
||||
|
||||
const clampTopBottom = (newY: number, newHeight: number) => {
|
||||
return clamp(newY, newHeight, y, height, minHeight, maxHeight)
|
||||
}
|
||||
|
||||
const onPointerMove = (e: PointerEvent) => {
|
||||
if (isInpainting) {
|
||||
return
|
||||
}
|
||||
const curX = e.clientX
|
||||
const curY = e.clientY
|
||||
|
||||
const offsetY = Math.round((curY - evData.startResizeY) / scale)
|
||||
const offsetX = Math.round((curX - evData.startResizeX) / scale)
|
||||
|
||||
const moveTop = () => {
|
||||
const newHeight = evData.initHeight - offsetY
|
||||
const newY = evData.initY + offsetY
|
||||
const [clampedY, clampedHeight] = clampTopBottom(newY, newHeight)
|
||||
setHeight(clampedHeight)
|
||||
setY(clampedY)
|
||||
}
|
||||
|
||||
const moveBottom = () => {
|
||||
const newHeight = evData.initHeight + offsetY
|
||||
const [clampedY, clampedHeight] = clampTopBottom(evData.initY, newHeight)
|
||||
setHeight(clampedHeight)
|
||||
setY(clampedY)
|
||||
}
|
||||
|
||||
const moveLeft = () => {
|
||||
const newWidth = evData.initWidth - offsetX
|
||||
const newX = evData.initX + offsetX
|
||||
const [clampedX, clampedWidth] = clampLeftRight(newX, newWidth)
|
||||
setWidth(clampedWidth)
|
||||
setX(clampedX)
|
||||
}
|
||||
|
||||
const moveRight = () => {
|
||||
const newWidth = evData.initWidth + offsetX
|
||||
const [clampedX, clampedWidth] = clampLeftRight(evData.initX, newWidth)
|
||||
setWidth(clampedWidth)
|
||||
setX(clampedX)
|
||||
}
|
||||
|
||||
if (isResizing) {
|
||||
switch (evData.ord) {
|
||||
case 'topleft': {
|
||||
moveTop()
|
||||
moveLeft()
|
||||
break
|
||||
}
|
||||
case 'topright': {
|
||||
moveTop()
|
||||
moveRight()
|
||||
break
|
||||
}
|
||||
case 'bottomleft': {
|
||||
moveBottom()
|
||||
moveLeft()
|
||||
break
|
||||
}
|
||||
case 'bottomright': {
|
||||
moveBottom()
|
||||
moveRight()
|
||||
break
|
||||
}
|
||||
case 'top': {
|
||||
moveTop()
|
||||
break
|
||||
}
|
||||
case 'right': {
|
||||
moveRight()
|
||||
break
|
||||
}
|
||||
case 'bottom': {
|
||||
moveBottom()
|
||||
break
|
||||
}
|
||||
case 'left': {
|
||||
moveLeft()
|
||||
break
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (isMoving) {
|
||||
const newX = evData.initX + offsetX
|
||||
const newY = evData.initY + offsetY
|
||||
const [clampedX, clampedWidth] = clampLeftRight(newX, evData.initWidth)
|
||||
const [clampedY, clampedHeight] = clampTopBottom(newY, evData.initHeight)
|
||||
setWidth(clampedWidth)
|
||||
setHeight(clampedHeight)
|
||||
setX(clampedX)
|
||||
setY(clampedY)
|
||||
}
|
||||
}
|
||||
|
||||
const onPointerDone = (e: PointerEvent) => {
|
||||
if (isResizing) {
|
||||
setIsResizing(false)
|
||||
}
|
||||
|
||||
if (isMoving) {
|
||||
setIsMoving(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isResizing || isMoving) {
|
||||
document.addEventListener('pointermove', onPointerMove, DOC_MOVE_OPTS)
|
||||
document.addEventListener('pointerup', onPointerDone, DOC_MOVE_OPTS)
|
||||
document.addEventListener('pointercancel', onPointerDone, DOC_MOVE_OPTS)
|
||||
return () => {
|
||||
document.removeEventListener(
|
||||
'pointermove',
|
||||
onPointerMove,
|
||||
DOC_MOVE_OPTS
|
||||
)
|
||||
document.removeEventListener('pointerup', onPointerDone, DOC_MOVE_OPTS)
|
||||
document.removeEventListener(
|
||||
'pointercancel',
|
||||
onPointerDone,
|
||||
DOC_MOVE_OPTS
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [isResizing, isMoving, width, height, evData])
|
||||
|
||||
const onCropPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
const { ord } = (e.target as HTMLElement).dataset
|
||||
if (ord) {
|
||||
setIsResizing(true)
|
||||
setEVData({
|
||||
initX: x,
|
||||
initY: y,
|
||||
initHeight: height,
|
||||
initWidth: width,
|
||||
startResizeX: e.clientX,
|
||||
startResizeY: e.clientY,
|
||||
ord,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const createCropSelection = () => {
|
||||
return (
|
||||
<div
|
||||
className="drag-elements"
|
||||
onFocus={onDragFocus}
|
||||
onPointerDown={onCropPointerDown}
|
||||
>
|
||||
<div
|
||||
className="drag-bar ord-top"
|
||||
data-ord="top"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
<div
|
||||
className="drag-bar ord-right"
|
||||
data-ord="right"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
<div
|
||||
className="drag-bar ord-bottom"
|
||||
data-ord="bottom"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
<div
|
||||
className="drag-bar ord-left"
|
||||
data-ord="left"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="drag-handle ord-topleft"
|
||||
data-ord="topleft"
|
||||
aria-label="topleft"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="drag-handle ord-topright"
|
||||
data-ord="topright"
|
||||
aria-label="topright"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="drag-handle ord-bottomleft"
|
||||
data-ord="bottomleft"
|
||||
aria-label="bottomleft"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="drag-handle ord-bottomright"
|
||||
data-ord="bottomright"
|
||||
aria-label="bottomright"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="drag-handle ord-top"
|
||||
data-ord="top"
|
||||
aria-label="top"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
<div
|
||||
className="drag-handle ord-right"
|
||||
data-ord="right"
|
||||
aria-label="right"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
<div
|
||||
className="drag-handle ord-bottom"
|
||||
data-ord="bottom"
|
||||
aria-label="bottom"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
<div
|
||||
className="drag-handle ord-left"
|
||||
data-ord="left"
|
||||
aria-label="left"
|
||||
tabIndex={0}
|
||||
role="button"
|
||||
style={{ transform: `scale(${1 / scale})` }}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const onInfoBarPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
|
||||
setIsMoving(true)
|
||||
setEVData({
|
||||
initX: x,
|
||||
initY: y,
|
||||
initHeight: height,
|
||||
initWidth: width,
|
||||
startResizeX: e.clientX,
|
||||
startResizeY: e.clientY,
|
||||
ord: '',
|
||||
})
|
||||
}
|
||||
|
||||
const createInfoBar = () => {
|
||||
return (
|
||||
<div
|
||||
className="info-bar"
|
||||
onPointerDown={onInfoBarPointerDown}
|
||||
style={{
|
||||
transform: `scale(${1 / scale})`,
|
||||
top: `${10 / scale}px`,
|
||||
left: `${10 / scale}px`,
|
||||
}}
|
||||
>
|
||||
<div className="crop-size">
|
||||
{width} x {height}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const createBorder = () => {
|
||||
return (
|
||||
<div
|
||||
className="extender-border"
|
||||
style={{
|
||||
height,
|
||||
width,
|
||||
outlineWidth: `${DRAG_HANDLE_BORDER / scale}px`,
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="extender-wrapper"
|
||||
style={{ visibility: show ? 'visible' : 'hidden' }}
|
||||
>
|
||||
<div className="extender" style={{ height, width, left: x, top: y }}>
|
||||
{createBorder()}
|
||||
{createInfoBar()}
|
||||
{createCropSelection()}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Extender
|
@ -1,5 +1,6 @@
|
||||
import React, { ReactNode, useEffect, useState } from 'react'
|
||||
import React, { ReactNode, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import * as Tabs from '@radix-ui/react-tabs'
|
||||
import {
|
||||
AIModel,
|
||||
CV2Flag,
|
||||
@ -9,13 +10,21 @@ import {
|
||||
import Selector from '../shared/Selector'
|
||||
import { Switch, SwitchThumb } from '../shared/Switch'
|
||||
import Tooltip from '../shared/Tooltip'
|
||||
import { LDMSampler } from './HDSettingBlock'
|
||||
import HDSettingBlock, { LDMSampler } from './HDSettingBlock'
|
||||
import NumberInputSetting from './NumberInputSetting'
|
||||
import SettingBlock from './SettingBlock'
|
||||
import Flex from '../shared/Layout'
|
||||
import ManualRunInpaintingSettingBlock from './ManualRunInpaintingSettingBlock'
|
||||
|
||||
const ERASE_TAB = 'erase_tab'
|
||||
const INPAINTING_DIFFUSION_TAB = 'inpainting_diffusion_tab'
|
||||
const DIFFUSION_TAB = 'diffusion_tab'
|
||||
const OTHER_TAB = 'other_tab'
|
||||
|
||||
function ModelSettingBlock() {
|
||||
const [setting, setSettingState] = useRecoilState(settingState)
|
||||
const isDisableModelSwitch = useRecoilValue(isDisableModelSwitchState)
|
||||
const [tab, setTab] = useState(ERASE_TAB)
|
||||
|
||||
const onModelChange = (value: AIModel) => {
|
||||
setSettingState(old => {
|
||||
@ -190,112 +199,63 @@ function ModelSettingBlock() {
|
||||
}
|
||||
}
|
||||
|
||||
const renderPaperCodeBadge = (): ReactNode => {
|
||||
switch (setting.model) {
|
||||
case AIModel.LAMA:
|
||||
return renderModelDesc(
|
||||
'Resolution-robust Large Mask Inpainting with Fourier Convolutions',
|
||||
'https://arxiv.org/abs/2109.07161',
|
||||
'https://github.com/saic-mdal/lama'
|
||||
)
|
||||
case AIModel.LDM:
|
||||
return renderModelDesc(
|
||||
'High-Resolution Image Synthesis with Latent Diffusion Models',
|
||||
'https://arxiv.org/abs/2112.10752',
|
||||
'https://github.com/CompVis/latent-diffusion'
|
||||
)
|
||||
case AIModel.ZITS:
|
||||
return renderModelDesc(
|
||||
'Incremental Transformer Structure Enhanced Image Inpainting with Masking Positional Encoding',
|
||||
'https://arxiv.org/abs/2203.00867',
|
||||
'https://github.com/DQiaole/ZITS_inpainting'
|
||||
)
|
||||
case AIModel.MAT:
|
||||
return renderModelDesc(
|
||||
'Mask-Aware Transformer for Large Hole Image Inpainting',
|
||||
'https://arxiv.org/abs/2203.15270',
|
||||
'https://github.com/fenglinglwb/MAT'
|
||||
)
|
||||
case AIModel.FCF:
|
||||
return renderModelDesc(
|
||||
'Keys to Better Image Inpainting: Structure and Texture Go Hand in Hand',
|
||||
'https://arxiv.org/abs/2208.03382',
|
||||
'https://github.com/SHI-Labs/FcF-Inpainting'
|
||||
)
|
||||
case AIModel.SD15:
|
||||
return renderModelDesc(
|
||||
'Stable Diffusion 1.5',
|
||||
'https://ommer-lab.com/research/latent-diffusion-models/',
|
||||
'https://github.com/CompVis/stable-diffusion'
|
||||
)
|
||||
case AIModel.ANYTHING4:
|
||||
return renderModelDesc(
|
||||
'andite/anything-v4.0',
|
||||
'https://huggingface.co/andite/anything-v4.0',
|
||||
'https://huggingface.co/andite/anything-v4.0'
|
||||
)
|
||||
case AIModel.REALISTIC_VISION_1_4:
|
||||
return renderModelDesc(
|
||||
'SG161222/Realistic_Vision_V1.4',
|
||||
'https://huggingface.co/SG161222/Realistic_Vision_V1.4',
|
||||
'https://huggingface.co/SG161222/Realistic_Vision_V1.4'
|
||||
)
|
||||
case AIModel.SD2:
|
||||
return renderModelDesc(
|
||||
'Stable Diffusion 2',
|
||||
'https://ommer-lab.com/research/latent-diffusion-models/',
|
||||
'https://github.com/Stability-AI/stablediffusion'
|
||||
)
|
||||
case AIModel.Mange:
|
||||
return renderModelDesc(
|
||||
'Manga Inpainting',
|
||||
'https://www.cse.cuhk.edu.hk/~ttwong/papers/mangainpaint/mangainpaint.html',
|
||||
'https://github.com/msxie92/MangaInpainting'
|
||||
)
|
||||
case AIModel.CV2:
|
||||
return renderModelDesc(
|
||||
'OpenCV Image Inpainting',
|
||||
'https://docs.opencv.org/4.6.0/df/d3d/tutorial_py_inpainting.html',
|
||||
'https://docs.opencv.org/4.6.0/df/d3d/tutorial_py_inpainting.html'
|
||||
)
|
||||
case AIModel.PAINT_BY_EXAMPLE:
|
||||
return renderModelDesc(
|
||||
'Paint by Example',
|
||||
'https://arxiv.org/abs/2211.13227',
|
||||
'https://github.com/Fantasy-Studio/Paint-by-Example'
|
||||
)
|
||||
case AIModel.PIX2PIX:
|
||||
return renderModelDesc(
|
||||
'InstructPix2Pix',
|
||||
'https://arxiv.org/abs/2211.09800',
|
||||
'https://github.com/timothybrooks/instruct-pix2pix'
|
||||
)
|
||||
case AIModel.KANDINSKY21:
|
||||
return renderModelDesc(
|
||||
'Kandinsky 2.1',
|
||||
'https://huggingface.co/kandinsky-community/kandinsky-2-1-inpaint',
|
||||
'https://huggingface.co/kandinsky-community/kandinsky-2-1-inpaint'
|
||||
)
|
||||
default:
|
||||
return <></>
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingBlock
|
||||
className="model-setting-block"
|
||||
title="Model"
|
||||
titleSuffix={renderPaperCodeBadge()}
|
||||
input={
|
||||
<Selector
|
||||
value={setting.model as string}
|
||||
options={Object.values(AIModel)}
|
||||
onChange={val => onModelChange(val as AIModel)}
|
||||
disabled={isDisableModelSwitch}
|
||||
/>
|
||||
}
|
||||
optionDesc={renderOptionDesc()}
|
||||
/>
|
||||
// <SettingBlock
|
||||
// className="model-setting-block"
|
||||
// title="Model"
|
||||
// input={
|
||||
// <Selector
|
||||
// value={setting.model as string}
|
||||
// options={Object.values(AIModel)}
|
||||
// onChange={val => onModelChange(val as AIModel)}
|
||||
// disabled={isDisableModelSwitch}
|
||||
// />
|
||||
// }
|
||||
// optionDesc={renderOptionDesc()}
|
||||
// />
|
||||
<Flex
|
||||
style={{
|
||||
justifyContent: 'start',
|
||||
flexDirection: 'column',
|
||||
gap: 10,
|
||||
alignItems: 'start',
|
||||
}}
|
||||
>
|
||||
<Flex style={{ justifyContent: 'space-between', width: '100%' }}>
|
||||
<div>Current Model</div>
|
||||
<div>{setting.model}</div>
|
||||
</Flex>
|
||||
<Tabs.Root
|
||||
className="TabsRoot"
|
||||
defaultValue={ERASE_TAB}
|
||||
onValueChange={val => setTab(val)}
|
||||
>
|
||||
<Tabs.List className="TabsList" aria-label="Object remove model">
|
||||
<Tabs.Trigger className="TabsTrigger" value={ERASE_TAB}>
|
||||
Erase
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
className="TabsTrigger"
|
||||
value={INPAINTING_DIFFUSION_TAB}
|
||||
>
|
||||
Inpainting Diffusion
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger className="TabsTrigger" value={DIFFUSION_TAB}>
|
||||
Diffusion
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger className="TabsTrigger" value={OTHER_TAB}>
|
||||
Other
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
<Tabs.Content value={ERASE_TAB}>
|
||||
<ManualRunInpaintingSettingBlock />
|
||||
<HDSettingBlock />
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value={INPAINTING_DIFFUSION_TAB}>123</Tabs.Content>
|
||||
<Tabs.Content value={DIFFUSION_TAB}>123</Tabs.Content>
|
||||
<Tabs.Content value={OTHER_TAB}>123</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
</Flex>
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,6 @@ interface SettingModalProps {
|
||||
export default function SettingModal(props: SettingModalProps) {
|
||||
const { onClose } = props
|
||||
const [setting, setSettingState] = useRecoilState(settingState)
|
||||
const isSD = useRecoilValue(isSDState)
|
||||
const isDiffusionModels = useRecoilValue(isDiffusionModelsState)
|
||||
|
||||
const handleOnClose = () => {
|
||||
@ -57,9 +56,8 @@ export default function SettingModal(props: SettingModalProps) {
|
||||
show={setting.show}
|
||||
>
|
||||
<DownloadMaskSettingBlock />
|
||||
{isDiffusionModels ? <></> : <ManualRunInpaintingSettingBlock />}
|
||||
<ManualRunInpaintingSettingBlock />
|
||||
<ModelSettingBlock />
|
||||
{isDiffusionModels ? <></> : <HDSettingBlock />}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
@ -85,7 +85,7 @@
|
||||
}
|
||||
|
||||
.resize-title-tile {
|
||||
width: 86px;
|
||||
width: 60px;
|
||||
font-size: 0.5rem;
|
||||
color: var(--text-color-gray);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import React, { FormEvent } from 'react'
|
||||
import React, { FormEvent, useState } from 'react'
|
||||
import { useRecoilState, useRecoilValue } from 'recoil'
|
||||
import * as PopoverPrimitive from '@radix-ui/react-popover'
|
||||
import * as Tabs from '@radix-ui/react-tabs'
|
||||
import { useToggle } from 'react-use'
|
||||
import {
|
||||
ControlNetMethod,
|
||||
@ -10,6 +11,8 @@ import {
|
||||
propmtState,
|
||||
SDSampler,
|
||||
settingState,
|
||||
SIDE_PANEL_TAB,
|
||||
SIDE_PANEL_TAB_TYPE,
|
||||
} from '../../store/Atoms'
|
||||
import NumberInputSetting from '../Settings/NumberInputSetting'
|
||||
import SettingBlock from '../Settings/SettingBlock'
|
||||
@ -23,6 +26,7 @@ const INPUT_WIDTH = 30
|
||||
|
||||
const SidePanel = () => {
|
||||
const [open, toggleOpen] = useToggle(true)
|
||||
const [tab, setTab] = useState<SIDE_PANEL_TAB_TYPE>(SIDE_PANEL_TAB.inpainting)
|
||||
const [setting, setSettingState] = useRecoilState(settingState)
|
||||
const [negativePrompt, setNegativePrompt] =
|
||||
useRecoilState(negativePropmtState)
|
||||
@ -95,27 +99,58 @@ const SidePanel = () => {
|
||||
>
|
||||
Config
|
||||
</PopoverPrimitive.Trigger>
|
||||
|
||||
<PopoverPrimitive.Portal>
|
||||
<PopoverPrimitive.Content className="side-panel-content">
|
||||
{isControlNet && renderConterNetSetting()}
|
||||
|
||||
<SettingBlock
|
||||
title="Croper"
|
||||
input={
|
||||
<Switch
|
||||
checked={setting.showCroper}
|
||||
onCheckedChange={value => {
|
||||
setSettingState(old => {
|
||||
return { ...old, showCroper: value }
|
||||
})
|
||||
}}
|
||||
<Tabs.Root
|
||||
className="TabsRoot"
|
||||
style={{ alignSelf: 'center' }}
|
||||
defaultValue={tab}
|
||||
onValueChange={val => setTab(val as SIDE_PANEL_TAB_TYPE)}
|
||||
>
|
||||
<Tabs.List className="TabsList">
|
||||
<Tabs.Trigger
|
||||
className="TabsTrigger"
|
||||
value={SIDE_PANEL_TAB.inpainting}
|
||||
>
|
||||
<SwitchThumb />
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
In Painting
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger
|
||||
className="TabsTrigger"
|
||||
value={SIDE_PANEL_TAB.outpainting}
|
||||
>
|
||||
Out Painting
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</Tabs.Root>
|
||||
|
||||
<ImageResizeScale />
|
||||
{isControlNet && tab === SIDE_PANEL_TAB.inpainting ? (
|
||||
renderConterNetSetting()
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{tab === SIDE_PANEL_TAB.inpainting ? (
|
||||
<SettingBlock
|
||||
title="Croper"
|
||||
input={
|
||||
<Switch
|
||||
checked={setting.showCroper}
|
||||
onCheckedChange={value => {
|
||||
setSettingState(old => {
|
||||
return { ...old, showCroper: value }
|
||||
})
|
||||
}}
|
||||
>
|
||||
<SwitchThumb />
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
{tab === SIDE_PANEL_TAB.inpainting ? <ImageResizeScale /> : <></>}
|
||||
|
||||
{/*
|
||||
<NumberInputSetting
|
||||
@ -158,35 +193,43 @@ const SidePanel = () => {
|
||||
}}
|
||||
/>
|
||||
|
||||
<NumberInputSetting
|
||||
title="Mask Blur"
|
||||
width={INPUT_WIDTH}
|
||||
value={`${setting.sdMaskBlur}`}
|
||||
desc="Blur the edge of mask area. The higher the number the smoother blend with the original image"
|
||||
onValue={value => {
|
||||
const val = value.length === 0 ? 0 : parseInt(value, 10)
|
||||
setSettingState(old => {
|
||||
return { ...old, sdMaskBlur: val }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
{tab === SIDE_PANEL_TAB.inpainting ? (
|
||||
<NumberInputSetting
|
||||
title="Mask Blur"
|
||||
width={INPUT_WIDTH}
|
||||
value={`${setting.sdMaskBlur}`}
|
||||
desc="Blur the edge of mask area. The higher the number the smoother blend with the original image"
|
||||
onValue={value => {
|
||||
const val = value.length === 0 ? 0 : parseInt(value, 10)
|
||||
setSettingState(old => {
|
||||
return { ...old, sdMaskBlur: val }
|
||||
})
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<SettingBlock
|
||||
title="Match Histograms"
|
||||
desc="Match the inpainting result histogram to the source image histogram, will improves the inpainting quality for some images."
|
||||
input={
|
||||
<Switch
|
||||
checked={setting.sdMatchHistograms}
|
||||
onCheckedChange={value => {
|
||||
setSettingState(old => {
|
||||
return { ...old, sdMatchHistograms: value }
|
||||
})
|
||||
}}
|
||||
>
|
||||
<SwitchThumb />
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
{tab === SIDE_PANEL_TAB.outpainting ? (
|
||||
<SettingBlock
|
||||
title="Match Histograms"
|
||||
desc="Match the inpainting result histogram to the source image histogram, will improves the inpainting quality for some images."
|
||||
input={
|
||||
<Switch
|
||||
checked={setting.sdMatchHistograms}
|
||||
onCheckedChange={value => {
|
||||
setSettingState(old => {
|
||||
return { ...old, sdMatchHistograms: value }
|
||||
})
|
||||
}}
|
||||
>
|
||||
<SwitchThumb />
|
||||
</Switch>
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
|
||||
<SettingBlock
|
||||
className="sub-setting-block"
|
||||
|
@ -3,6 +3,13 @@ import _ from 'lodash'
|
||||
import { HDStrategy, LDMSampler } from '../components/Settings/HDSettingBlock'
|
||||
import { ToastState } from '../components/shared/Toast'
|
||||
|
||||
function strEnum<T extends string>(o: Array<T>): { [K in T]: K } {
|
||||
return o.reduce((res, key) => {
|
||||
res[key] = key
|
||||
return res
|
||||
}, Object.create(null))
|
||||
}
|
||||
|
||||
export enum AIModel {
|
||||
LAMA = 'lama',
|
||||
LDM = 'ldm',
|
||||
@ -17,7 +24,7 @@ export enum AIModel {
|
||||
Mange = 'manga',
|
||||
PAINT_BY_EXAMPLE = 'paint_by_example',
|
||||
PIX2PIX = 'instruct_pix2pix',
|
||||
KANDINSKY21 = 'kandinsky2.1',
|
||||
KANDINSKY22 = 'kandinsky2.2',
|
||||
}
|
||||
|
||||
export enum ControlNetMethod {
|
||||
@ -351,6 +358,20 @@ export const croperState = atom<Rect>({
|
||||
},
|
||||
})
|
||||
|
||||
export const SIDE_PANEL_TAB = strEnum(['inpainting', 'outpainting'])
|
||||
export type SIDE_PANEL_TAB_TYPE = keyof typeof SIDE_PANEL_TAB
|
||||
|
||||
export interface SidePanelState {
|
||||
tab: SIDE_PANEL_TAB_TYPE
|
||||
}
|
||||
|
||||
export const sidePanelTabState = atom<SidePanelState>({
|
||||
key: 'sidePanelTabState',
|
||||
default: {
|
||||
tab: SIDE_PANEL_TAB.inpainting,
|
||||
},
|
||||
})
|
||||
|
||||
export const croperX = selector({
|
||||
key: 'croperX',
|
||||
get: ({ get }) => get(croperState).x,
|
||||
@ -387,6 +408,52 @@ export const croperWidth = selector({
|
||||
},
|
||||
})
|
||||
|
||||
export const extenderState = atom<Rect>({
|
||||
key: 'extenderState',
|
||||
default: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 512,
|
||||
height: 512,
|
||||
},
|
||||
})
|
||||
|
||||
export const extenderX = selector({
|
||||
key: 'extenderX',
|
||||
get: ({ get }) => get(extenderState).x,
|
||||
set: ({ get, set }, newValue: any) => {
|
||||
const rect = get(extenderState)
|
||||
set(extenderState, { ...rect, x: newValue })
|
||||
},
|
||||
})
|
||||
|
||||
export const extenderY = selector({
|
||||
key: 'extenderY',
|
||||
get: ({ get }) => get(extenderState).y,
|
||||
set: ({ get, set }, newValue: any) => {
|
||||
const rect = get(extenderState)
|
||||
set(extenderState, { ...rect, y: newValue })
|
||||
},
|
||||
})
|
||||
|
||||
export const extenderHeight = selector({
|
||||
key: 'extenderHeight',
|
||||
get: ({ get }) => get(extenderState).height,
|
||||
set: ({ get, set }, newValue: any) => {
|
||||
const rect = get(extenderState)
|
||||
set(extenderState, { ...rect, height: newValue })
|
||||
},
|
||||
})
|
||||
|
||||
export const extenderWidth = selector({
|
||||
key: 'extenderWidth',
|
||||
get: ({ get }) => get(extenderState).width,
|
||||
set: ({ get, set }, newValue: any) => {
|
||||
const rect = get(extenderState)
|
||||
set(extenderState, { ...rect, width: newValue })
|
||||
},
|
||||
})
|
||||
|
||||
interface ToastAtomState {
|
||||
open: boolean
|
||||
desc: string
|
||||
@ -567,7 +634,7 @@ const defaultHDSettings: ModelsHDSettings = {
|
||||
hdStrategyCropMargin: 128,
|
||||
enabled: true,
|
||||
},
|
||||
[AIModel.KANDINSKY21]: {
|
||||
[AIModel.KANDINSKY22]: {
|
||||
hdStrategy: HDStrategy.ORIGINAL,
|
||||
hdStrategyResizeLimit: 768,
|
||||
hdStrategyCropTrigerSize: 512,
|
||||
@ -728,7 +795,7 @@ export const isSDState = selector({
|
||||
settings.model === AIModel.SD2 ||
|
||||
settings.model === AIModel.ANYTHING4 ||
|
||||
settings.model === AIModel.REALISTIC_VISION_1_4 ||
|
||||
settings.model === AIModel.KANDINSKY21
|
||||
settings.model === AIModel.KANDINSKY22
|
||||
)
|
||||
},
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user