7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Ollamaの実験的な画像生成機能をDelphi(VCL)で利用する

Posted at

この記事は,2026/01/20に公開された Ollamaの Image generation (experimental) で実装された画像生成機能をmacOSで実行し,
Delphi 13で記述したアプリケーションから利用しています。
今後OllamaのAPIが変更されると動かなくなる可能性があります。

1. はじめに

2026/01/20にOllamaの画像生成機能の実験的実装が公開されました。
Z-Image TurboFLUX.2 Klein が利用できます。

まだ,macOSのOllamaでしか利用できませんが,既にAPIが公開されているのでOllamaをmacOSで動かして,Windowsから利用することが可能です。

そこで,Delphi 13 でVCLを使い macOS 上の Ollama にアクセスして画像を生成するプログラムを作ります。

2. 準備

macOSにOllamaをインストール

まだWindowsやLinuxでは画像生成できませんので,macOSにOllamaをインストールして,利用するモデルをダウンロードします。以下からダウンロードできます。

OllamaはHomebrewでインストールできるようです。

brew install ollama

モデルをインストール

Z-Image Turbo または FLUX.2 Kleinをインストールします。

Z-Image Turbo

Z-Image Turbo は Alibaba の Tongyi Lab が開発しています。 Apache 2.0 ライセンスで利用できます。

Ollamaのモデルのページは以下にあります。

コマンドラインから以下のコマンドの実行でインストールできます。

ollama run x/z-image-turbo

FLUX.2 Klein

FLUX.2 Klein は Black Forest Labs が開発しています。4Bモデルは Apache 2.0 ライセンスですが,9モデルは FLUX Non-Commercial License v2.1 となっているので利用には注意が必要です。

Ollamaのモデルのページは以下にあります。

コマンドラインから以下のコマンドの実行でインストールできます。

ollama run x/flux2-klein

私は,flux2-klein:4b を利用しました。

macOS の Ollama を Windows から使う

コマンドラインからOllamaを実行すると,ツールバーにアイコンが表示されます。アイコンをクリックしてメニューを開き, Settings... を選択して, Expose Ollama to the network をオンにします。
この設定により,ローカルネットワークからOllamaにアクセスできるようになります。

Delphi13 で Ollama を使うためのUnitを用意する。

以下の2つのUnitを作成しました。
KP.OllamaImageThreads.pasは,Ollamaへのアクセスをスレッドで行うためのUnitで,KP.OllamaImageUtils.pasは,KP.OllamaImageThreads.pasを利用して簡単にOllamaの画像生成機能を利用するためのUnitです。

KP.OllamaImageThreads.pas
KP.OllamaImageThreads.pas
unit KP.OllamaImageThreads;
(*
  Ollama Image generation Thread Unit (experimental)
  Please check https://ollama.com/blog/image-generation
  Please use it from KP.OllamaImageUtils.
  01/26/2026

  LICENSE
  Copyright (c) 2026 Yuzuru Kato
  Released under the MIT license
  http://opensource.org/licenses/mit-license.php
*)
interface

uses
  System.Types, System.Classes,
  REST.Client; // REST

const
  gOllamaTimeout = 60000;   // Generateのタイムアウト

type
  TOllamaImageParamIn = record
    Host:  string;
    Model: string;
    temperature:  single;
    top_p:        single;  // top-kと連携します。値が大きいほど(0.95など),テキストの多様性が増し,値を小さくする(0.5など)テキストは,より焦点を絞った保守的なテキストが生成されます。(デフォルト: 0.9)
    Sleep:        integer; // OllamaThreadChatQueueのループの休み
    Timeout:      integer; // GenerateとChatのタイムアウト
    // 以下はOllamaのみ適用される
    num_ctx:      integer; // コンテキスト長
    num_predict:  integer; // 無限生成
    top_k:        integer; // ナンセンスを生成する確率を減らします。値が大きいほど(例:100),回答が多様化し,値が低いほど(例:10)保守的になります。(デフォルト: 40)
    min_p:        single;  // top_pに代わるもので,品質と多様性のバランスを確保することを目指しています。パラメーター p は,最も可能性の高いトークンの確率に対する,トークンが考慮される最小の確率を表します。たとえば,p=0.05 で,最も可能性の高いトークンの確率が 0.9 の場合,値が 0.045 未満のロジットはフィルターで除外されます。(デフォルト: 0.0)
    KeepAlive:    string;  // Load Keep Alive 5s default
    width:        integer; // 出力画像の横サイズ
    height:       integer; // 出力画像の縦サイズ
    procedure Init;
  end;

