fix: 兼容像素分辨率参数
This commit is contained in:
parent
97f29ed156
commit
a8fa8dd212
@ -207,6 +207,14 @@ func (imageSizeProcessor) Process(params map[string]any, modelType string, conte
|
|||||||
width, height = constrainImageDimensions(width, height, capability)
|
width, height = constrainImageDimensions(width, height, capability)
|
||||||
params["width"] = width
|
params["width"] = width
|
||||||
params["height"] = height
|
params["height"] = height
|
||||||
|
if stringFromAny(params["aspect_ratio"]) == "" {
|
||||||
|
aspectRatio := aspectRatioFromDimensions(width, height)
|
||||||
|
allowed := aspectRatioAllowed(capability["aspect_ratio_allowed"], firstNonEmptyString(stringFromAny(params["resolution"]), context.resolution))
|
||||||
|
if processed, ok := validateAndAdjustAspectRatio(aspectRatio, capability, allowed); ok && processed != "" {
|
||||||
|
params["aspect_ratio"] = processed
|
||||||
|
context.aspectRatio = processed
|
||||||
|
}
|
||||||
|
}
|
||||||
resolution := normalizeImageResolutionForCapability(firstNonEmptyString(stringFromAny(params["resolution"]), context.resolution), width, height, capability)
|
resolution := normalizeImageResolutionForCapability(firstNonEmptyString(stringFromAny(params["resolution"]), context.resolution), width, height, capability)
|
||||||
if resolution != "" {
|
if resolution != "" {
|
||||||
params["resolution"] = resolution
|
params["resolution"] = resolution
|
||||||
@ -248,7 +256,10 @@ func imageDimensionsFromParams(params map[string]any) (int, int, bool) {
|
|||||||
if width > 0 && height > 0 {
|
if width > 0 && height > 0 {
|
||||||
return width, height, true
|
return width, height, true
|
||||||
}
|
}
|
||||||
return parsePixelSizeString(stringFromAny(params["size"]))
|
if width, height, ok := parsePixelSizeString(stringFromAny(params["size"])); ok {
|
||||||
|
return width, height, true
|
||||||
|
}
|
||||||
|
return parsePixelSizeString(stringFromAny(params["resolution"]))
|
||||||
}
|
}
|
||||||
|
|
||||||
func imageSizeCapabilityConfigured(capability map[string]any) bool {
|
func imageSizeCapabilityConfigured(capability map[string]any) bool {
|
||||||
@ -431,6 +442,30 @@ func imageResolutionFromDimensions(width int, height int) string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func aspectRatioFromDimensions(width int, height int) string {
|
||||||
|
if width <= 0 || height <= 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
divisor := gcd(width, height)
|
||||||
|
return fmt.Sprintf("%d:%d", width/divisor, height/divisor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func gcd(a int, b int) int {
|
||||||
|
if a < 0 {
|
||||||
|
a = -a
|
||||||
|
}
|
||||||
|
if b < 0 {
|
||||||
|
b = -b
|
||||||
|
}
|
||||||
|
for b != 0 {
|
||||||
|
a, b = b, a%b
|
||||||
|
}
|
||||||
|
if a == 0 {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
func closestImageResolution(target string, allowed []string) string {
|
func closestImageResolution(target string, allowed []string) string {
|
||||||
order := []string{"1K", "2K", "3K", "4K", "8K"}
|
order := []string{"1K", "2K", "3K", "4K", "8K"}
|
||||||
targetIndex := indexOfString(order, target)
|
targetIndex := indexOfString(order, target)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package runner
|
package runner
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
"github.com/easyai/easyai-ai-gateway/apps/api/internal/store"
|
||||||
@ -699,6 +700,65 @@ func TestParamProcessorImageSizeConstraintsNormalizeExplicitDimensions(t *testin
|
|||||||
t.Fatalf("expected image size preprocessing log against output_size_range, got %+v", result.Log.Changes)
|
t.Fatalf("expected image size preprocessing log against output_size_range, got %+v", result.Log.Changes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestParamProcessorImageSizeConstraintsAcceptPixelResolutionStrings(t *testing.T) {
|
||||||
|
candidate := store.RuntimeModelCandidate{
|
||||||
|
ModelType: "image_generate",
|
||||||
|
Capabilities: map[string]any{
|
||||||
|
"image_generate": map[string]any{
|
||||||
|
"output_resolutions": []any{"1K", "2K", "4K"},
|
||||||
|
"aspect_ratio_allowed": []any{
|
||||||
|
"1:1",
|
||||||
|
"16:9",
|
||||||
|
"9:16",
|
||||||
|
},
|
||||||
|
"output_size_range": []any{655360, 8294400},
|
||||||
|
"width_height_range": []any{1, 3840},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, body := range map[string]map[string]any{
|
||||||
|
"resolution": {
|
||||||
|
"model": "gpt-image-2",
|
||||||
|
"prompt": "draw",
|
||||||
|
"resolution": "3840x2160",
|
||||||
|
},
|
||||||
|
"size": {
|
||||||
|
"model": "gpt-image-2",
|
||||||
|
"prompt": "draw",
|
||||||
|
"size": "3840x2160",
|
||||||
|
},
|
||||||
|
"non-standard-resolution": {
|
||||||
|
"model": "gpt-image-2",
|
||||||
|
"prompt": "draw",
|
||||||
|
"resolution": "3600x1900",
|
||||||
|
},
|
||||||
|
} {
|
||||||
|
t.Run(name, func(t *testing.T) {
|
||||||
|
processed := preprocessRequest("images.generations", body, candidate)
|
||||||
|
expectedWidth := 3840
|
||||||
|
expectedHeight := 2160
|
||||||
|
if name == "non-standard-resolution" {
|
||||||
|
expectedWidth = 3600
|
||||||
|
expectedHeight = 1900
|
||||||
|
}
|
||||||
|
if processed["width"] != expectedWidth || processed["height"] != expectedHeight {
|
||||||
|
t.Fatalf("pixel resolution string should populate width/height, got %+v", processed)
|
||||||
|
}
|
||||||
|
if processed["aspect_ratio"] != "16:9" {
|
||||||
|
t.Fatalf("pixel resolution string should infer aspect_ratio, got %+v", processed)
|
||||||
|
}
|
||||||
|
if processed["resolution"] != "4K" {
|
||||||
|
t.Fatalf("pixel resolution string should normalize resolution to allowed bucket, got %+v", processed)
|
||||||
|
}
|
||||||
|
expectedSize := fmt.Sprintf("%dx%d", expectedWidth, expectedHeight)
|
||||||
|
if processed["size"] != expectedSize {
|
||||||
|
t.Fatalf("size should stay synchronized with normalized width/height, got %+v", processed)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestParamProcessorImageSizeConstraintsNormalizeEditDimensions(t *testing.T) {
|
func TestParamProcessorImageSizeConstraintsNormalizeEditDimensions(t *testing.T) {
|
||||||
body := map[string]any{
|
body := map[string]any{
|
||||||
"model": "gpt-image-2",
|
"model": "gpt-image-2",
|
||||||
|
|||||||
@ -29,7 +29,7 @@ func validateAndAdjustAspectRatio(aspectRatio string, capability map[string]any,
|
|||||||
if containsString(allowed, aspectRatio) {
|
if containsString(allowed, aspectRatio) {
|
||||||
return aspectRatio, true
|
return aspectRatio, true
|
||||||
}
|
}
|
||||||
return allowed[0], true
|
return closestAspectRatio(aspectRatio, allowed), true
|
||||||
}
|
}
|
||||||
|
|
||||||
func isMediaModelTypeWithAspectRatio(capability map[string]any) bool {
|
func isMediaModelTypeWithAspectRatio(capability map[string]any) bool {
|
||||||
@ -445,6 +445,33 @@ func adjustAspectRatioToRange(value string, minValue float64, maxValue float64,
|
|||||||
return ratioString(maxValue)
|
return ratioString(maxValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func closestAspectRatio(value string, allowed []string) string {
|
||||||
|
if len(allowed) == 0 {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
current, ok := aspectRatioNumber(value)
|
||||||
|
if !ok {
|
||||||
|
return allowed[0]
|
||||||
|
}
|
||||||
|
closest := ""
|
||||||
|
minDiff := math.Inf(1)
|
||||||
|
for _, candidate := range allowed {
|
||||||
|
ratio, ok := aspectRatioNumber(candidate)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
diff := math.Abs(ratio - current)
|
||||||
|
if diff < minDiff {
|
||||||
|
minDiff = diff
|
||||||
|
closest = candidate
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if closest != "" {
|
||||||
|
return closest
|
||||||
|
}
|
||||||
|
return allowed[0]
|
||||||
|
}
|
||||||
|
|
||||||
func ratioString(value float64) string {
|
func ratioString(value float64) string {
|
||||||
if value <= 0 {
|
if value <= 0 {
|
||||||
return "1:1"
|
return "1:1"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user