函数/New-GPT图像.ps1
|
function New-GPT图像 { <# .SYNOPSIS 调用 gpt-image-2 模型生成图像。 .DESCRIPTION 通过 OpenAI 兼容接口生成图像,自动处理 b64_json / url 两种返回形式。 密钥、基础地址与模型以 DPAPI 加密记住。 有参考图时走 images/edits 端点,否则走 images/generations。 .PARAMETER 提示词 图像描述。与 -提示文件 二选一。 .PARAMETER 提示文件 提示词文本文件路径。与 -提示词 二选一。 .PARAMETER 密钥 API 令牌。未指定时查找已记住的值。 .PARAMETER 基础地址 Base URL。未指定时查找已记住的值。 .PARAMETER 模型 模型名。未指定时查找已记住的值。 .PARAMETER 尺寸 1024x1024 | 1536x1024 | 1024x1536 | auto。默认 auto。 .PARAMETER 质量 low | medium | high | auto。默认 auto。 .PARAMETER 输出路径 输出文件路径。默认时间戳 PNG。 .PARAMETER 超时秒数 超时时间,默认 300。 .PARAMETER 参考图 参考图像路径(可多张)。 .PARAMETER 背景 opaque | transparent | auto。默认不指定。 .PARAMETER 蒙版 蒙版图像路径。仅在有参考图时生效。 .EXAMPLE New-GPT图像 -提示词 "水彩柴犬" -密钥 'sk-xxx' -基础地址 'https://open.cherryin.net/v1' #> [CmdletBinding(DefaultParameterSetName = '提示词')] param( [Parameter(Mandatory = $true, Position = 0, ParameterSetName = '提示词')] [ValidateNotNullOrEmpty()] [string]$提示词, [Parameter(Mandatory = $true, ParameterSetName = '提示文件')] [ValidateNotNullOrEmpty()] [string]$提示文件, [Parameter()][string]$密钥, [Parameter()][string]$基础地址, [Parameter()][string]$模型, [Parameter()] [ValidateSet('1024x1024', '1536x1024', '1024x1536', 'auto')] [string]$尺寸 = 'auto', [Parameter()] [ValidateSet('low', 'medium', 'high', 'auto')] [string]$质量 = 'auto', [Parameter()] [string]$输出路径 = ".\gpt-image-2_$(Get-Date -Format 'yyyyMMdd_HHmmss').png", [Parameter()][int]$超时秒数 = 300, [Parameter()] [ValidateNotNullOrEmpty()] [string[]]$参考图, [Parameter()][string]$背景 = '', [Parameter()][string]$蒙版 = '' ) $配置路径 = Join-Path $env:LOCALAPPDATA 'Image-generation-cli\AI图像生成-GPT配置.xml' # 提示词 if ($PSCmdlet.ParameterSetName -eq '提示文件') { $提示词 = Read-提示文件 -路径 $提示文件 } # 凭据 $记住的配置 = Import-记住的配置 -配置路径 $配置路径 $凭据 = Resolve-配置凭据 -参数密钥 $密钥 -参数基础地址 $基础地址 -参数模型 $模型 ` -记住的配置 $记住的配置 -配置路径 $配置路径 # 参考图 $参考图路径列表 = @() if ($参考图) { foreach ($单张 in $参考图) { if (-not (Test-Path -LiteralPath $单张 -PathType Leaf)) { throw "参考图不存在:$单张" } $参考图路径列表 += $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($单张) } } # 背景 if ($背景 -and $背景 -notin @('opaque', 'transparent', 'auto')) { throw "背景取值无效:$背景。有效值:opaque、transparent、auto" } # 蒙版 $蒙版路径 = $null if ($蒙版) { if (-not (Test-Path -LiteralPath $蒙版 -PathType Leaf)) { throw "蒙版文件不存在:$蒙版" } if ($参考图路径列表.Count -eq 0) { throw "蒙版仅在指定 -参考图 时有效。" } $蒙版路径 = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($蒙版) } # 构造请求 if ($参考图路径列表.Count -gt 0) { Add-Type -AssemblyName System.Net.Http $端点 = "$($凭据.基础地址.TrimEnd('/'))/images/edits" $表单 = [System.Net.Http.MultipartFormDataContent]::new() $表单.Add([System.Net.Http.StringContent]::new($凭据.模型), 'model') $表单.Add([System.Net.Http.StringContent]::new($提示词), 'prompt') $表单.Add([System.Net.Http.StringContent]::new('1'), 'n') $表单.Add([System.Net.Http.StringContent]::new('low'), 'moderation') if ($尺寸 -ne 'auto') { $表单.Add([System.Net.Http.StringContent]::new($尺寸), 'size') } if ($质量 -ne 'auto') { $表单.Add([System.Net.Http.StringContent]::new($质量), 'quality') } if ($背景) { $表单.Add([System.Net.Http.StringContent]::new($背景), 'background') } if ($蒙版路径) { $mc = [System.Net.Http.ByteArrayContent]::new([System.IO.File]::ReadAllBytes($蒙版路径)) $mc.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('application/octet-stream') $表单.Add($mc, 'mask', [System.IO.Path]::GetFileName($蒙版路径)) } $字段名 = $(if ($参考图路径列表.Count -gt 1) { 'image[]' } else { 'image' }) foreach ($p in $参考图路径列表) { $ic = [System.Net.Http.ByteArrayContent]::new([System.IO.File]::ReadAllBytes($p)) $ic.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::Parse('application/octet-stream') $表单.Add($ic, $字段名, [System.IO.Path]::GetFileName($p)) } Write-Host "正在调用 $($凭据.模型) 生成图像 ..." -ForegroundColor Cyan $客户端 = [System.Net.Http.HttpClient]::new() $客户端.Timeout = [TimeSpan]::FromSeconds($超时秒数) $客户端.DefaultRequestHeaders.Authorization = [System.Net.Http.Headers.AuthenticationHeaderValue]::new('Bearer', $凭据.密钥明文) try { $响应消息 = $客户端.PostAsync($端点, $表单).GetAwaiter().GetResult() $响应文本 = $响应消息.Content.ReadAsStringAsync().GetAwaiter().GetResult() if (-not $响应消息.IsSuccessStatusCode) { throw "API 请求失败(HTTP $([int]$响应消息.StatusCode)):$响应文本" } $响应 = $响应文本 | ConvertFrom-Json } catch [System.Management.Automation.MethodInvocationException] { throw "API 请求失败:$($_.Exception.InnerException.Message)" } catch { throw "API 请求失败:$($_.Exception.Message)" } finally { $表单.Dispose(); $客户端.Dispose() } } else { $端点 = "$($凭据.基础地址.TrimEnd('/'))/images/generations" $请求头 = @{ 'Authorization' = "Bearer $($凭据.密钥明文)"; 'Content-Type' = 'application/json' } $请求体 = @{ model = $凭据.模型; prompt = $提示词; n = 1; moderation = 'low' } if ($尺寸 -ne 'auto') { $请求体['size'] = $尺寸 } if ($质量 -ne 'auto') { $请求体['quality'] = $质量 } if ($背景) { $请求体['background'] = $背景 } $请求体 = $请求体 | ConvertTo-Json -Depth 5 Write-Host "正在调用 $($凭据.模型) 生成图像 ..." -ForegroundColor Cyan try { $响应 = Invoke-RestMethod -Uri $端点 -Method Post -Headers $请求头 ` -Body ([System.Text.Encoding]::UTF8.GetBytes($请求体)) -TimeoutSec $超时秒数 } catch { $错误详情 = $_.ErrorDetails.Message if (-not $错误详情) { $错误详情 = $_.Exception.Message } throw "API 请求失败:$错误详情" } } # 记住凭据 if ($凭据.密钥来源 -ne '已记住' -or $凭据.基础地址来源 -ne '已记住' -or $凭据.模型来源 -ne '已记住') { try { Save-配置 -安全密钥 $凭据.安全密钥对象 -保存基础地址 $凭据.基础地址 -保存模型 $凭据.模型 -配置路径 $配置路径 Write-Host "密钥、基础地址与模型已记住:$配置路径" -ForegroundColor DarkGray } catch { Write-Warning "配置保存失败:$($_.Exception.Message)" } } # 解析响应 if (-not $响应.data -or $响应.data.Count -eq 0) { throw "API 未返回图像数据:$($响应 | ConvertTo-Json -Depth 10 -Compress)" } $图像 = $响应.data[0] $改写 = $图像.PSObject.Properties['revised_prompt'] if ($改写 -and $改写.Value) { Write-Host "改写后的提示词: $($改写.Value)" -ForegroundColor DarkGray } $解析后输出 = Resolve-输出路径 -输出路径 $输出路径 if ($图像.b64_json) { [System.IO.File]::WriteAllBytes($解析后输出, [Convert]::FromBase64String($图像.b64_json)) } elseif ($图像.url) { Invoke-WebRequest -Uri $图像.url -OutFile $解析后输出 -TimeoutSec $超时秒数 } else { throw "响应中既没有 b64_json 也没有 url。" } Write-Host "图像已保存: $解析后输出" -ForegroundColor Green return $解析后输出 } |