type
  TOllamaImageParamOut = record
    tick_time        :Int64;
  end;

type
  TOllamaImageThreadOut    = procedure(AStream: TMemoryStream;
    AOllamaImageParamOut: TOllamaImageParamOut) of object;
  TOllamaImageThreadFinish = procedure of object;
  TOllamaThreadError = procedure(AText:string) of object;

type
  TOllamaImageGenerateThread = class(TThread)
  private
    { Private 宣言 }
  protected
    FOllamaImageParamIn:      TOllamaImageParamIn;
    FText:                    string;
    FOllamaImageThreadOut:    TOllamaImageThreadOut;
    FOllamaImageThreadFinish: TOllamaImageThreadFinish;
    FOllamaThreadError:       TOllamaThreadError;
    procedure Execute; override; abstract;
  public
    constructor Create(
      AOllamaImageParamIn:      TOllamaImageParamIn;
      AText:                    string;
      AOllamaImageThreadOut:    TOllamaImageThreadOut;
      AOllamaImageThreadFinish: TOllamaImageThreadFinish;
      AOllamaThreadError:       TOllamaThreadError); virtual;
  end;

type
  TOllamaImageGenerate = class(TOllamaImageGenerateThread)
  private
    { Private 宣言 }
  protected
    procedure Execute; override;
  end;

implementation

uses
  System.Diagnostics, // TStopWatch
  System.NetEncoding,
  System.JSON, System.JSON.Types, System.JSON.Serializers,  // TJsonSerializer
  REST.Types; // TRESTRequestMethod

const
  gOllamaErrorText: string = 'The following questions could not be answered:'; // エラー時に履歴に残す先頭文字列

const
  OllamaLastErrors:array[-3..-1] of string
  =('Couldn''t parse output.',       // -3 パースエラー
    'Couldn''t connect to Ollama. ', // -2 接続エラー
    'Ollama Error');                 // -1 その他のエラー

{ OllamaImageParamIn }

procedure TOllamaImageParamIn.Init;
begin
  temperature  :=  0.1; // 0.8;    0 - 2
  top_p:=0.1;  // 0.9;  // top-kと連携します。値が大きいほど(0.95など),テキストの多様性が増し,値を小さくする(0.5など)テキストは,より焦点を絞った保守的なテキストが生成されます。(デフォルト: 0.9)
  // スレッド用のパラメータ
  Sleep        :=   10;           // OllamaThreadChatQueueのループの休み
  Timeout      := gOllamaTimeout; // GenerateとChatのタイムアウト
  // 以下はOllamaのみ適用される
  num_ctx      := 2048; // コンテキスト長
  num_predict  :=   -1; // -1; 無限生成
  top_k        :=   10; // 40; ナンセンスを生成する確率を減らします。値が大きいほど(例:100),回答が多様化し,値が低いほど(例:10)保守的になります。(デフォルト: 40)
  min_p        :=  0.0; // top_pに代わるもので,品質と多様性のバランスを確保することを目指しています。パラメーター p は,最も可能性の高いトークンの確率に対する,トークンが考慮される最小の確率を表します。たとえば,p=0.05 で,最も可能性の高いトークンの確率が 0.9 の場合,値が 0.045 未満のロジットはフィルターで除外されます。(デフォルト: 0.0)
  KeepAlive    := '30m'; // '5m'; '5s'; ModelのKeep Alive -1:無制限 0:即時停止
  width        := 1024;
  height       := 1024;
