Laravel AI SDK 图像生成实战

摘要

Laravel AI SDK 提供了统一的图像生成 API,支持 DALL-E、Stable Diffusion 等多种图像生成模型。本文将深入讲解如何使用 Laravel AI SDK 进行图像生成,包括:

  • 图像生成 API 基础用法
  • 支持的图像生成模型
  • 图像尺寸与质量控制
  • 图像编辑与变体生成
  • 批量图像生成
  • 实战案例:构建 AI 图像生成器

本文适合希望在 Laravel 应用中集成图像生成功能的开发者。

1. 图像生成概述

1.1 支持的模型

模型提供商特点
DALL-E 3OpenAI高质量、创意性强
DALL-E 2OpenAI编辑功能、变体生成
Stable Diffusion多提供商开源、可定制
ImagenGoogle真实感强
Midjourney 风格兼容 API艺术风格

1.2 基本用法

1
2
3
4
5
use Laravel\Ai\Image;

$image = Image::of('A serene mountain landscape at sunset')->generate();

$rawContent = (string) $image;

1.3 保存图像

1
2
3
$image = Image::of('A cute cat wearing a hat')->generate();

Storage::put('generated/cat.png', $image->toBinary());

2. 图像生成配置

2.1 模型选择

1
2
3
$image = Image::of('A futuristic city')
->usingModel('dall-e-3')
->generate();

2.2 尺寸设置

1
2
3
4
5
6
7
8
9
10
11
12
$image = Image::of('A beautiful garden')
->size(1024, 1024) // 1024x1024
->generate();

// 预设尺寸
$image = Image::of('A portrait')
->size('portrait') // 1024x1792
->generate();

$image = Image::of('A landscape')
->size('landscape') // 1792x1024
->generate();

2.3 质量设置

1
2
3
$image = Image::of('A detailed illustration')
->quality('hd') // 'standard' 或 'hd'
->generate();

2.4 风格设置

1
2
3
$image = Image::of('A sunset over the ocean')
->style('vivid') // 'vivid' 或 'natural'
->generate();

3. 图像编辑

3.1 基于图像编辑

1
2
3
$image = Image::edit($existingImage)
->prompt('Add a rainbow in the sky')
->generate();

3.2 蒙版编辑

1
2
3
4
$image = Image::edit($existingImage)
->mask($maskImage) // 白色区域将被修改
->prompt('Replace with a sunset')
->generate();

3.3 局部重绘

1
2
3
4
$image = Image::inpaint($originalImage)
->mask($maskImage)
->prompt('A beautiful flower')
->generate();

4. 图像变体

4.1 生成变体

1
2
3
4
5
6
7
$variants = Image::of($originalImage)
->variations(4) // 生成 4 个变体
->generate();

foreach ($variants as $variant) {
Storage::put('variants/' . uniqid() . '.png', $variant);
}

4.2 变体配置

1
2
3
4
$variants = Image::of($originalImage)
->variations(3)
->size(512, 512)
->generate();

5. 批量生成

5.1 批量提示词

1
2
3
4
5
6
7
8
9
10
11
12
13
$prompts = [
'A cat wearing a hat',
'A dog wearing glasses',
'A bird wearing a bowtie',
];

$images = Image::batch($prompts)
->size(512, 512)
->generate();

foreach ($images as $index => $image) {
Storage::put("batch/image_{$index}.png", $image);
}

5.2 队列处理

1
2
3
4
5
6
use Laravel\Ai\Jobs\GenerateImage;

foreach ($prompts as $prompt) {
GenerateImage::dispatch($prompt)
->onQueue('image-generation');
}

6. 高级功能

6.1 负面提示词

1
2
3
$image = Image::of('A beautiful woman')
->negativePrompt('blurry, low quality, distorted')
->generate();

6.2 种子控制

1
2
3
$image = Image::of('A random pattern')
->seed(12345) // 固定种子确保可重复
->generate();

6.3 步数控制

1
2
3
$image = Image::of('A detailed painting')
->steps(50) // Stable Diffusion 步数
->generate();

6.4 CFG Scale

1
2
3
$image = Image::of('A fantasy castle')
->cfgScale(7.5) // 提示词相关性
->generate();

