diff --git a/backend/src/nodes/properties/inputs/numeric_inputs.py b/backend/src/nodes/properties/inputs/numeric_inputs.py index b44685d6e..011c54dc1 100644 --- a/backend/src/nodes/properties/inputs/numeric_inputs.py +++ b/backend/src/nodes/properties/inputs/numeric_inputs.py @@ -119,7 +119,7 @@ def __init__( ends: Tuple[Union[str, None], Union[str, None]] = (None, None), hide_trailing_zeros: bool = False, gradient: Union[List[str], None] = None, - scale: Literal["linear", "log", "log-offset"] = "linear", + scale: Literal["linear", "log", "log-offset", "sqrt"] = "linear", ): super().__init__( label, diff --git a/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py b/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py index 0d3c942e8..e0b1445e4 100644 --- a/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py +++ b/backend/src/packages/chaiNNer_standard/image_filter/blur/surface_blur.py @@ -3,7 +3,7 @@ import cv2 import numpy as np -from nodes.properties.inputs import ImageInput, NumberInput, SliderInput +from nodes.properties.inputs import ImageInput, SliderInput from nodes.properties.outputs import ImageOutput from nodes.utils.utils import get_h_w_c @@ -17,7 +17,14 @@ icon="MdBlurOn", inputs=[ ImageInput(), - NumberInput("Diameter", controls_step=1, default=12), + SliderInput( + "Radius", + minimum=0, + maximum=100, + default=4, + controls_step=1, + scale="sqrt", + ), SliderInput( "Color Sigma", controls_step=1, @@ -39,14 +46,15 @@ ) def surface_blur_node( img: np.ndarray, - diameter: int, + radius: int, sigma_color: int, sigma_space: int, ) -> np.ndarray: - if diameter == 0 or sigma_color == 0 or sigma_space == 0: + if radius == 0 or sigma_color == 0 or sigma_space == 0: return img sigma_color_adjusted = sigma_color / 255 + diameter = radius * 2 + 1 _, _, c = get_h_w_c(img) if c == 4: diff --git a/src/common/common-types.ts b/src/common/common-types.ts index ccd705a48..275291c47 100644 --- a/src/common/common-types.ts +++ b/src/common/common-types.ts @@ -117,7 +117,7 @@ export interface SliderInput extends InputBase { readonly ends: readonly [string | null, string | null]; readonly sliderStep: number; readonly gradient?: readonly string[] | null; - readonly scale: 'linear' | 'log' | 'log-offset'; + readonly scale: 'linear' | 'log' | 'log-offset' | 'sqrt'; } export interface ColorInput extends InputBase { readonly kind: 'color'; diff --git a/src/common/migrations.ts b/src/common/migrations.ts index 43fe2d1a1..6d1366991 100644 --- a/src/common/migrations.ts +++ b/src/common/migrations.ts @@ -1181,6 +1181,28 @@ const emptyStringInput: ModernMigration = (data) => { return data; }; +const surfaceBlurRadius: ModernMigration = (data) => { + const toRadius = (diameter: number): number => { + diameter = Math.round(diameter); + if (diameter <= 0) return 0; + if (diameter <= 3) return 1; + // d = 2r+1 + const r = Math.ceil((diameter - 1) / 2); + return Math.min(r, 100); + }; + + data.nodes.forEach((node) => { + if (node.data.schemaId === 'chainner:image:bilateral_blur') { + const diameter = node.data.inputData[1]; + if (typeof diameter === 'number') { + node.data.inputData[1] = toRadius(diameter); + } + } + }); + + return data; +}; + // ============== const versionToMigration = (version: string) => { @@ -1228,6 +1250,7 @@ const migrations = [ seedInput, createColor, emptyStringInput, + surfaceBlurRadius, ]; export const currentMigration = migrations.length; diff --git a/src/renderer/components/inputs/SliderInput.tsx b/src/renderer/components/inputs/SliderInput.tsx index cf6b3ab8f..7e485af87 100644 --- a/src/renderer/components/inputs/SliderInput.tsx +++ b/src/renderer/components/inputs/SliderInput.tsx @@ -11,7 +11,14 @@ import { BackendContext } from '../../contexts/BackendContext'; import { useContextMenu } from '../../hooks/useContextMenu'; import { AdvancedNumberInput } from './elements/AdvanceNumberInput'; import { CopyOverrideIdSection } from './elements/CopyOverrideIdSection'; -import { LINEAR_SCALE, LogScale, Scale, SliderStyle, StyledSlider } from './elements/StyledSlider'; +import { + LINEAR_SCALE, + LogScale, + PowerScale, + Scale, + SliderStyle, + StyledSlider, +} from './elements/StyledSlider'; import { WithLabel, WithoutLabel } from './InputContainer'; import { InputProps } from './props'; @@ -25,6 +32,8 @@ const parseScale = ( return new LogScale(input.min, input.precision); case 'log-offset': return new LogScale(input.min + 0.66, input.precision); + case 'sqrt': + return new PowerScale(0.5, input.min, input.precision); default: return assertNever(input.scale); } diff --git a/src/renderer/components/inputs/elements/StyledSlider.tsx b/src/renderer/components/inputs/elements/StyledSlider.tsx index 7e040116a..ed237288e 100644 --- a/src/renderer/components/inputs/elements/StyledSlider.tsx +++ b/src/renderer/components/inputs/elements/StyledSlider.tsx @@ -36,6 +36,28 @@ export class LogScale implements Scale { return Number(value.toFixed(this.precision)); } } +export class PowerScale implements Scale { + public readonly power: number; + + public readonly min: number; + + public readonly precision: number; + + constructor(power: number, min: number, precision: number) { + this.power = power; + this.min = min; + this.precision = precision; + } + + toScale(value: number): number { + return (value - this.min) ** this.power; + } + + fromScale(scaledValue: number): number { + const value = scaledValue ** (1 / this.power) + this.min; + return Number(value.toFixed(this.precision)); + } +} interface OldLabelStyle { readonly type: 'old-label';