add thumbnail functionality to CtrlNet annotated image

edit_245
Abdullah Alfaraj 2023-06-09 05:11:11 +03:00
parent 95fa58cb6a
commit b3f94ab4f0
5 changed files with 314 additions and 9 deletions

36
icon/move_to_canvas.svg Normal file
View File

@ -0,0 +1,36 @@
<svg
version="1.0"
xmlns="http://www.w3.org/2000/svg"
width="18.000000pt"
height="18.000000pt"
viewBox="0 0 127.000000 127.000000"
preserveAspectRatio="xMidYMid meet"
style="
width: 32px;
height: 27px;
position: absolute;
top: 3px;
left: -1px;
"
>
<g
transform="translate(0.000000,115.000000) scale(0.100000,-0.100000)"
fill="#000000"
stroke="none"
>
<path
d="M577 1172 c-10 -11 -17 -36 -17 -60 l0 -42 -133 0 c-117 0 -136 -2
-150 -18 -15 -16 -17 -49 -17 -254 l0 -236 -24 -6 c-53 -13 -74 -67 -38 -99
15 -13 39 -17 115 -17 53 0 97 -4 97 -9 0 -5 -13 -69 -30 -143 -35 -155 -36
-169 -12 -191 45 -41 84 -11 104 78 l13 60 150 0 150 0 13 -60 c15 -67 35 -95
67 -95 27 0 55 29 55 56 0 11 -13 78 -30 150 -16 71 -30 135 -30 142 0 9 26
12 98 12 83 0 102 3 115 18 33 36 14 85 -39 98 l-24 6 0 238 c0 217 -2 238
-18 253 -15 14 -42 17 -150 17 l-132 0 0 43 c0 30 -6 49 -18 60 -26 23 -94 22
-115 -1z m393 -377 l0 -235 -335 0 -335 0 0 235 0 235 335 0 335 0 0 -235z
m77 -291 c3 -9 0 -20 -8 -25 -18 -11 -790 -11 -808 0 -8 5 -11 16 -8 25 6 14
49 16 412 16 363 0 406 -2 412 -16z m-293 -133 c9 -39 16 -75 16 -80 0 -7 -47
-11 -135 -11 -88 0 -135 4 -135 11 0 5 7 41 16 80 l16 69 103 0 103 0 16 -69z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

9
icon/pen.svg Normal file
View File

@ -0,0 +1,9 @@
<svg
viewBox="0 0 36 36"
style="width: 18px; height: 18px"
>
<path
d="M33.567 8.2L27.8 2.432a1.215 1.215 0 0 0-.866-.353H26.9a1.371 1.371 0 0 0-.927.406L5.084 23.372a.99.99 0 0 0-.251.422L2.055 33.1c-.114.377.459.851.783.851a.251.251 0 0 0 .062-.007c.276-.063 7.866-2.344 9.311-2.778a.972.972 0 0 0 .414-.249l20.888-20.889a1.372 1.372 0 0 0 .4-.883 1.221 1.221 0 0 0-.346-.945zM11.4 29.316c-2.161.649-4.862 1.465-6.729 2.022l2.009-6.73z"
/>
</svg>

After

Width:  |  Height:  |  Size: 729 B

56
icon/preview.svg Normal file
View File

