增加ai去特征
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/jpeg"
|
||||
"image/png"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
_ "image/gif"
|
||||
|
||||
_ "golang.org/x/image/webp"
|
||||
)
|
||||
|
||||
// applyDeAI post-processes a generated image to weaken AI-detection artifacts:
|
||||
// a small edge crop (kills border fingerprints), per-pixel low-amplitude noise,
|
||||
// a subtle brightness/contrast jitter, and a JPEG re-encode round-trip that
|
||||
// both introduces natural compression statistics and strips any embedded
|
||||
// metadata/watermark chunks. Output is PNG (matching the stored content type).
|
||||
func applyDeAI(b []byte) ([]byte, error) {
|
||||
src, _, err := image.Decode(bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bounds := src.Bounds()
|
||||
w, h := bounds.Dx(), bounds.Dy()
|
||||
|
||||
// Crop a sliver off each edge (~0.3% of the dimension, 2–12px).
|
||||
cropX := clampInt(w*3/1000, 2, 12)
|
||||
cropY := clampInt(h*3/1000, 2, 12)
|
||||
if w <= cropX*4 || h <= cropY*4 {
|
||||
cropX, cropY = 0, 0
|
||||
}
|
||||
x0, y0 := bounds.Min.X+cropX, bounds.Min.Y+cropY
|
||||
x1, y1 := bounds.Max.X-cropX, bounds.Max.Y-cropY
|
||||
|
||||
rng := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// Subtle tone jitter: contrast ±2%, brightness ±2 levels.
|
||||
contrast := 1.0 + (rng.Float64()-0.5)*0.04
|
||||
brightness := (rng.Float64() - 0.5) * 4.0
|
||||
|
||||
out := image.NewRGBA(image.Rect(0, 0, x1-x0, y1-y0))
|
||||
for y := y0; y < y1; y++ {
|
||||
for x := x0; x < x1; x++ {
|
||||
r, g, bl, a := src.At(x, y).RGBA()
|
||||
out.SetRGBA(x-x0, y-y0, color.RGBA{
|
||||
R: jitterChannel(r, contrast, brightness, rng),
|
||||
G: jitterChannel(g, contrast, brightness, rng),
|
||||
B: jitterChannel(bl, contrast, brightness, rng),
|
||||
A: uint8(a >> 8),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// JPEG round-trip: natural compression statistics + strips metadata.
|
||||
var jbuf bytes.Buffer
|
||||
if err := jpeg.Encode(&jbuf, out, &jpeg.Options{Quality: 93}); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rt, err := jpeg.Decode(bytes.NewReader(jbuf.Bytes()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var pbuf bytes.Buffer
|
||||
if err := png.Encode(&pbuf, rt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pbuf.Bytes(), nil
|
||||
}
|
||||
|
||||
// jitterChannel applies contrast/brightness around mid-gray plus ±2 noise to a
|
||||
// 16-bit color channel, returning the clamped 8-bit value.
|
||||
func jitterChannel(v uint32, contrast, brightness float64, rng *rand.Rand) uint8 {
|
||||
f := float64(v>>8)
|
||||
f = (f-128.0)*contrast + 128.0 + brightness + (rng.Float64()-0.5)*4.0
|
||||
if f < 0 {
|
||||
f = 0
|
||||
}
|
||||
if f > 255 {
|
||||
f = 255
|
||||
}
|
||||
return uint8(f + 0.5)
|
||||
}
|
||||
|
||||
func clampInt(n, lo, hi int) int {
|
||||
if n < lo {
|
||||
return lo
|
||||
}
|
||||
if n > hi {
|
||||
return hi
|
||||
}
|
||||
return n
|
||||
}
|
||||
Reference in New Issue
Block a user