diff --git a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx b/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx index 5fb3cc7dca..eeee461209 100644 --- a/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx +++ b/invokeai/frontend/web/src/features/controlLayers/components/SelectObject/SelectObject.tsx @@ -115,7 +115,7 @@ const SelectObjectContent = memo( onClick={adapter.segmentAnything.processImmediate} loadingText={t('controlLayers.selectObject.process')} variant="ghost" - isDisabled={isProcessing || !hasPoints || autoProcess} + isDisabled={isProcessing || !hasPoints || (autoProcess && hasImageState)} > {t('controlLayers.selectObject.process')} {isProcessing && } diff --git a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts index 2a127b060d..171907b0d8 100644 --- a/invokeai/frontend/web/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts +++ b/invokeai/frontend/web/src/features/controlLayers/konva/CanvasSegmentAnythingModule.ts @@ -114,7 +114,7 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { subscriptions = new Set<() => void>(); /** - * The AbortController used to cancel the filter processing. + * The AbortController used to cancel the segment processing. */ abortController: AbortController | null = null; @@ -178,16 +178,16 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { $invert = atom(false); /** - * The masked image object, if it exists. + * The masked image object module, if it exists. */ - maskedImage: CanvasObjectImage | null = null; + imageModule: CanvasObjectImage | null = null; /** * The Konva nodes for the module. */ konva: { /** - * The main Konva group node for the module. + * The main Konva group node for the module. This is added to the parent layer on start, and removed on teardown. */ group: Konva.Group; /** @@ -488,7 +488,7 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { }; /** - * Adds event listeners needed while segmenting the entity. + * Removes event listeners used while segmenting the entity. */ unsubscribe = () => { this.subscriptions.forEach((unsubscribe) => unsubscribe()); @@ -607,18 +607,18 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { this.$imageState.set(imageState); // Destroy any existing masked image and create a new one - if (this.maskedImage) { - this.maskedImage.destroy(); + if (this.imageModule) { + this.imageModule.destroy(); } if (this.konva.maskTween) { this.konva.maskTween.destroy(); this.konva.maskTween = null; } - this.maskedImage = new CanvasObjectImage(imageState, this); + this.imageModule = new CanvasObjectImage(imageState, this); // Force update the masked image - after awaiting, the image will be rendered (in memory) - await this.maskedImage.update(imageState, true); + await this.imageModule.update(imageState, true); // Update the compositing rect to match the image size this.konva.compositingRect.setAttrs({ @@ -629,7 +629,7 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { // Now we can add the masked image to the mask group. It will be rendered above the compositing rect, but should be // under it, so we will move the compositing rect to the top - this.konva.maskGroup.add(this.maskedImage.konva.group); + this.konva.maskGroup.add(this.imageModule.konva.group); this.konva.compositingRect.moveToTop(); // Cache the group to ensure the mask is rendered correctly w/ opacity @@ -666,7 +666,7 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { process = debounce(this.processImmediate, this.config.PROCESS_DEBOUNCE_MS); /** - * Applies the segmented image to the entity. + * Applies the segmented image to the entity, replacing the entity's objects with the masked image. */ apply = () => { const imageState = this.$imageState.get(); @@ -676,7 +676,7 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { } this.log.trace('Applying'); - // Rasterize the entity, this time replacing the objects with the masked image + // Rasterize the entity, replacing the objects with the masked image const rect = this.parent.transformer.getRelativeRect(); this.manager.stateApi.rasterizeEntity({ entityIdentifier: this.parent.entityIdentifier, @@ -694,7 +694,8 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { }; /** - * Applies the segmented image to the entity. + * Saves the segmented image as a new entity of the given type. + * @param type The type of entity to save the segmented image as. */ saveAs = (type: Exclude) => { const imageState = this.$imageState.get(); @@ -795,8 +796,9 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { for (const point of this.$points.get()) { point.konva.circle.destroy(); } - if (this.maskedImage) { - this.maskedImage.destroy(); + if (this.imageModule) { + this.imageModule.destroy(); + this.imageModule = null; } if (this.konva.maskTween) { this.konva.maskTween.destroy(); @@ -878,7 +880,7 @@ export class CanvasSegmentAnythingModule extends CanvasModuleBase { circle: getKonvaNodeDebugAttrs(konva.circle), })), imageState: deepClone(this.$imageState.get()), - maskedImage: this.maskedImage?.repr(), + imageModule: this.imageModule?.repr(), config: deepClone(this.config), $isSegmenting: this.$isSegmenting.get(), $lastProcessedHash: this.$lastProcessedHash.get(),