Generating images
Every image operation on TTMSMCPCloudImageAI goes through one of four calls —
Execute, RemoveBackground, ReplaceBackground, and the Images collection
that changes what Execute means — and all four end in the same place: a
base64 payload delivered to OnImageGenerated, or a failure delivered to
OnRequestError. Which call to reach for is decided by how many reference
images you have and how specific the instruction is, not by which service you
are on: the component presents all five providers through the same surface and
substitutes a written instruction where a provider has no dedicated endpoint.
This guide covers each call with a working example, what to do with the result,
and where a request can fail without the component raising anything.
Text to image
The simplest request has no reference image at all: a prompt goes out, an image comes back. Use it whenever you are creating something rather than changing something.
procedure TForm1.GenerateFromPrompt(const APrompt: string);
begin
{ A leftover reference image turns the next call into an image-to-image
request, so clear the collection when you want text to image. }
ImageAI.Images.Clear;
btnGenerate.Enabled := False;
memoLog.Lines.Add('Requesting: ' + APrompt);
{ The call returns immediately. Everything else happens in the two
result events. }
ImageAI.Execute(APrompt);
end;
Execute returns as soon as the request is dispatched, which means the method
that called it finishes before the image exists. Disable whatever control
started the request there, and re-enable it in both result events — an error
path that leaves a button disabled is the most common way this goes wrong.
Note the Images.Clear call. The collection is not consumed by Execute, so
reference images left over from a previous request silently turn the next
text-to-image call into an image-to-image one.
Receiving and saving the result
OnImageGenerated hands you the generated image as base64 plus the finished
HTTP request. The encoding is the same on every service: where a provider
answers with a URL rather than image data, the component downloads it and
encodes it before the event fires, so a single decoding path is enough.
procedure TForm1.ImageAIImageGenerated(Sender: TObject;
ARequestResult: TTMSMCPCloudBaseRequestResult; ABase64Image: string);
var
Stream: TMemoryStream;
FileName: string;
begin
btnGenerate.Enabled := True;
if ABase64Image = '' then
begin
memoLog.Lines.Add('The service returned no image.');
Exit;
end;
Stream := TMemoryStream.Create;
try
TTMSMCPUtils.LoadStreamFromBase64(ABase64Image, Stream);
FileName := TPath.Combine(FOutputFolder,
FormatDateTime('yyyymmdd-hhnnss', Now) + '.png');
Stream.SaveToFile(FileName);
{ Rewind before handing the same stream to a picture control. }
Stream.Position := 0;
imgResult.Bitmap.LoadFromStream(Stream);
finally
Stream.Free;
end;
memoLog.Lines.Add('Saved ' + FileName);
end;
procedure TForm1.ImageAIRequestError(Sender: TObject;
ARequestResult: TTMSMCPCloudBaseRequestResult);
begin
btnGenerate.Enabled := True;
memoLog.Lines.Add(Format('Failed (%d): %s',
[ARequestResult.ResponseCode, ARequestResult.ResultString]));
end;
TTMSMCPUtils.LoadStreamFromBase64 fills a stream from the payload. Rewind it
with Position := 0 before handing the same stream to a picture control after
saving it — SaveToFile leaves the position at the end.
An empty ABase64Image is possible on a request the transport considered
successful, so check for it. The component treats that as a failure and routes
it to OnRequestError in most paths, but a defensive check in the success
handler costs nothing and makes the distinction visible in the log.
Editing one reference image
Pass a reference image and the call becomes an edit: the prompt now describes
the change rather than the subject. Four overloads take the reference in
whatever form you already have it — a TStream, a base64 string, or the
framework picture type (TBitmap in FMX, TPicture in VCL):
procedure TForm1.EditFromFile(const AFileName, APrompt: string);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile(AFileName);
{ The stream is read during the call, so it is safe to free it as soon
as Execute returns - the request no longer needs it. }
ImageAI.Execute(Stream, APrompt);
finally
Stream.Free;
end;
end;
procedure TForm1.EditDisplayedImage(const APrompt: string);
begin
{ The framework overload saves the picture to a stream for you: TBitmap
in FMX, TPicture in VCL. }
ImageAI.Execute(imgSource.Bitmap, APrompt);
end;
procedure TForm1.EditFromBase64(const ABase64, APrompt: string);
begin
{ A reference that already arrived as base64 - from a previous generation,
or from a database - needs no decoding of your own. }
ImageAI.Execute(ABase64, APrompt);
end;
The stream is read during the call — encoded into the request body before
Execute returns — so it is safe to free it immediately afterwards. The
framework overloads simply save the picture to a temporary stream and call the
stream overload, so they behave identically.
Several references with the Images collection
For a composition — several products on one surface, a subject placed into a
scene, a style taken from one image and applied to another — populate the
Images collection and call Execute with only a prompt. A non-empty
collection is what turns that call into a multi-reference request:
procedure TForm1.ComposeFromFiles;
begin
{ Whatever is in Images at the time of the call is sent with the prompt.
Clear it first so an earlier request does not leak into this one. }
ImageAI.Images.Clear;
ImageAI.Images.Add.Stream.LoadFromFile(
TPath.Combine(FAssetFolder, 'table.jpg'));
ImageAI.Images.Add.Stream.LoadFromFile(
TPath.Combine(FAssetFolder, 'book.jpg'));
ImageAI.Images.Add.Stream.LoadFromFile(
TPath.Combine(FAssetFolder, 'flowers.jpg'));
{ Execute with only a prompt: because Images is populated, this becomes a
multi-reference request instead of a text-to-image one. }
ImageAI.Execute('Create a still life composition from this table, book ' +
'and flowers, lit from the left.');
end;
procedure TForm1.ComposeFromDisplayedImage;
begin
ImageAI.Images.Clear;
{ The Add overload takes the framework picture type directly: TBitmap in
FMX, TPicture in VCL. }
ImageAI.Images.Add(imgSource.Bitmap);
ImageAI.Execute('Remove the coffee cup from this picture.');
end;
Each TTMSMCPCloudAIImage owns a
TMemoryStream you can load from a file, from another stream, or from a
displayed picture through the Add overload. The collection owns the items, so
nothing has to be freed by hand; Clear releases them.
How the references are used is the one place the providers genuinely differ. They are sent as multipart parts, inline data, numbered input images, or a reference-image array depending on the service, and each has its own limit on how many it will accept. Two or three references behave predictably everywhere; beyond that, check the provider's own documentation before relying on it.
Removing a background
RemoveBackground takes the same three input forms as Execute and needs no
prompt:
procedure TForm1.CutOutProduct(const AFileName: string);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile(AFileName);
ImageAI.RemoveBackground(Stream);
finally
Stream.Free;
end;
end;
procedure TForm1.CutOutDisplayedImage;
begin
ImageAI.RemoveBackground(imgSource.Bitmap);
end;
procedure TForm1.CutOutWithOwnPrompt(const AFileName: string);
var
Stream: TMemoryStream;
begin
{ Only some services have a dedicated background endpoint; for the rest
RemoveBackground sends a generic instruction. When the result is not
clean enough, drop to Execute and write the instruction yourself. }
Stream := TMemoryStream.Create;
try
Stream.LoadFromFile(AFileName);
ImageAI.Execute(Stream,
'Cut the product out of its background and place it on a fully ' +
'transparent background. Keep the original shadow under the product.');
finally
Stream.Free;
end;
end;
Only some services have a dedicated background endpoint. Stability does, and
the component uses it. For the others, RemoveBackground sends the reference
image with a generic written instruction — which works well for a clean subject
on a plain background and less well for hair, glass, or a busy scene. When the
result is not good enough, that is not a limitation to work around: drop to
Execute with the same image and write the instruction yourself, which is
exactly what the shortcut was doing on your behalf.
Replacing a background
ReplaceBackground takes two images: the subject first, the new background
second. Both come in the same three forms.
procedure TForm1.RestageProduct(const ASubjectFile, ABackgroundFile: string);
var
Subject, Background: TMemoryStream;
begin
Subject := TMemoryStream.Create;
try
Background := TMemoryStream.Create;
try
Subject.LoadFromFile(ASubjectFile);
Background.LoadFromFile(ABackgroundFile);
{ Order is part of the contract: the first image supplies the subject,
the second supplies the new background. Swapping them produces a
plausible-looking but wrong result rather than an error. }
ImageAI.ReplaceBackground(Subject, Background);
finally
Background.Free;
end;
finally
Subject.Free;
end;
end;
procedure TForm1.RestageDisplayedImage;
begin
ImageAI.ReplaceBackground(imgSubject.Bitmap, imgBackdrop.Bitmap);
end;
The order is part of the contract and is not checked. Swapping the two produces a plausible-looking wrong result — the background restaged against the subject — rather than an error, so it is worth naming the variables for their role as the example does.
As with removal, only Stability has a dedicated endpoint; the others receive both images with a written instruction. Stability's variant is also the one case where the component polls for its result, so a replacement there takes noticeably longer than a generation.
Handling failures and long-running requests
OnRequestError is the single failure path, and it covers three different
situations: the HTTP request itself failed, the service returned a response
that reported an error, or the response arrived successfully but carried no
image. ARequestResult.ResponseCode and ResultString are what separate them.
procedure TForm1.SetUpDiagnostics;
begin
ImageAI.OnRequestError := ImageAIRequestError;
{ Inherited from the cloud transport base: these fire for the generation
request and for every poll it may trigger. }
ImageAI.OnRequestStarted := ImageAIRequestStarted;
ImageAI.OnRequestProgress := ImageAIRequestProgress;
end;
procedure TForm1.ImageAIRequestStarted(Sender: TObject;
const ARequestResult: TTMSMCPCloudBaseRequestResult);
begin
progress.Value := 0;
lblStatus.Text := 'Sending ' + ARequestResult.Name;
end;
procedure TForm1.ImageAIRequestProgress(Sender: TObject;
const ARequestResult: TTMSMCPCloudBaseRequestResult; AProgress: Single;
AUpload: Boolean);
begin
progress.Value := AProgress;
end;
procedure TForm1.ImageAIRequestError(Sender: TObject;
ARequestResult: TTMSMCPCloudBaseRequestResult);
begin
lblStatus.Text := 'Failed';
{ OnRequestError covers three different failures: the HTTP call itself,
a response the service rejected, and a successful response that carried
no image. ResponseCode tells them apart. }
memoLog.Lines.Add(Format('%s failed with HTTP %d',
[ARequestResult.Name, ARequestResult.ResponseCode]));
memoLog.Lines.Add(ARequestResult.ResultString);
end;
The request events inherited from
TTMSMCPCloudBase —
OnRequestStarted, OnRequestProgress, OnRequestComplete — fire for the
generation request and for every poll it triggers. On a polling service that
means several started events for one image, so drive a progress indicator from
them but do not count generations with them.
One failure has no event at all: an empty APIKey makes Execute,
RemoveBackground, and ReplaceBackground raise Please fill in the API Key.
at the call site. Check the key before the call, or handle the exception there.
Combining reference images, a background replacement, and the saved result
A realistic pipeline chains the calls: compose from several references, restage
the composition against a new background, and save each stage. Because every
stage ends in the same event, the chaining happens inside OnImageGenerated:
procedure TForm1.RunCampaignShot(const ASubjectFiles: TArray<string>;
const ABackgroundFile: string);
var
FileName: string;
begin
FStage := csCompose;
FBackgroundFile := ABackgroundFile;
{ 1. Several reference images through the Images collection. }
ImageAI.Images.Clear;
for FileName in ASubjectFiles do
ImageAI.Images.Add.Stream.LoadFromFile(FileName);
{ 2. One prompt over all of them - this is still Execute(APrompt), it is
the populated collection that makes it a multi-reference request. }
ImageAI.Execute('Arrange these products on a single neutral surface, ' +
'evenly lit, seen slightly from above.');
end;
procedure TForm1.ImageAIImageGenerated(Sender: TObject;
ARequestResult: TTMSMCPCloudBaseRequestResult; ABase64Image: string);
var
Stream, Background: TMemoryStream;
begin
{ 3. Save whatever stage just completed. }
Stream := TMemoryStream.Create;
try
TTMSMCPUtils.LoadStreamFromBase64(ABase64Image, Stream);
Stream.SaveToFile(TPath.Combine(FOutputFolder,
Format('stage-%d.png', [Ord(FStage)])));
if FStage = csCompose then
begin
{ 4. Feed the composed image straight into a background replacement.
Clear Images first: the collection is not consumed by Execute,
and the reference images are no longer wanted. }
ImageAI.Images.Clear;
FStage := csRestage;
Background := TMemoryStream.Create;
try
Background.LoadFromFile(FBackgroundFile);
Stream.Position := 0;
ImageAI.ReplaceBackground(Stream, Background);
finally
Background.Free;
end;
end
else
begin
Stream.Position := 0;
imgResult.Bitmap.LoadFromStream(Stream);
end;
finally
Stream.Free;
end;
end;
The state field is what makes this readable. Both stages arrive through one handler, so something has to say which one just finished — and the same field tells the handler whether to chain again or stop.
Common mistakes
- Reading the result from the
Executecall. It returns as soon as the request is dispatched. Everything happens inOnImageGeneratedorOnRequestError. - Leaving
Imagespopulated. The collection is not cleared byExecute, so a leftover reference turns the next text-to-image request into an edit. Clear it before a fresh generation. - Re-enabling the UI in only one result path. Both events are terminal. Handle the error path too, or the control stays disabled after a failure.
- Not rewinding the decoded stream. After
SaveToFilethe position is at the end; a picture control then loads nothing. SetPosition := 0. - Passing the background image first to
ReplaceBackground. Subject first, background second. The wrong order is not rejected. - Expecting a dedicated background endpoint everywhere. Only some services
have one; elsewhere the call is a written instruction. Use
Executewith your own prompt when the shortcut is not precise enough. - Calling
Executewith an emptyAPIKey. It raises at the call site rather than reporting throughOnRequestError. - Counting
OnRequestStartedas one per image. A polling service fires it for every poll as well.
See also
- Providers and options — services, models, custom options, and polling
- Getting started — the first request end to end
- Cloud AI — the text side of the same idea
TTMSMCPCloudImageAI,TTMSMCPCloudAIImages,TTMSMCPCloudAIImage,TTMSMCPCloudBaseRequestResult