end;

{ TOllamaImageGenerateThread }

constructor TOllamaImageGenerateThread.Create(
  AOllamaImageParamIn: TOllamaImageParamIn; AText: string;
  AOllamaImageThreadOut: TOllamaImageThreadOut;
  AOllamaImageThreadFinish: TOllamaImageThreadFinish;
  AOllamaThreadError: TOllamaThreadError);
begin
  inherited Create(False);
  FOllamaImageParamIn      := AOllamaImageParamIn;
  FText                    := AText;
  FOllamaImageThreadOut    := AOllamaImageThreadOut;
  FOllamaImageThreadFinish := AOllamaImageThreadFinish;
  FOllamaThreadError       := AOllamaThreadError;
  FreeOnTerminate          := True;
end;

// Ollama ----------------------------------------------------------------------

function OllamaURL(AHost,AMethod:string):string;
begin
  Result:=AHost+'/api/'+AMethod;
end;

procedure OllamaRESTRequestPOST(ARESTRequest: TRESTRequest);
begin
  ARESTRequest.Client   := TRESTClient.Create(ARESTRequest);
  ARESTRequest.Client.SynchronizedEvents := False;
  ARESTRequest.Method   := TRESTRequestMethod.rmPOST;
  ARESTRequest.Response := TRESTResponse.Create(ARESTRequest);
  ARESTRequest.Params.Clear;
  // 空のアイテムを作っておく
  ARESTRequest.Params.AddItem('OllamaJSON', '', TRESTRequestParameterKind.pkREQUESTBODY,
    [], TRESTContentType.ctAPPLICATION_JSON);
  ARESTRequest.SynchronizedEvents := False;
end;

function OllamaRESTExecute(ARESTRequest:TRESTRequest; var AResponse: string): integer; // 0:成功 -2:接続エラー
var
  i:integer;
begin
  Result := -1;
  for i:=0 to 1 do begin
    try
      ARESTRequest.Execute;
    except
      Result := -2;
    end;
    if (ARESTRequest.Response.StatusCode=200) then break; // HTTP 応答ステータスが 200 OK 以外
  end;
  if (Result=-1)and(ARESTRequest.Response.StatusCode=200) then begin
    AResponse := ARESTRequest.Response.Content;
    Result    := 0;
  end else begin
    AResponse := '';
    Result    := -2; // -2:接続エラー
  end;
end;

{ TOllamaThreadGenerate2 }
function OllamaGenerate2ToJSON(AOllamaImageParamIn:TOllamaImageParamIn; AInput:String): string;
type
  TOllamaOptions=record
    num_ctx:integer;
    temperature: single;
    num_predict: integer; // テキストを生成するときに予測するトークンの最大数。(デフォルト: -1,無限生成)
    top_k: integer; // ナンセンスを生成する確率を減らします。値が大きいほど(例:100),回答が多様化し,値が低いほど(例:10)保守的になります。(デフォルト: 40)
    top_p: Single;  // top-kと連携します。値が大きいほど(0.95など),テキストの多様性が増し,値を小さくする(0.5など)テキストは,より焦点を絞った保守的なテキストが生成されます。(デフォルト: 0.9)
    min_p: Single;  // top_pに代わるもので,品質と多様性のバランスを確保することを目指しています。パラメーター p は,最も可能性の高いトークンの確率に対する,トークンが考慮される最小の確率を表します。たとえば,p=0.05 で,最も可能性の高いトークンの確率が 0.9 の場合,値が 0.045 未満のロジットはフィルターで除外されます。(デフォルト: 0.0)
  end;
  TOllama=record
    model:string;
    prompt:string;
    width:integer;
    height:integer;
    stream:boolean;
    options:TOllamaOptions;
  end;
var
  Ollama:TOllama;
  i,j:integer;
  s,f,e:string;