@ -0,0 +1,56 @@
<svg
version="1.0"
xmlns="http://www.w3.org/2000/svg"
width="512.000000pt"
height="512.000000pt"
viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet"
style="
width: 32px;
height: 32px;
position: absolute;
top: 0px;
left: -1px;
"
>
<g
transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="#000000"
stroke="none"
>
<path
d="M920 4311 c-118 -36 -212 -129 -259 -254 -18 -50 -21 -79 -21 -217
l0 -160 80 0 80 0 0 128 c0 147 14 208 60 264 51 62 99 78 231 78 63 0 131 3
152 6 l37 7 0 78 0 79 -167 -1 c-93 0 -179 -4 -193 -8z"
/>
<path
d="M3840 4242 l0 -79 38 -7 c20 -3 88 -6 151 -6 132 0 180 -16 231 -78
46 -56 60 -117 60 -264 l0 -128 80 0 80 0 0 158 c0 131 -3 169 -19 215 -36
104 -104 182 -204 234 -50 27 -58 28 -234 31 l-183 4 0 -80z"
/>
<path
d="M2450 3404 c-252 -30 -471 -107 -694 -243 -199 -122 -420 -320 -578
-519 -32 -40 -58 -79 -58 -85 0 -20 105 -146 213 -256 296 -302 597 -483 937
-562 122 -29 364 -37 495 -16 318 51 613 200 903 457 102 90 249 249 310 334
l32 44 -47 62 c-67 90 -253 281 -358 368 -248 208 -538 351 -802 397 -90 16
-290 27 -353 19z m279 -317 c189 -54 340 -214 386 -409 79 -338 -192 -670
-549 -669 -158 0 -278 47 -390 151 -123 116 -177 238 -178 400 0 161 54 284
178 400 153 143 345 187 553 127z"
/>
<path
d="M2485 2841 c-59 -17 -86 -31 -129 -71 -153 -141 -103 -397 92 -471
116 -43 262 -7 333 82 46 59 62 106 63 184 0 52 -6 82 -23 117 -27 55 -94 121
-148 143 -45 19 -146 28 -188 16z"
/>
<path
d="M640 1280 c0 -138 3 -167 21 -217 40 -107 103 -178 202 -230 50 -27
58 -28 235 -31 l182 -4 0 80 0 79 -37 7 c-21 3 -89 6 -152 6 -132 0 -180 16
-231 78 -46 56 -60 117 -60 264 l0 128 -80 0 -80 0 0 -160z"
/>
<path
d="M4320 1312 c0 -147 -14 -208 -60 -264 -51 -62 -99 -78 -231 -78 -63
0 -131 -3 -151 -6 l-38 -7 0 -79 0 -80 183 4 c176 3 184 4 234 31 99 52 162
123 202 230 18 50 21 79 21 217 l0 160 -80 0 -80 0 0 -128z"
/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@ -1,13 +1,15 @@
import { observer } from 'mobx-react';
import React from 'react';
import { SpCheckBox, SpMenu, SpSlider } from '../util/elements';
import { MoveToCanvasSvg,ActionButtonSVG, SpCheckBox, SpMenu, SpSlider, Thumbnail, PenSvg, PreviewSvg } from '../util/elements';
import ControlNetStore from './store';
import { mapRange, versionCompare } from './util';
import { note, selection } from '../util/oldSystem';
import { note, selection, html_manip, psapi,api, general } from '../util/oldSystem';
declare const g_generation_session: any;
declare const io: any;
declare const app: any;
declare let g_sd_url: string;
@observer
@ -116,6 +118,159 @@ export default class ControlNetUnit extends React.Component<{ index: number, app
}
}
async requestControlNetDetectMap(
controlnet_init_image:string,
_module:string,
processor_res:number,
threshold_a:number,
threshold_b:number
) {
try {
const payload = {
controlnet_module: _module,
controlnet_input_images: [controlnet_init_image],
controlnet_processor_res: processor_res,
controlnet_threshold_a: threshold_a,
controlnet_threshold_b: threshold_b,
}
const full_url = `${g_sd_url}/controlnet/detect`
const response_data = await api.requestPost(full_url, payload)
// update the mask preview with the new detectMap
if (response_data['images'].length === 0) {
app.showAlert(response_data['info'])
}
return response_data['images'][0]
} catch (e) {
console.warn('requestControlNetDetectMap(): ', _module, e)
}
}
async previewAnnotator() {
const index = this.props.index
try {
const storeData = this.props.appState.controlNetUnitData[index];
const controlnet_init_image = storeData.input_image
const _module = storeData.module || 'none';
const processor_res = storeData.processor_res
const threshold_a = storeData.threshold_a
const threshold_b = storeData.threshold_b
if (!controlnet_init_image) {
const error = 'ControlNet initial image is empty'
app.showAlert(error)
throw error
}
if (!_module || _module === 'none') {
const error = 'select a valid controlnet module (preprocessor)'
app.showAlert(error)
throw error
}
const detect_map = await this.requestControlNetDetectMap(
controlnet_init_image,
_module,
processor_res,
threshold_a,
threshold_b
)
const rgb_detect_map_url =
await io.convertBlackAndWhiteImageToRGBChannels3(detect_map)
const rgb_detect_map = general.base64UrlToBase64(rgb_detect_map_url)
g_generation_session.controlNetMask[index] = rgb_detect_map
storeData.mask = rgb_detect_map
} catch (e) {
console.warn('PreviewAnnotator click(): index: ', index, e)
}
}
async toCanvas() {
if (
g_generation_session.control_net_preview_selection_info &&
g_generation_session.controlNetMask[this.props.index]
) {
const selection_info =
g_generation_session.control_net_preview_selection_info
const layer = await io.IO.base64ToLayer(
g_generation_session.controlNetMask[this.props.index],
'ControlNet Mask.png',
selection_info.left,
selection_info.top,
selection_info.width,
selection_info.height
)
} else {
// await note.Notification.inactiveSelectionArea()
app.showAlert('Mask Image is not available')
}
}
async toControlNetInitImage() {
const preview_result_base64 = g_generation_session.controlNetMask[this.props.index]
g_generation_session.controlNetImage[this.props.index] = preview_result_base64
g_generation_session.control_net_selection_info =
g_generation_session.control_net_preview_selection_info
const rgb_detect_map_url =
await io.convertBlackAndWhiteImageToRGBChannels3(
preview_result_base64
)
// g_generation_session.controlNetMask[index] = rgb_detect_map
// html_manip.setControlImageSrc(rgb_detect_map_url, this.props.index)
const storeData = this.props.appState.controlNetUnitData[this.props.index];
storeData.input_image = storeData.mask
}
async previewAnnotatorFromCanvas() {
try {
const storeData = this.props.appState.controlNetUnitData[this.props.index];
const _module = storeData.module || 'none';
const width = html_manip.getWidth()
const height = html_manip.getHeight()
const selectionInfo = await psapi.getSelectionInfoExe()
g_generation_session.control_net_preview_selection_info = selectionInfo
const base64 = await io.IO.getSelectionFromCanvasAsBase64Interface_New(
width,
height,
selectionInfo,
true
)
if (!_module || _module === 'none') {
const error = 'select a valid controlnet module (preprocessor)'
app.showAlert(error)
throw error
}
const processor_res = storeData.processor_res
const threshold_a = storeData.threshold_a
const threshold_b = storeData.threshold_b
const detect_map = await this.requestControlNetDetectMap(
base64,
_module,
processor_res,
threshold_a,
threshold_b
)
const rgb_detect_map_url =
await io.convertBlackAndWhiteImageToRGBChannels3(detect_map)
g_generation_session.controlNetMask[this.props.index] = detect_map
storeData.mask = general.base64UrlToBase64(rgb_detect_map_url)
} catch (e) {
console.warn('PreviewAnnotator click(): index: ', this.props.index, e)
}
}
render() {
const storeData = this.props.appState.controlNetUnitData[this.props.index];
const pd = this.props.appState.preprocessorDetail[storeData.module] || {};
@ -158,19 +313,26 @@ export default class ControlNetUnit extends React.Component<{ index: number, app
className="imgContainer controlNetImaageContainer"
>
<div>
<img
<Thumbnail>
<img
id={`control_net_mask_${this.props.index}`}
className="column-item-image"
src={storeData.mask ? 'data:image/png;base64,' + storeData.mask : "https://source.unsplash.com/random"}
width="300px"
height="100px"
/>
/>
<ActionButtonSVG onClick={this.toControlNetInitImage.bind(this)} ><PenSvg/></ActionButtonSVG>
<ActionButtonSVG onClick={this.toCanvas.bind(this)} ><MoveToCanvasSvg/></ActionButtonSVG>
<ActionButtonSVG onClick={this.previewAnnotatorFromCanvas.bind(this)} ><PreviewSvg/></ActionButtonSVG>
</Thumbnail>
</div>
<div className="imgButton btnClass">
<button
className="column-item button-style btnSquare"
id={`bControlMask_${this.props.index}`}
onClick={this.onMaskButtonClick.bind(this)}
onClick={this.previewAnnotator.bind(this)}
title="Preview Annotator"
>
Preview Annotator

View File

@ -1,7 +1,16 @@
import React, { ReactEventHandler, useState } from 'react'
import React, { CSSProperties, ReactEventHandler, useState } from 'react'
// import ReactDOM from 'react-dom'
import ReactDOM from 'react-dom/client'
// import { versions } from 'uxp'
export { ReactComponent as MoveToCanvasSvg } from '../../icon/move_to_canvas.svg';
export { ReactComponent as PenSvg } from '../../icon/pen.svg';
export { ReactComponent as PreviewSvg } from '../../icon/preview.svg';
declare global {
namespace JSX {
interface IntrinsicElements {
@ -16,6 +25,7 @@ declare global {
'sp-divider': any
'sp-detail': any
'sp-textarea': any
'sp-action-button': any
}
}
}
@ -174,7 +184,7 @@ export class SpMenu extends React.Component<{
id?: string
title?: string
style?: string
style?: CSSProperties
items?: string[]
disabled?: boolean[]
label_item?: string
@ -212,11 +222,11 @@ export class SpMenu extends React.Component<{
render() {
return (
<div>
<div style={this.props.style}>
<sp-picker
title={this.props.title}
size="m"
style={{ width: '199px', marginRight: '5px' }}
// style={{ width: '199px', marginRight: '5px' }}
>
<sp-menu id={this.props.id} slot="options">
{this.props.label_item && (
@ -314,6 +324,12 @@ export class SpPicker extends PhotoshopElem {
return <sp-picker ref={(elem: Element) => this.elem = elem} {...attr}></sp-picker>
}
}
export class SpMenuComponent extends PhotoshopElem {
render() {
const [attr] = this.splitProps(this.props)
return <sp-menu ref={(elem: Element) => this.elem = elem} {...attr}></sp-menu>
}
}
export class SpMenuItem extends PhotoshopElem {
render() {
const [attr] = this.splitProps(this.props)
@ -357,3 +373,29 @@ export class SpDivider extends PhotoshopElem {
return <sp-divider ref={(elem: Element) => this.elem = elem} {...attr}></sp-divider>
}
}
export class Thumbnail extends React.Component<{children:React.ReactNode}> {
render() {
return <div className='viewer-image-container'>
{this.props.children}
</div>
}
}
export class ActionButtonSVG extends PhotoshopElem {
render() {
const [attr] = this.splitProps(this.props)
return <sp-action-button style={{padding: 0, maxWidth: "32px",maxHeight: "32px" /* display: none; */}} class="thumbnail-image-button" ref={(elem: Element) => this.elem = elem} {...attr}>
<div slot="icon" style={{fill: 'currentColor'}}>
{this.props.children}
</div>
</sp-action-button>}}