7. 实战案例:AI 图像生成器

7.1 控制器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
<?php

namespace App\Http\Controllers;

use Laravel\Ai\Image;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;

class ImageGeneratorController extends Controller
{
public function generate(Request $request)
{
$validated = $request->validate([
'prompt' => 'required|string|max:1000',
'size' => 'nullable|in:256,512,1024,portrait,landscape',
'quality' => 'nullable|in:standard,hd',
'style' => 'nullable|in:vivid,natural',
]);

$image = Image::of($validated['prompt'])
->size($validated['size'] ?? '1024')
->quality($validated['quality'] ?? 'standard')
->style($validated['style'] ?? 'vivid')
->generate();

$filename = 'generated/' . uniqid() . '.png';
Storage::put($filename, $image->toBinary());

return response()->json([
'url' => Storage::url($filename),
'revised_prompt' => $image->revisedPrompt(),
]);
}

public function edit(Request $request)
{
$validated = $request->validate([
'image' => 'required|image|max:4096',
'prompt' => 'required|string|max:1000',
'mask' => 'nullable|image|max:4096',
]);

$imagePath = $request->file('image')->store('temp');
$imageContent = Storage::get($imagePath);

$edited = Image::edit($imageContent)
->prompt($validated['prompt'])
->when($request->hasFile('mask'), function ($image) use ($request) {
$maskPath = $request->file('mask')->store('temp');
return $image->mask(Storage::get($maskPath));
})
->generate();

$filename = 'edited/' . uniqid() . '.png';
Storage::put($filename, $edited->toBinary());

Storage::delete([$imagePath]);
if ($request->hasFile('mask')) {
Storage::delete([$maskPath ?? null]);
}

return response()->json([
'url' => Storage::url($filename),
]);
}

public function variations(Request $request)
{
$validated = $request->validate([
'image' => 'required|image|max:4096',
'count' => 'nullable|integer|min:1|max:4',
]);

$imagePath = $request->file('image')->store('temp');
$imageContent = Storage::get($imagePath);

$variants = Image::of($imageContent)
->variations($validated['count'] ?? 1)
->generate();

$urls = [];
foreach ($variants as $index => $variant) {
$filename = 'variants/' . uniqid() . '.png';
Storage::put($filename, $variant->toBinary());
$urls[] = Storage::url($filename);
}

Storage::delete($imagePath);

return response()->json(['urls' => $urls]);
}
}

7.2 Livewire 组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?php

namespace App\Http\Livewire;

use Livewire\Component;
use Livewire\WithFileUploads;
use Laravel\Ai\Image;
use Illuminate\Support\Facades\Storage;

class ImageGenerator extends Component
{
use WithFileUploads;

public string $prompt = '';
public string $size = '1024';
public string $quality = 'standard';
public string $style = 'vivid';

public $generatedImage = null;
public bool $generating = false;

public function generate()
{
$this->validate([
'prompt' => 'required|min:10|max:1000',
]);

$this->generating = true;

try {
$image = Image::of($this->prompt)
->size($this->size)
->quality($this->quality)
->style($this->style)
->generate();

$filename = 'generated/' . uniqid() . '.png';
Storage::put($filename, $image->toBinary());

$this->generatedImage = Storage::url($filename);
} finally {
$this->generating = false;
}
}

public function render()
{
return view('livewire.image-generator');
}
}

7.3 视图模板

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
<div class="image-generator">
<div class="form-group">
<label>Prompt</label>
<textarea
wire:model="prompt"
class="form-control"
rows="3"
placeholder="Describe the image you want to generate..."
></textarea>
</div>

<div class="row">
<div class="col-md-4">
<label>Size</label>
<select wire:model="size" class="form-control">
<option value="256">256x256</option>
<option value="512">512x512</option>
<option value="1024" selected>1024x1024</option>
<option value="portrait">Portrait (1024x1792)</option>
<option value="landscape">Landscape (1792x1024)</option>
</select>
</div>

<div class="col-md-4">
<label>Quality</label>
<select wire:model="quality" class="form-control">
<option value="standard">Standard</option>
<option value="hd">HD</option>
</select>
</div>