begin
  Result:='';
  Ollama.model  := AOllamaImageParamIn.Model;
  Ollama.prompt := AInput;
  Ollama.width  := AOllamaImageParamIn.Width;
  Ollama.Height := AOllamaImageParamIn.Height;

  Ollama.options.num_ctx     := AOllamaImageParamIn.num_ctx;
  Ollama.options.temperature := AOllamaImageParamIn.temperature;
  Ollama.options.num_predict := AOllamaImageParamIn.num_predict;
  Ollama.options.top_k       := AOllamaImageParamIn.top_k;
  Ollama.options.top_p       := AOllamaImageParamIn.top_p;
  Ollama.options.min_p       := AOllamaImageParamIn.min_p;

  Ollama.stream := False;
  var JsonSerializer := TJsonSerializer.Create;
  try
    JsonSerializer.Formatting := TJsonFormatting.Indented;
    Result := JsonSerializer.Serialize<TOllama>(Ollama);
  finally
    JsonSerializer.Free;
  end;
end;

function OllamaJSONToGenerate2(AContent: string; var AOutput: string;
  var AOllamaImageParamOut:TOllamaImageParamOut): integer;
type
  TOllama=record
    model:          string;
    created_at:     TDateTime;
    image:          string;
    done:           boolean;
    total_duration: Int64;
    load_duration:  Int64;
  end;
begin
  Result := -3;
  var JsonSerializer := TJsonSerializer.Create;
  try
    var JSONed  := JsonSerializer.Deserialize<TOllama>(AContent);
    AOutput := JSONed.image;
    AOllamaImageParamOut.tick_time := JSONed.total_duration; // TStopWatchを使う場合は不要
    Result := 0;
  finally
    JsonSerializer.Free;
  end;
end;

function OllamaImageGenerate(ARESTRequest:TRESTRequest; AOllamaImageParamIn: TOllamaImageParamIn;
  AText: string; var AOutPut: string;var AOllamaImageParamOut: TOllamaImageParamOut): integer;
var
  LastError: integer;
  LResponse: string;
begin
  LastError := -1;
  ARESTRequest.Client.ReadTimeout := AOllamaImageParamIn.Timeout;
  ARESTRequest.Client.BaseURL     := OllamaURL(AOllamaImageParamIn.Host,'generate');
  ARESTRequest.Params[0].Value    := OllamaGenerate2ToJSON(AOllamaImageParamIn, AText);
  LastError := OllamaRESTExecute(ARESTRequest, LResponse);
  if LastError=0 then begin
    LastError := OllamaJSONToGenerate2(LResponse,AOutput,AOllamaImageParamOut);
    if LastError<>0 then begin
      AOutput := '';
      AOllamaImageParamOut.tick_time         := 0;
    end;
  end;
  Result := LastError;
end;

procedure TOllamaImageGenerate.Execute;
var
  LRESTRequest:         TRESTRequest;
  LText:                string;
  LOllamaImageParamOut: TOllamaImageParamOut;
  isDone:               boolean;
  LastError:            integer;
  StopWatch:            TStopWatch; // Ollamaからの値を使う場合は不要
begin
  LRESTRequest := TRESTRequest.Create(nil);
  OllamaRESTRequestPOST(LRESTRequest);

  StopWatch := TStopWatch.StartNew;                                // Ollamaからの値を使う場合は不要
  LastError := OllamaImageGenerate(LRESTRequest, FOllamaImageParamIn, FText,
    LText, LOllamaImageParamOut);
  LOllamaImageParamOut.tick_time := StopWatch.ElapsedMilliseconds; // Ollamaからの値を使う場合は不要
  if LastError=0 then begin
    if LText='' then begin // 回答が空なら回答できなかった扱いにする
      LText := gOllamaErrorText+' '+FText;
    end;
    if Assigned(FOllamaImageThreadOut) then begin
      Synchronize(
        procedure
        var
          StringStream: TStringStream;
          MemoryStream: TMemoryStream;
        begin
          StringStream := TStringStream.Create(LText);
          MemoryStream := TMemoryStream.Create;
          TNetEncoding.Base64.Decode(StringStream, MemoryStream);
          MemoryStream.Position := 0;
          StringStream.Free;
          FOllamaImageThreadOut(MemoryStream, LOllamaImageParamOut);
          MemoryStream.Free;
        end
      );
    end;
  end else begin
    LText := gOllamaErrorText+' '+FText;
    if Assigned(FOllamaThreadError) then begin
      Synchronize(
        procedure
        begin
          FOllamaThreadError(OllamaLastErrors[LastError]);
        end
      );
    end;
  end;
  LRESTRequest.Response.Free;
  LRESTRequest.Client.Free;
  LRESTRequest.Free;
  if Assigned(FOllamaImageThreadFinish) then begin
    Synchronize(
      procedure
      begin
        FOllamaImageThreadFinish;
      end
    );
  end;
