Skip to content

Commit b72488a

Browse files
authored
Add support for extract transformation (#57)
1 parent 265c5cb commit b72488a

File tree

9 files changed

+237
-6
lines changed

9 files changed

+237
-6
lines changed

__TESTS_BUNDLE_SIZE__/bundleSizeTestCases.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ const bundleSizeTestCases:ITestCase[] = [
3232
},
3333
{
3434
name: 'Import all of the SDK',
35-
sizeLimitInKB: 128,
35+
sizeLimitInKB: 129,
3636
importsArray: [
3737
importFromPackage('* as CloudinaryURLGEN')
3838
]
@@ -46,7 +46,7 @@ const bundleSizeTestCases:ITestCase[] = [
4646
},
4747
{
4848
name: 'Import All Actions',
49-
sizeLimitInKB: 64,
49+
sizeLimitInKB: 65,
5050
importsArray: [
5151
importFromPackage('Actions')
5252
]

__TESTS__/unit/fromJson/effect.fromJson.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ describe('effect.fromJson', () => {
5151
{ actionType: 'enhance' },
5252
{ actionType: 'generativeBackgroundReplace', prompt: 'dog' },
5353
{ actionType: 'generativeBackgroundReplace'},
54+
{ actionType: 'extract', 'prompts': 'blue sky' },
55+
{ actionType: 'extract', 'prompts': ['blue sky', 'yellow sun'], detectMultiple: true },
56+
{ actionType: 'extract', 'prompts': ['green grass'], mode: 'mask', invert: true },
57+
{ actionType: 'extract', 'prompts': ['yellow sun', 'green grass'], mode: 'content' },
58+
5459
]});
5560

5661
expect(transformation.toString().split('/')).toStrictEqual([
@@ -101,7 +106,11 @@ describe('effect.fromJson', () => {
101106
'e_upscale',
102107
'e_enhance',
103108
'e_gen_background_replace:prompt_dog',
104-
'e_gen_background_replace'
109+
'e_gen_background_replace',
110+
'e_extract:prompt_blue sky',
111+
'e_extract:prompt_(blue sky;yellow sun);multiple_true',
112+
'e_extract:prompt_green grass;mode_mask;invert_true',
113+
'e_extract:prompt_(yellow sun;green grass);mode_content',
105114
]);
106115
});
107116
});

__TESTS__/unit/toJson/effect.toJson.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -504,6 +504,46 @@ describe('Effect toJson()', () => {
504504
});
505505
});
506506

