mirror of
https://github.com/invoke-ai/InvokeAI.git
synced 2026-04-23 03:00:31 -04:00
feat(nodes)(ui): Add line mode to ImageAlphaToOutline
Allows the user to pick where the outlines are drawn .. both inside and outside of the alpha region
This commit is contained in:
@@ -1070,6 +1070,7 @@ class ImageAlphaToOutlineInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||
le=100,
|
||||
description="The width of the outline as a percentage of image dimension",
|
||||
)
|
||||
line_mode: Literal["inner", "outer", "both"] = InputField(default="both", description="Where to apply the mask")
|
||||
|
||||
def invoke(self, context: InvocationContext) -> ImageOutput:
|
||||
img_pil = context.images.get_pil(self.image.image_name, mode="RGBA")
|
||||
@@ -1097,6 +1098,15 @@ class ImageAlphaToOutlineInvocation(BaseInvocation, WithMetadata, WithBoard):
|
||||
thickness=line_width,
|
||||
)
|
||||
|
||||
# Limit drawing the outline
|
||||
if self.line_mode == "inner":
|
||||
contour_mask = numpy.minimum(contour_mask, binary_mask)
|
||||
if self.line_mode == "outer":
|
||||
inverted_binary_mask = cv2.bitwise_not(binary_mask)
|
||||
contour_mask = cv2.bitwise_and(contour_mask, inverted_binary_mask)
|
||||
if self.line_mode == "both":
|
||||
pass
|
||||
|
||||
# Create our result image, fully transparent
|
||||
result_rgba = numpy.zeros((contour_mask.shape[0], contour_mask.shape[1], 4), dtype=numpy.uint8)
|
||||
|
||||
|
||||
@@ -1815,7 +1815,11 @@
|
||||
"alpha_to_outline": {
|
||||
"label": "Alpha to Outline",
|
||||
"description": "Outlines the opaque regions of the image with a line of the given width.",
|
||||
"line_width_percent": "Line Width Percent"
|
||||
"line_width_percent": "Line Width Percent",
|
||||
"line_mode": "Line Mode",
|
||||
"inner": "Inner",
|
||||
"outer": "Outer",
|
||||
"both": "Both"
|
||||
},
|
||||
"color_map": {
|
||||
"label": "Color Map",
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { CompositeNumberInput, CompositeSlider, FormControl, FormLabel } from '@invoke-ai/ui-library';
|
||||
import {
|
||||
CompositeNumberInput,
|
||||
CompositeSlider,
|
||||
Flex,
|
||||
FormControl,
|
||||
FormLabel,
|
||||
Radio,
|
||||
RadioGroup,
|
||||
Text,
|
||||
} from '@invoke-ai/ui-library';
|
||||
import type { AlphaToOutlineFilterConfig } from 'features/controlLayers/store/filters';
|
||||
import { IMAGE_FILTERS } from 'features/controlLayers/store/filters';
|
||||
import { useCallback } from 'react';
|
||||
@@ -18,6 +27,13 @@ export const FilterAlphaToOutline = ({ onChange, config }: Props) => {
|
||||
[onChange, config]
|
||||
);
|
||||
|
||||
const handleLineModeChange = useCallback(
|
||||
(v: string) => {
|
||||
onChange({ ...config, line_mode: v });
|
||||
},
|
||||
[onChange, config]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FormControl>
|
||||
@@ -37,6 +53,22 @@ export const FilterAlphaToOutline = ({ onChange, config }: Props) => {
|
||||
max={100}
|
||||
/>
|
||||
</FormControl>
|
||||
<FormControl w="min-content">
|
||||
<FormLabel m={0}>{t('controlLayers.filter.alpha_to_outline.line_mode')}</FormLabel>
|
||||
<RadioGroup value={config.line_mode} onChange={handleLineModeChange} w="full" size="md">
|
||||
<Flex alignItems="center" w="full" gap={4} fontWeight="semibold" color="base.300">
|
||||
<Radio value="both">
|
||||
<Text>{t('controlLayers.filter.alpha_to_outline.both')}</Text>
|
||||
</Radio>
|
||||
<Radio value="inner">
|
||||
<Text>{t('controlLayers.filter.alpha_to_outline.inner')}</Text>
|
||||
</Radio>
|
||||
<Radio value="outer">
|
||||
<Text>{t('controlLayers.filter.alpha_to_outline.outer')}</Text>
|
||||
</Radio>
|
||||
</Flex>
|
||||
</RadioGroup>
|
||||
</FormControl>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -98,6 +98,7 @@ export type SpandrelFilterConfig = z.infer<typeof zSpandrelFilterConfig>;
|
||||
const zAlphaToOutlineFilterConfig = z.object({
|
||||
type: z.literal('alpha_to_outline'),
|
||||
line_width_percent: z.number().gte(1).lte(100),
|
||||
line_mode: z.string(),
|
||||
});
|
||||
export type AlphaToOutlineFilterConfig = z.infer<typeof zAlphaToOutlineFilterConfig>;
|
||||
|
||||
@@ -176,14 +177,16 @@ export const IMAGE_FILTERS: { [key in FilterConfig['type']]: ImageFilterData<key
|
||||
buildDefaults: () => ({
|
||||
type: 'alpha_to_outline',
|
||||
line_width_percent: 5,
|
||||
line_mode: 'both',
|
||||
}),
|
||||
buildGraph: ({ image_name }, { line_width_percent }) => {
|
||||
buildGraph: ({ image_name }, { line_width_percent, line_mode }) => {
|
||||
const graph = new Graph(getPrefixedId('alpha_to_outline_filter'));
|
||||
const node = graph.addNode({
|
||||
id: getPrefixedId('alpha_to_outline'),
|
||||
type: 'img_alpha_to_outline',
|
||||
image: { image_name },
|
||||
line_width_percent,
|
||||
line_mode,
|
||||
});
|
||||
return {
|
||||
graph,
|
||||
|
||||
@@ -7911,6 +7911,13 @@ export type components = {
|
||||
* @default 5
|
||||
*/
|
||||
line_width_percent?: number;
|
||||
/**
|
||||
* Line Mode
|
||||
* @description Where to apply the mask
|
||||
* @default both
|
||||
* @enum {string}
|
||||
*/
|
||||
line_mode?: "inner" | "outer" | "both";
|
||||
/**
|
||||
* type
|
||||
* @default img_alpha_to_outline
|
||||
|
||||
Reference in New Issue
Block a user