end;

end.
KP.OllamaImageUtils.pas
KP.OllamaImageUtils.pas
unit KP.OllamaImageUtils;
(*
  Utilities of KP.OllamaImageThreads (experimental)
  Please check https://ollama.com/blog/image-generation
  01/26/2026

  LICENSE
  Copyright (c) 2026 Yuzuru Kato
  Released under the MIT license
  http://opensource.org/licenses/mit-license.php
*)
interface

uses
  System.Classes,
  KP.OllamaImageThreads;

{ TOllamaImage }

type
  TOllamaImageOut    = procedure(AStream:TMemoryStream; AOllamaImageParamOut: TOllamaImageParamOut) of object;
  TOllamaImageIn     = procedure of object;
  TOllamaImageFinish = procedure of object;

  TOllamaImage = class(TObject)
  private
    FisThreadStart: boolean;
    FOllamaImageGenerateThread: TOllamaImageGenerateThread;
    FImageIn:       TOllamaImageIn;
    FImageOut:      TOllamaImageOut;
    FImageFinish:   TOllamaImageFinish;
    procedure ThreadImageOut(AStream: TMemoryStream; AOllamaImageParamOut: TOllamaImageParamOut);
    procedure ThreadImageFinish;
  protected
  public
    constructor Create;
    destructor  Destroy; override;
    procedure Open(AOllamaImageParamIn: TOllamaImageParamIn;
      AText:        string;
      AImageOut:    TOllamaImageOut;
      AImageFinish: TOllamaImageFinish;
      AThreadError: TOllamaThreadError);
    property isThreadStart: boolean read FisThreadStart;
  end;

implementation

uses
{$IFDEF FRAMEWORK_VCL}
  Vcl.Forms;
{$ENDIF FRAMEWORK_VCL}
{$IFDEF FRAMEWORK_FMX}
  FMX.Forms;
{$ENDIF FRAMEWORK_FMX}

constructor TOllamaImage.Create;
begin
  FisThreadStart             := False;
  FOllamaImageGenerateThread := nil;
end;

destructor TOllamaImage.Destroy;
begin
  if FisThreadStart then begin
    FOllamaImageGenerateThread.Terminate;
    while FisThreadStart do begin
      Application.ProcessMessages;
    end;
  end;
  inherited;
end;

procedure TOllamaImage.Open(AOllamaImageParamIn: TOllamaImageParamIn; AText: string;
  AImageOut: TOllamaImageOut; AImageFinish: TOllamaImageFinish;
  AThreadError: TOllamaThreadError);
begin
  FImageOut    := AImageOut;
  FImageFinish := AImageFinish;
  FOllamaImageGenerateThread := TOllamaImageGenerate.Create(AOllamaImageParamIn,
    AText, ThreadImageOut, ThreadImageFinish, AThreadError);
  FisThreadStart := True;
end;

procedure TOllamaImage.ThreadImageFinish;
begin
  FisThreadStart := False;
  if Assigned(FImageFinish) then FImageFinish;
end;

procedure TOllamaImage.ThreadImageOut(AStream: TMemoryStream;
  AOllamaImageParamOut: TOllamaImageParamOut);