507+
it('effect.extract', () => {
508+
const transformation = new Transformation()
509+
.addAction(Effect.extract('palms'))
510+
.addAction(Effect.extract(['man']).mode('mask'))
511+
.addAction(Effect.extract('woman').invert(true))
512+
.addAction(Effect.extract(['cats', 'dogs']).detectMultiple(true))
513+
.addAction(Effect.extract(['dogs', 'cats']).mode('content').detectMultiple(true).invert(false));
514+
515+
516+
expect(transformation.toJson()).toStrictEqual({
517+
actions: [
518+
{
519+
actionType: 'extract',
520+
prompts: ['palms'],
521+
},
522+
{
523+
actionType: 'extract',
524+
prompts: ['man'],
525+
mode: 'mask',
526+
},
527+
{
528+
actionType: 'extract',
529+
prompts: ['woman'],
530+
invert: true,
531+
},
532+
{
533+
actionType: 'extract',
534+
prompts: ['cats', 'dogs'],
535+
detectMultiple: true,
536+
},
537+
{
538+
actionType: 'extract',
539+
prompts: ['dogs', 'cats'],
540+
mode: 'content',
541+
detectMultiple: true
542+
},
543+
]
544+
});
545+
});
546+
507547
it('effect.GenerativeRecolor', () => {
508548
const transformation = new Transformation()
509549
.addAction(Effect.generativeRecolor('something', 'red'))

src/actions/effect.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import { GenerativeRemove } from "./effect/GenerativeRemove.js";
3131
import { GenerativeReplace } from "./effect/GenerativeReplace.js";
3232
import { GenerativeRecolor } from "./effect/GenerativeRecolor.js";
3333
import { GenerativeBackgroundReplace } from "./effect/GenerativeBackgroundReplace.js";
34+
import { Extract } from "./effect/Extract.js";
3435

3536
/**
3637
* @summary action
@@ -473,6 +474,18 @@ function generativeBackgroundReplace(): GenerativeBackgroundReplace {
473474
return new GenerativeBackgroundReplace();
474475
}
475476

477+
/**
478+
* @summary action
479+
* @description Extracts an area or multiple areas of an image, described in natural language.
480+
* {@link https://cloudinary.com/documentation/transformation_reference#e_extract|Extract}
481+
*
482+
* @memberOf Actions.Effect
483+
* @return {Actions.Effect.Extract}
484+
*/
485+
function extract(prompts: string | string[]): Extract {
486+
return new Extract(prompts);
487+
}
488+
476489
/**
477490
* @summary action
478491
* @description Uses generative AI to recolor objects from your image.
@@ -628,7 +641,8 @@ const Effect = {
628641
generativeRestore,
629642
upscale,
630643
theme,
631-
enhance
644+
enhance,
645+
extract
632646
};
633647

634648
export declare type EffectActions =
@@ -652,7 +666,8 @@ export declare type EffectActions =
652666
| GenerativeRemove
653667
| GenerativeReplace
654668
| GenerativeBackgroundReplace
655-
| GenerativeRecolor;
669+
| GenerativeRecolor
670+
| Extract;
656671

657672
export {
658673
Effect,
@@ -698,4 +713,5 @@ export {
698713
upscale,
699714
theme,
700715
enhance,
716+
extract
701717
};

src/actions/effect/Extract.ts

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
import { Action } from "../../internal/Action.js";
2+
import { Qualifier } from "../../internal/qualifier/Qualifier.js";
3+
import { IExtractModel } from "../../internal/models/IEffectActionModel.js";
4+
import { ExtractModeType } from "../../types/types.js";
5+
import { QualifierValue } from "../../internal/qualifier/QualifierValue.js";
6+
7+
/**
8+
* @description Extracts an area or multiple areas of an image, described in natural language.
9+
* @extends SDK.Action
10+
* @memberOf Actions.Effect
11+
* @see Visit {@link Actions.Effect|Effect} for an example
12+
*/
13+
class Extract extends Action {
14+
private _prompts: Array<string> = [];
15+
private _detectMultiple = false;
16+
private _mode: ExtractModeType;
17+
private _invert = false;
18+
19+
constructor(prompts: string | string[]) {
20+
super();
21+
this._actionModel.actionType = "extract";
22+
23+
this._prompts = Array.isArray(prompts) ? prompts : [prompts];
24+
this._actionModel.prompts = this._prompts;
25+
}
26+
27+
detectMultiple(value = false) {
28+
this._detectMultiple = value;
29+
30+
if (this._detectMultiple) {
31+
this._actionModel.detectMultiple = this._detectMultiple;
32+
}
33+
34+
return this;
35+
}
36+
37+
mode(mode?: ExtractModeType) {
38+
this._mode = mode;
39+
this._actionModel.mode = this._mode;
40+
41+
return this;
42+
}
43+
44+
invert(value = false) {
45+
this._invert = value;
46+
47+
if (this._invert) {
48+
this._actionModel.invert = this._invert;
49+
}
50+
51+
return this;
52+
}
53+
54+
protected prepareQualifiers(): void {
55+
const qualifierValue = new QualifierValue().setDelimiter(";");
56+
57+
if (this._prompts.length) {
58+
qualifierValue.addValue(this.preparePromptValue());
59+
}
60+
61+
if (this._detectMultiple) {
62+
qualifierValue.addValue("multiple_true");
63+
}
64+
65+
if (this._mode) {
66+
qualifierValue.addValue(`mode_${this._mode}`);
67+
}
68+
69+
if (this._invert) {
70+
qualifierValue.addValue("invert_true");
71+
}
72+
73+
this.addQualifier(
74+
new Qualifier("e", `extract:${qualifierValue.toString()}`)
75+
);
76+
}
77+
78+
private preparePromptValue() {
79+
const prompts = this._prompts;
80+
const qualifierValue = new QualifierValue().setDelimiter(";");
81+
82+
if (prompts.length === 1) {
83+
qualifierValue.addValue(`prompt_${prompts[0]}`);
84+
} else {
85+
qualifierValue.addValue(`prompt_(${prompts.join(";")})`);
86+
}
87+
88+
return qualifierValue;
89+
}
90+
91+
static fromJson(actionModel: IExtractModel): Extract {
92+
const { prompts, detectMultiple, mode, invert } = actionModel;
93+
const result = new this(prompts);
94+
95+
if (detectMultiple) {
96+
result.detectMultiple(detectMultiple);
97+
}
98+
99+
if (mode) {
100+
result.mode(mode);
101+
}
102+
103+
if (invert) {
104+
result.invert(invert);
105+
}
106+
107+
return result;
108+
}
109+
}
110+
111+
export { Extract };

src/internal/fromJson.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import { ResizeAdvancedAction } from "../actions/resize/ResizeAdvancedAction.js"
7373
import { BackgroundColor } from "../actions/background/actions/BackgroundColor.js";
7474
import { ResizeAutoPadAction } from "../actions/resize/ResizeAutoPadAction.js";
7575
import { GenerativeBackgroundReplace } from "../actions/effect/GenerativeBackgroundReplace.js";
76+
import { Extract } from "../actions/effect/Extract.js";
7677

7778
const ActionModelMap: Record<string, IHasFromJson> = {
7879
scale: ResizeScaleAction,
@@ -158,7 +159,8 @@ const ActionModelMap: Record<string, IHasFromJson> = {
158159
backgroundColor: BackgroundColor,
159160
autoPad: ResizeAutoPadAction,
160161
enhance: SimpleEffectAction,
161-
sharpen: EffectActionWithStrength
162+
sharpen: EffectActionWithStrength,
163+
extract: Extract
162164
};
163165

164166
/**

src/internal/models/IEffectActionModel.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { IActionModel } from "./IActionModel.js";
22
import { ForegroundObjectValue } from "../../qualifiers/foregroundObject.js";
33
import { SystemColors } from "../../qualifiers/color.js";
4+
import { ExtractModeType } from "types/types.js";
45

56
interface IEffectActionWithLevelModel extends IActionModel {
67
level?: number;
@@ -143,6 +144,14 @@ interface IBackgroundColorModel extends IActionModel {
143144
color?: SystemColors | string;
144145
}
145146

147+
148+
interface IExtractModel extends IActionModel {
149+
prompts: Array<string>;
150+
detectMultiple?: boolean;
151+
mode?: ExtractModeType;
152+
invert?: boolean;
153+
}
154+
146155
export {
147156
IEffectActionWithLevelModel,
148157
ISimpleEffectActionModel,
@@ -169,4 +178,5 @@ export {
169178
IGenerativeRecolorModel,
170179
IGenerativeBackgroundReplaceModel,
171180
IBackgroundColorModel,
181+
IExtractModel
172182
};

src/qualifiers/extractMode.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @description Contains functions that decide whether to keep the content of the extracted area, or to replace it with a mask.
3+
* @namespace Extract
4+
* @memberOf Qualifiers
5+
* @see Visit {@link Actions.Effect|Effect Action} for an example
6+
*/
7+
8+
/**
9+
* @summary qualifier
10+
* @memberOf Qualifiers.Extract
11+
*/
12+
function content(): string {
13+
return 'content';
14+
}
15+
16+
/**
17+
* @summary qualifier
18+
* @memberOf Qualifiers.Extract
19+
*/
20+
function mask(): string {
21+
return 'mask';
22+
}
23+
24+
/**
25+
* @summary qualifier
26+
* @memberOf Qualifiers.Extract
27+
*/
28+
29+
30+
const ExtractMode = {
31+
content,
32+
mask
33+
};
34+
35+
export {
36+
ExtractMode,
37+
content,
38+
mask
39+
};

src/types/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,10 @@ export type OutlineModeType =
386386
"inner_fill"|
387387
"fill";
388388

389+
export type ExtractModeType =
390+
"content"|
391+
"mask";
392+
389393
export type ProgressiveType =
390394
"semi"|
391395
"none"|

0 commit comments

Comments
 (0)