<div class="col-md-4">
<label>Style</label>
<select wire:model="style" class="form-control">
<option value="vivid">Vivid</option>
<option value="natural">Natural</option>
</select>
</div>
</div>

<button
wire:click="generate"
wire:loading.attr="disabled"
class="btn btn-primary mt-3"
>
<span wire:loading.remove>Generate Image</span>
<span wire:loading>Generating...</span>
</button>

@if($generatedImage)
<div class="mt-4">
<img src="{{ $generatedImage }}" class="img-fluid rounded" alt="Generated image">
<a href="{{ $generatedImage }}" download class="btn btn-success mt-2">
Download
</a>
</div>
@endif
</div>

8. 图像处理管道

8.1 处理流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class ImagePipeline
{
public function process(string $prompt): array
{
$results = [];

foreach (['dall-e-3', 'stable-diffusion'] as $model) {
$image = Image::of($prompt)
->usingModel($model)
->generate();

$filename = "pipeline/{$model}_" . uniqid() . '.png';
Storage::put($filename, $image->toBinary());

$results[$model] = Storage::url($filename);
}

return $results;
}
}

8.2 后处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
use Intervention\Image\Facades\Image as Intervention;

class ImagePostProcessor
{
public function process($generatedImage): string
{
$img = Intervention::make($generatedImage);

$img->resize(800, null, function ($constraint) {
$constraint->aspectRatio();
});

$img->insert(
public_path('watermark.png'),
'bottom-right',
10,
10
);

return $img->encode('png');
}
}

9. 成本控制

9.1 成本估算

1
2
3
4
5
6
7
8
9
use Laravel\Ai\Cost;

$cost = Cost::forImageGeneration(
size: '1024x1024',
quality: 'hd',
model: 'dall-e-3'
);

echo $cost->inDollars(); // 0.08

9.2 用户配额

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ImageGenerationService
{
public function canGenerate(User $user): bool
{
$dailyLimit = $user->subscription->daily_image_limit;
$todayCount = $user->generatedImages()
->whereDate('created_at', today())
->count();

return $todayCount < $dailyLimit;
}

public function generate(User $user, string $prompt): Image
{
if (!$this->canGenerate($user)) {
throw new QuotaExceededException('Daily image limit reached');
}

$image = Image::of($prompt)->generate();

$user->generatedImages()->create([
'prompt' => $prompt,
'path' => $this->store($image),
]);

return $image;
}
}

10. 最佳实践

10.1 提示词优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PromptOptimizer
{
public function enhance(string $prompt): string
{
$enhancements = [
'highly detailed',
'professional photography',
'8k resolution',
'dramatic lighting',
];

return $prompt . ', ' . implode(', ', $enhancements);
}
}

10.2 错误处理

1
2
3
4
5
6
7
8
9
try {
$image = Image::of($prompt)->generate();
} catch (ContentPolicyException $e) {
return back()->with('error', 'Your prompt violates content policy');
} catch (RateLimitException $e) {
return back()->with('error', 'Too many requests. Please try again later');
} catch (AiException $e) {
return back()->with('error', 'Image generation failed');
}

10.3 缓存策略

1
2
3
4
5
6
7
8
9
10
11
12
13
use Illuminate\Support\Facades\Cache;

function getCachedImage(string $prompt): string
{
$key = 'image:' . md5($prompt);

return Cache::remember($key, now()->addDays(7), function () use ($prompt) {
$image = Image::of($prompt)->generate();
$filename = 'cached/' . uniqid() . '.png';
Storage::put($filename, $image->toBinary());
return Storage::url($filename);
});
}

11. 总结

Laravel AI SDK 的图像生成功能为 Laravel 应用提供了强大的 AI 绘画能力:

  1. 统一 API:支持多种图像生成模型
  2. 灵活配置:尺寸、质量、风格可定制
  3. 编辑能力:支持图像编辑和变体生成
  4. 批量处理:队列支持大规模生成
  5. 成本控制:内置成本估算和配额管理

通过本指南,您已经掌握了 Laravel AI SDK 图像生成的核心功能,可以开始构建自己的 AI 图像生成应用了。

参考资料