begin
  if Assigned(FImageOut) then FImageOut(AStream, AOllamaImageParamOut);
end;

end.

サンプルプログラム

上記の2つのUnitを使ってVCLのサンプルプログラムを書きます。

フォームのデザイン

Windows VCLアプリケーションを新規作成し,表示されたForm1: TFormに以下のコンポーネントを配置します。

  • Memo1: TMmemo
  • Button1: TButton Memo1に書かれたプロンプトで画像を生成します。
  • Image1: TImage 生成された画像を表示します。以下のプロパティを設定します。
    Image1.Center: True
    Image1.Proportional: True
  • ActivityIndicator1: TActivityIndicator 「生成中」を表示します
    フォームのサイズを変形しても配置が崩れないようにそれぞれのコンポーネントのAnchorsプロパティを適切に設定してください。
    私は,以下のように配置しました。
    Snap20260126_0000.png

Usesの記述

interface の直下のuses節に以下を追加します。

  KP.OllamaImageThreads, KP.OllamaImageUtils;

用意した2つのUnitを利用するための宣言です。

さらに,interface の直下に以下のuses節を追加します。

uses
  Vcl.Imaging.pngimage;

画像生成されたデータがTMemoryStreamに格納されたPNG形式で渡されるため,それを出コードするために追加します。

Form1への追加

Form1の宣言に以下を追加します。

  private
    { Private 宣言 }
    FOllamaImage: TOllamaImage;
    procedure OllamaThreadError(AText:string);
    procedure OllamaImageThreadOut(AStream: TMemoryStream; AOpenAIParamOut:TOllamaImageParamOut);
    procedure OllamaImageThreadFinish;

