Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved threshold nodes #2091

Merged
merged 1 commit into from
Aug 14, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 0 additions & 68 deletions backend/src/nodes/properties/inputs/image_dropdown_inputs.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import cv2

import navi

from ...impl.color.convert_data import (
Expand Down Expand Up @@ -97,72 +95,6 @@ def BorderType::getOutputChannels(type: BorderType, channels: uint, color: Color
)


def ThresholdInput() -> DropDownInput:
"""Threshold type option dropdown"""
return DropDownInput(
input_type="ThresholdType",
label="Threshold Type",
options=[
{
"option": "Binary",
"value": cv2.THRESH_BINARY,
},
{
"option": "Binary (Inverted)",
"value": cv2.THRESH_BINARY_INV,
},
{
"option": "Truncated",
"value": cv2.THRESH_TRUNC,
},
{
"option": "To Zero",
"value": cv2.THRESH_TOZERO,
},
{
"option": "To Zero (Inverted)",
"value": cv2.THRESH_TOZERO_INV,
},
],
)


def AdaptiveThresholdInput() -> DropDownInput:
"""Adaptive Threshold type option dropdown"""
return DropDownInput(
input_type="AdaptiveThresholdType",
label="Threshold Type",
options=[
{
"option": "Binary",
"value": cv2.THRESH_BINARY,
},
{
"option": "Binary (Inverted)",
"value": cv2.THRESH_BINARY_INV,
},
],
)


def AdaptiveMethodInput() -> DropDownInput:
"""Adaptive method border option dropdown"""
return DropDownInput(
input_type="AdaptiveMethod",
label="Adaptive Method",
options=[
{
"option": "Mean - C",
"value": cv2.ADAPTIVE_THRESH_MEAN_C,
},
{
"option": "Gaussian - C",
"value": cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
},
],
)


def NormalChannelInvertInput() -> DropDownInput:
return DropDownInput(
input_type="NormalChannelInvert",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,35 @@
from __future__ import annotations

from enum import Enum
from typing import Dict

import cv2
import numpy as np
from sanic.log import logger

from nodes.properties.inputs import ImageInput, SliderInput, ThresholdInput
from nodes.groups import if_enum_group
from nodes.properties.inputs import EnumInput, ImageInput, SliderInput
from nodes.properties.outputs import ImageOutput

from .. import adjustments_group


class ThresholdType(Enum):
BINARY = cv2.THRESH_BINARY
BINARY_INV = cv2.THRESH_BINARY_INV
TRUNC = cv2.THRESH_TRUNC
TO_ZERO = cv2.THRESH_TOZERO
TO_ZERO_INV = cv2.THRESH_TOZERO_INV


_THRESHOLD_TYPE_LABELS: Dict[ThresholdType, str] = {
ThresholdType.BINARY: "Binary",
ThresholdType.BINARY_INV: "Binary (Inverted)",
ThresholdType.TRUNC: "Truncated",
ThresholdType.TO_ZERO: "To Zero",
ThresholdType.TO_ZERO_INV: "To Zero (Inverted)",
}


@adjustments_group.register(
schema_id="chainner:image:threshold",
name="Threshold",
Expand All @@ -24,29 +44,33 @@
precision=1,
controls_step=1,
),
SliderInput(
"Maximum Value",
maximum=100,
default=100,
precision=1,
controls_step=1,
EnumInput(
ThresholdType,
"Threshold Type",
default=ThresholdType.BINARY,
option_labels=_THRESHOLD_TYPE_LABELS,
).with_id(3),
if_enum_group(3, (ThresholdType.BINARY, ThresholdType.BINARY_INV))(
SliderInput(
"Maximum Value",
maximum=100,
default=100,
precision=1,
controls_step=1,
).with_id(2),
),
ThresholdInput(),
],
outputs=[ImageOutput(image_type="Input0")],
)
def threshold_node(
img: np.ndarray, thresh: float, maxval: float, thresh_type: int
img: np.ndarray,
threshold: float,
thresh_type: ThresholdType,
max_value: float,
) -> np.ndarray:
"""Takes an image and applies a threshold to it"""

logger.debug(f"thresh {thresh}, maxval {maxval}, type {thresh_type}")

real_thresh = thresh / 100
real_maxval = maxval / 100

logger.debug(f"real_thresh {real_thresh}, real_maxval {real_maxval}")
threshold /= 100
max_value /= 100

_, result = cv2.threshold(img, real_thresh, real_maxval, thresh_type)
_, result = cv2.threshold(img, threshold, max_value, thresh_type.value)

return result
Original file line number Diff line number Diff line change
@@ -1,65 +1,101 @@
from __future__ import annotations

from enum import Enum
from typing import Dict

import cv2
import numpy as np

from nodes.impl.image_utils import to_uint8
from nodes.properties.inputs import (
AdaptiveMethodInput,
AdaptiveThresholdInput,
ImageInput,
NumberInput,
SliderInput,
)
from nodes.properties.inputs import EnumInput, ImageInput, NumberInput, SliderInput
from nodes.properties.outputs import ImageOutput

from .. import adjustments_group


class AdaptiveThresholdType(Enum):
BINARY = cv2.THRESH_BINARY
BINARY_INV = cv2.THRESH_BINARY_INV


_THRESHOLD_TYPE_LABELS: Dict[AdaptiveThresholdType, str] = {
AdaptiveThresholdType.BINARY: "Binary",
AdaptiveThresholdType.BINARY_INV: "Binary (Inverted)",
}


class AdaptiveMethod(Enum):
MEAN = cv2.ADAPTIVE_THRESH_MEAN_C
GAUSSIAN = cv2.ADAPTIVE_THRESH_GAUSSIAN_C


_ADAPTIVE_METHOD_LABELS: Dict[AdaptiveMethod, str] = {
AdaptiveMethod.MEAN: "Mean",
AdaptiveMethod.GAUSSIAN: "Gaussian",
}


@adjustments_group.register(
schema_id="chainner:image:threshold_adaptive",
name="Threshold (Adaptive)",
description="Similar to regular threshold, but determines the threshold for a pixel based on a small region around it.",
icon="MdAutoGraph",
inputs=[
ImageInput(channels=1),
EnumInput(
AdaptiveThresholdType,
"Threshold Type",
default=AdaptiveThresholdType.BINARY,
option_labels=_THRESHOLD_TYPE_LABELS,
).with_id(3),
SliderInput(
"Maximum Value",
maximum=100,
default=100,
precision=1,
controls_step=1,
).with_id(1),
EnumInput(
AdaptiveMethod,
"Adaptive Method",
default=AdaptiveMethod.MEAN,
option_labels=_ADAPTIVE_METHOD_LABELS,
).with_id(2),
NumberInput("Block Radius", default=1, minimum=1).with_id(4),
NumberInput(
"Constant Subtraction",
minimum=-100,
maximum=100,
default=0,
precision=1,
)
.with_id(5)
.with_docs(
"A constant value that is subtracted from the automatically determined adaptive threshold.",
"Assuming that **Threshold Type** is *Binary*, then higher values will result in more white pixels and lower values will result in more black pixels.",
),
AdaptiveMethodInput(),
AdaptiveThresholdInput(),
NumberInput("Block Radius", default=1, minimum=1),
NumberInput("Mean Subtraction"),
],
outputs=[ImageOutput(image_type="Input0")],
limited_to_8bpc=True,
)
def threshold_adaptive_node(
img: np.ndarray,
maxval: float,
adaptive_method: int,
thresh_type: int,
threshold_type: AdaptiveThresholdType,
max_value: float,
adaptive_method: AdaptiveMethod,
block_radius: int,
c: int,
c: float,
) -> np.ndarray:
"""Takes an image and applies an adaptive threshold to it"""

# Adaptive threshold requires uint8 input
img = to_uint8(img, normalized=True)

real_maxval = maxval / 100 * 255
max_value = max_value / 100 * 255

result = cv2.adaptiveThreshold(
return cv2.adaptiveThreshold(
img,
real_maxval,
adaptive_method,
thresh_type,
max_value,
adaptive_method.value,
threshold_type.value,
block_radius * 2 + 1,
c,
round(c / 100 * 255),
)

return result
4 changes: 0 additions & 4 deletions src/common/types/chainner-scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,11 @@ let OnnxGenericModel = OnnxModel {
struct IteratorAuto;

// various inputs
struct AdaptiveMethod;
struct AdaptiveThresholdType;
struct ColorSpace { channels: 1 | 3 | 4, supportsAlpha: bool }
struct DdsFormat;
struct DdsMipMaps;
struct ImageExtension;
struct NormalChannelInvert;
struct RotateInterpolationMode;
struct ThresholdType;
struct TileSize;
struct VideoPreset;
struct VideoType;
Expand Down