图像色调计算
This commit is contained in:
		@@ -2,11 +2,14 @@ package api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"image"
 | 
				
			||||||
	"log"
 | 
						"log"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"git.satori.love/gameui/webp/models"
 | 
						"git.satori.love/gameui/webp/models"
 | 
				
			||||||
	"github.com/doug-martin/goqu/v9"
 | 
						"github.com/doug-martin/goqu/v9"
 | 
				
			||||||
@@ -104,18 +107,49 @@ func NewSchema(config Config) (graphql.Schema, error) {
 | 
				
			|||||||
		log.Fatalln("连接数据库失败", err)
 | 
							log.Fatalln("连接数据库失败", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//// 定时检查补全颜色字段
 | 
						// 定时检查补全颜色字段
 | 
				
			||||||
	//checkColorNullRows := func() {
 | 
						checkColorNullRows := func() {
 | 
				
			||||||
	//	for {
 | 
							for {
 | 
				
			||||||
	//		var ids struct {
 | 
								time.Sleep(10 * time.Second)
 | 
				
			||||||
	//			ID int
 | 
								var list []struct {
 | 
				
			||||||
	//		}
 | 
									ID      int
 | 
				
			||||||
	//		if err := db.Table("web_images").Where("color IS NULL").Scan(ids).Error; err != nil {
 | 
									Content string
 | 
				
			||||||
	//			fmt.Println("定时检查补全颜色字段查询失败", err)
 | 
								}
 | 
				
			||||||
	//		}
 | 
								if err := db.Table("web_images").Select("id", "content").Where("article_category_top_id = 22").Where("color_0_r IS NULL").Limit(10).Scan(&list).Error; err != nil {
 | 
				
			||||||
	//		fmt.Println("定时检查补全颜色字段查询成功", ids)
 | 
									fmt.Println("定时检查补全颜色字段查询失败", err)
 | 
				
			||||||
	//	}
 | 
									continue
 | 
				
			||||||
	//}
 | 
								}
 | 
				
			||||||
 | 
								fmt.Println("定时检查补全颜色字段查询成功", list)
 | 
				
			||||||
 | 
								for index, item := range list {
 | 
				
			||||||
 | 
									matches := regexp.MustCompile(`^https?://image\.gameuiux\.cn/(.*)`).FindStringSubmatch(item.Content)
 | 
				
			||||||
 | 
									if len(matches) < 2 {
 | 
				
			||||||
 | 
										fmt.Println("转换路径失败", index, item.ID, item.Content)
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									// 打开图像文件
 | 
				
			||||||
 | 
									file, err := os.Open("oss/" + matches[1])
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										fmt.Println("打开文件失败", index, item.ID, item.Content, err)
 | 
				
			||||||
 | 
										continue
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// 解码 JPEG 图像
 | 
				
			||||||
 | 
									img, _, err := image.Decode(file)
 | 
				
			||||||
 | 
									if err != nil {
 | 
				
			||||||
 | 
										fmt.Println("解码图像失败", index, item.ID, item.Content, err)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									centers, _ := KMeans(extractColors(img), 8)
 | 
				
			||||||
 | 
									fmt.Println(centers)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if config.Oss.Local {
 | 
				
			||||||
 | 
							fmt.Println("开启图像色调计算")
 | 
				
			||||||
 | 
							go checkColorNullRows()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// 用户的可选字段
 | 
						// 用户的可选字段
 | 
				
			||||||
	user := graphql.NewObject(graphql.ObjectConfig{
 | 
						user := graphql.NewObject(graphql.ObjectConfig{
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										131
									
								
								api/kMeans.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								api/kMeans.go
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					package api
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"image"
 | 
				
			||||||
 | 
						"image/color"
 | 
				
			||||||
 | 
						"math"
 | 
				
			||||||
 | 
						"math/rand"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 定义一个结构来表示 RGB 颜色
 | 
				
			||||||
 | 
					type RGB struct {
 | 
				
			||||||
 | 
						R, G, B int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 计算两个 RGB 颜色的欧氏距离
 | 
				
			||||||
 | 
					func distance(c1, c2 RGB) float64 {
 | 
				
			||||||
 | 
						return math.Sqrt(float64((c1.R-c2.R)*(c1.R-c2.R) + (c1.G-c2.G)*(c1.G-c2.G) + (c1.B-c2.B)*(c1.B-c2.B)))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 对图像颜色进行 KMeans 聚类
 | 
				
			||||||
 | 
					func KMeans(colors []RGB, k int) ([]RGB, []int) {
 | 
				
			||||||
 | 
						// 随机选择初始中心
 | 
				
			||||||
 | 
						var centers []RGB
 | 
				
			||||||
 | 
						for i := 0; i < k; i++ {
 | 
				
			||||||
 | 
							centers = append(centers, colors[rand.Intn(len(colors))])
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 聚类算法
 | 
				
			||||||
 | 
						labels := make([]int, len(colors))
 | 
				
			||||||
 | 
						for iter := 0; iter < 10; iter++ { // 设置最大迭代次数为 10
 | 
				
			||||||
 | 
							// 为每个颜色分配最近的聚类中心
 | 
				
			||||||
 | 
							for i, color := range colors {
 | 
				
			||||||
 | 
								minDist := distance(color, centers[0])
 | 
				
			||||||
 | 
								labels[i] = 0
 | 
				
			||||||
 | 
								for j := 1; j < k; j++ {
 | 
				
			||||||
 | 
									dist := distance(color, centers[j])
 | 
				
			||||||
 | 
									if dist < minDist {
 | 
				
			||||||
 | 
										minDist = dist
 | 
				
			||||||
 | 
										labels[i] = j
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 计算新的聚类中心
 | 
				
			||||||
 | 
							newCenters := make([]RGB, k)
 | 
				
			||||||
 | 
							counts := make([]int, k)
 | 
				
			||||||
 | 
							for i, label := range labels {
 | 
				
			||||||
 | 
								newCenters[label].R += colors[i].R
 | 
				
			||||||
 | 
								newCenters[label].G += colors[i].G
 | 
				
			||||||
 | 
								newCenters[label].B += colors[i].B
 | 
				
			||||||
 | 
								counts[label]++
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 计算平均值作为新的中心
 | 
				
			||||||
 | 
							for i := 0; i < k; i++ {
 | 
				
			||||||
 | 
								if counts[i] > 0 {
 | 
				
			||||||
 | 
									newCenters[i].R /= counts[i]
 | 
				
			||||||
 | 
									newCenters[i].G /= counts[i]
 | 
				
			||||||
 | 
									newCenters[i].B /= counts[i]
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							// 如果中心没有变化,停止迭代
 | 
				
			||||||
 | 
							changed := false
 | 
				
			||||||
 | 
							for i := 0; i < k; i++ {
 | 
				
			||||||
 | 
								if newCenters[i] != centers[i] {
 | 
				
			||||||
 | 
									changed = true
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !changed {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							centers = newCenters
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return centers, labels
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 加载图像并提取颜色
 | 
				
			||||||
 | 
					func extractColors(img image.Image) (colors []RGB) {
 | 
				
			||||||
 | 
						bounds := img.Bounds()
 | 
				
			||||||
 | 
						for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
 | 
				
			||||||
 | 
							for x := bounds.Min.X; x < bounds.Max.X; x++ {
 | 
				
			||||||
 | 
								r, g, b, _ := img.At(x, y).RGBA()
 | 
				
			||||||
 | 
								colors = append(colors, RGB{int(r >> 8), int(g >> 8), int(b >> 8)})
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return colors
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 将聚类后的颜色应用到图像上
 | 
				
			||||||
 | 
					func recolorImage(img image.Image, centers []RGB, labels []int) image.Image {
 | 
				
			||||||
 | 
						bounds := img.Bounds()
 | 
				
			||||||
 | 
						newImage := image.NewRGBA(bounds)
 | 
				
			||||||
 | 
						for y := bounds.Min.Y; y < bounds.Max.Y; y++ {
 | 
				
			||||||
 | 
							for x := bounds.Min.X; x < bounds.Max.X; x++ {
 | 
				
			||||||
 | 
								label := labels[(y-bounds.Min.Y)*bounds.Max.X+(x-bounds.Min.X)]
 | 
				
			||||||
 | 
								// 将 RGB 颜色转换为 RGBA 类型
 | 
				
			||||||
 | 
								newColor := color.RGBA{
 | 
				
			||||||
 | 
									R: uint8(centers[label].R),
 | 
				
			||||||
 | 
									G: uint8(centers[label].G),
 | 
				
			||||||
 | 
									B: uint8(centers[label].B),
 | 
				
			||||||
 | 
									A: 255, // 不透明
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								// 设置新图像像素颜色
 | 
				
			||||||
 | 
								newImage.Set(x, y, newColor)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return newImage
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ImageToColors(str string) (colors []RGB, err error) {
 | 
				
			||||||
 | 
						// 打开图像文件
 | 
				
			||||||
 | 
						file, err := os.Open(str)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 解码 JPEG 图像
 | 
				
			||||||
 | 
						img, _, err := image.Decode(file)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// 提取图像颜色
 | 
				
			||||||
 | 
						centers, _ := KMeans(extractColors(img), 8)
 | 
				
			||||||
 | 
						return centers, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -155,6 +155,11 @@ type ConfigMysql struct {
 | 
				
			|||||||
	Password string
 | 
						Password string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Oss struct {
 | 
				
			||||||
 | 
						Local bool
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Config struct {
 | 
					type Config struct {
 | 
				
			||||||
	Mysql ConfigMysql
 | 
						Mysql ConfigMysql
 | 
				
			||||||
 | 
						Oss
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -247,6 +247,9 @@ func main() {
 | 
				
			|||||||
			UserName: config.GetString("mysql.user"),
 | 
								UserName: config.GetString("mysql.user"),
 | 
				
			||||||
			Password: config.GetString("mysql.password"),
 | 
								Password: config.GetString("mysql.password"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							Oss: api.Oss{
 | 
				
			||||||
 | 
								Local: config.GetBool("oss.local"),
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		log.Fatalf("failed to create new schema, error: %v", err)
 | 
							log.Fatalf("failed to create new schema, error: %v", err)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user