OllamaThreadErrorでエラー表示をします。
OllamaImageThreadOutで画像生成された結果を受け取りImage1に表示します
`OllamaImageThreadFinish'でスレッドの終了時の処理を記述します

スレッド処理のためのイベント

implementationより下に,宣言された3つのメソッドを以下のように記述します。

procedure TForm1.OllamaThreadError(AText: string);
begin
  if AText<>'' then begin
    MessageDlg(AText, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  end;
end;

procedure TForm1.OllamaImageThreadOut(AStream: TMemoryStream;
  AOpenAIParamOut: TOllamaImageParamOut);
var
  PngImage: TPngImage;
begin
  PngImage := TPngImage.Create;
  PngImage.LoadFromStream(AStream);       
  Image1.Picture.Bitmap.Assign(PngImage); 
  PngImage.Free;
end;

procedure TForm1.OllamaImageThreadFinish;
begin
  ActivityIndicator1.Animate := False;
  Button1.Enabled            := True;
end;

FormCreateとFormDestroy

Form1OnCreateDestroyイベントを追加し以下のように記述します。
以下のようにKP.KP.OllamaImageUtilsのTOllamaImageを初期化し,終了する手続きを追加します。

procedure TForm1.FormCreate(Sender: TObject);
begin
  FOllamaImage := TOllamaImage.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FOllamaImage.Free;
end;

画像生成ボタン

Button1OnClickイベントを追加し以下のように記述します。

LHostには Ollamaの動作しているMacのIPアドレス を記述します。
http:// はプロトコルで,:11434Ollamaのポート番号 を書いています。

LModelは利用するモデルで
ollama run z-image-turbo でダウンロードしたら z-image-turbo:latest
ollama run x/flux2-klein でダウンロードしたら x/flux2-klein:latest
と書きます。

生成される画像サイズはMacのメモリサイズと相談しながら決めるとよいと思います。

procedure TForm1.Button1Click(Sender: TObject);
var
  LHost:               string;
  LModel:              string;
  LOllamaImageParamIn: TOllamaImageParamIn;
begin
  LHost  := 'http://192.168.1.XX:11434';
  LModel := 'x/flux2-klein:latest';
  if Memo1.Lines.Text='' then exit;
  LOllamaImageParamIn.Init;
  with LOllamaImageParamIn do begin
    Host         := LHost;
    Model        := LModel;
    Timeout      := 60000*20; // 20分でタイムアウトする
    num_ctx      := 2048; // コンテキスト長
    width        := 1024; // 生成される画像の幅
    height       := 1024; // 生成される画像の高さ
  end;
  ActivityIndicator1.Animate := True;
  FOllamaImage.Open(LOllamaImageParamIn, Memo1.Lines.Text,
    OllamaImageThreadOut, OllamaImageThreadFinish, OllamaThreadError);
  Button1.Enabled := False;
end;

するとUnit1は以下のようになります。

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
  Vcl.WinXCtrls,
  KP.OllamaImageThreads, KP.OllamaImageUtils;

type
  TForm1 = class(TForm)
    Memo1: TMemo;
    Image1: TImage;
    Button1: TButton;
    ActivityIndicator1: TActivityIndicator;
    procedure FormDestroy(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
    FOllamaImage: TOllamaImage;
    procedure OllamaThreadError(AText:string);
    procedure OllamaImageThreadOut(AStream: TMemoryStream; AOpenAIParamOut:TOllamaImageParamOut);
    procedure OllamaImageThreadFinish;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Vcl.Imaging.pngimage;

{ Form1 }

procedure TForm1.FormCreate(Sender: TObject);
begin
  FOllamaImage := TOllamaImage.Create;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FOllamaImage.Free;
end;

procedure TForm1.OllamaImageThreadOut(AStream: TMemoryStream;
  AOpenAIParamOut: TOllamaImageParamOut);
var
  PngImage: TPngImage;
begin
  PngImage := TPngImage.Create;
  PngImage.LoadFromStream(AStream);
  Image1.Picture.Bitmap.Assign(PngImage);
  PngImage.Free;
end;

procedure TForm1.OllamaThreadError(AText: string);
begin
  if AText<>'' then begin
    MessageDlg(AText, TMsgDlgType.mtError, [TMsgDlgBtn.mbOK], 0);
  end;
end;

procedure TForm1.OllamaImageThreadFinish;
begin
  ActivityIndicator1.Animate := False;
  Button1.Enabled            := True;
end;

// https://ollama.com/x/flux2-klein
procedure TForm1.Button1Click(Sender: TObject);
var
  LHost:               string;
  LModel:              string;
  LOllamaImageParamIn: TOllamaImageParamIn;
begin
  LHost  := 'http://192.168.1.XXX:11434'; // Ollamaを動かしているMacのIPアドレス 11434はOllamaのポート番号
  LModel := 'x/flux2-klein:latest';       // 利用しているモデル名 
  if Memo1.Lines.Text='' then exit;
  LOllamaImageParamIn.Init;
  with LOllamaImageParamIn do begin
    Host         := LHost;
    Model        := LModel;
    Timeout      := 60000*20; // 20分でタイムアウトする
    num_ctx      := 2048; // コンテキスト長
    width        := 1024; // 生成される画像の幅
    height       := 1024; // 生成される画像の高さ
  end;
  ActivityIndicator1.Animate := True;
  FOllamaImage.Open(LOllamaImageParamIn, Memo1.Lines.Text,
    OllamaImageThreadOut, OllamaImageThreadFinish, OllamaThreadError);
  Button1.Enabled := False;
end;

end.

Memo1 に 英語で プロンプトを書き,Button1をクリックしてしばらく待つと,Image1に生成された画像が表示されます。
Snap20260126_0002.png
文字が怪しいのはお許しください,,

おわりに

画像サイズを変更したり,表示された画像を保存する機能を追加するとより便利になると思います。
また,Ollamaを実行するホストを選択できるようにしたり,モデルを選択可能するような拡張ができると思います。

謝辞

@P-man_Brown さんの Qiita【Mac】Ollamaの導入方法 を参考にmacOSのOllama導入を書きました。この場を借りてお礼申し上げます。

誤って理解していることがあればご指摘いただけるとありがたいです。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?