Skip to content

Commit 14a32d4

Browse files
authored
image-resizer: compute the main color automatically (#324)
* image-resizer: compute the main color automatically Use ImageMagick to compute the main color automatically. Remove that data from metadata.json. Bump Go to 1.24. * Install imagemagick-dev
1 parent abdfce0 commit 14a32d4

File tree

7 files changed

+91
-85
lines changed

7 files changed

+91
-85
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ jobs:
1010

1111
steps:
1212
- name: Install dependencies
13-
run: apk add gcc git make musl-dev pkgconfig vips-dev
13+
run: apk add gcc git imagemagick-dev make musl-dev pkgconfig vips-dev
1414

1515
- name: Check out the repo
1616
uses: actions/checkout@v4
@@ -34,7 +34,7 @@ jobs:
3434

3535
steps:
3636
- name: Install dependencies
37-
run: apk add gcc musl-dev pkgconfig tar vips-dev
37+
run: apk add gcc imagemagick-dev musl-dev pkgconfig tar vips-dev
3838

3939
- name: Check out the repo
4040
uses: actions/checkout@v4

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ COPY go.mod go.mod
1111
COPY go.sum go.sum
1212
COPY pkg/ pkg/
1313

14-
RUN ["apk", "add", "gcc", "git", "make", "musl-dev", "pkgconfig", "vips-dev"]
14+
RUN ["apk", "add", "gcc", "git", "imagemagick-dev", "make", "musl-dev", "pkgconfig", "vips-dev"]
1515
RUN ["make", "server", "img-out"]
1616

1717
FROM python:3 as python-builder

cmd/image-resizer/main.go

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414

1515
"github.com/davidbyttow/govips/v2/vips"
1616
"github.com/qbarrand/quba.fr/internal/metadata"
17+
"gopkg.in/gographics/imagick.v3/imagick"
1718
)
1819

1920
type breakpoints struct {
@@ -142,6 +143,34 @@ func (r *processor) resize(ctx context.Context, p *params) error {
142143
return nil
143144
}
144145

146+
func mainColor(fullPath string) (string, error) {
147+
mw := imagick.NewMagickWand()
148+
defer mw.Destroy()
149+
150+
if err := mw.ReadImage(fullPath); err != nil {
151+
return "", fmt.Errorf("could not read image %s: %v", fullPath, err)
152+
}
153+
154+
if err := mw.SetDepth(8); err != nil {
155+
return "", fmt.Errorf("could not set image depth: %v", err)
156+
}
157+
158+
if err := mw.QuantizeImage(1, imagick.COLORSPACE_UNDEFINED, 0, imagick.DITHER_METHOD_FLOYD_STEINBERG, false); err != nil {
159+
return "", fmt.Errorf("error quantizing image: %v", err)
160+
}
161+
162+
colors, pws := mw.GetImageHistogram()
163+
if colors != 1 {
164+
return "", fmt.Errorf("expected 1 color, got %d", colors)
165+
}
166+
167+
red := uint(pws[0].GetRed() * 256)
168+
green := uint(pws[0].GetGreen() * 256)
169+
blue := uint(pws[0].GetBlue() * 256)
170+
171+
return fmt.Sprintf("#%X%X%X", red, green, blue), nil
172+
}
173+
145174
func main() {
146175
var (
147176
breakpointsFile string
@@ -167,6 +196,9 @@ func main() {
167196
vips.Startup(&vips.Config{ConcurrencyLevel: concurrency})
168197
defer vips.Shutdown()
169198

199+
imagick.Initialize()
200+
defer imagick.Terminate()
201+
170202
ctx, cancel := context.WithCancel(context.Background())
171203
defer cancel()
172204

@@ -201,20 +233,24 @@ func main() {
201233
log.Fatalf("Could not initialize the database: %v", err)
202234
}
203235

204-
// Add image metadata
205-
for k, v := range meta {
206-
if err = mdb.AddImage(ctx, k, v.Date, v.Location, v.MainColor); err != nil {
207-
log.Fatalf("Could not add %s to the metadata DB: %v", k, err)
208-
}
209-
}
210-
211236
rs := &processor{mdb: mdb}
212237

213-
for baseName := range meta {
238+
for baseName, m := range meta {
214239
log.Printf("Processing %s", baseName)
215240

216241
fullPath := filepath.Join(imgInDir, baseName)
217242

243+
log.Printf("Getting main color")
244+
245+
mainColor, err := mainColor(fullPath)
246+
if err != nil {
247+
log.Fatalf("Could not get the main color: %v", err)
248+
}
249+
250+
if err = mdb.AddImage(ctx, baseName, m.Date, m.Location, mainColor); err != nil {
251+
log.Fatalf("Could not add %s to the metadata DB: %v", baseName, err)
252+
}
253+
218254
origImg, err := vips.LoadImageFromFile(fullPath, vips.NewImportParams())
219255
if err != nil {
220256
log.Fatalf("Could not load image %s", fullPath)

cmd/image-resizer/metadata.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,10 @@ import (
88
)
99

1010
type ImageMetadata struct {
11-
Date string `json:"date"`
12-
Formats []string `json:"formats"`
13-
Heights []int `json:"heights"`
14-
Location string `json:"location"`
15-
MainColor string `json:"main_color"`
11+
Date string `json:"date"`
12+
Formats []string `json:"formats"`
13+
Heights []int `json:"heights"`
14+
Location string `json:"location"`
1615
}
1716

1817
type Metadata map[string]*ImageMetadata

go.mod

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
module github.com/qbarrand/quba.fr
22

3-
go 1.20
3+
go 1.24
4+
5+
toolchain go1.24.1
46

57
require (
68
github.com/Masterminds/squirrel v1.5.4
@@ -29,5 +31,6 @@ require (
2931
golang.org/x/sys v0.28.0 // indirect
3032
golang.org/x/text v0.21.0 // indirect
3133
google.golang.org/protobuf v1.34.2 // indirect
34+
gopkg.in/gographics/imagick.v3 v3.7.2 // indirect
3235
gopkg.in/yaml.v3 v3.0.1 // indirect
3336
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,8 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn
121121
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
122122
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
123123
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
124+
gopkg.in/gographics/imagick.v3 v3.7.2 h1:PmsYCf60YS/7f1omBTDaoS6yp4817Wv61S0JpWH4cMc=
125+
gopkg.in/gographics/imagick.v3 v3.7.2/go.mod h1:7I4S9VWdwr88yzYi7g+ZL4H8oZuH9cmSQI7GsZCcYFM=
124126
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
125127
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
126128
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

img-src/metadata.json

Lines changed: 34 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,172 +1,138 @@
11
{
22
"aurlandsfjord_1.jpg": {
33
"date": "September 2021",
4-
"location": "Aurlandsfjord, Norway",
5-
"main_color": "#6B7477"
4+
"location": "Aurlandsfjord, Norway"
65
},
76
"chicago_1.jpg": {
87
"date": "November 2023",
9-
"location": "Chicago, USA",
10-
"main_color": "#77838A"
8+
"location": "Chicago, USA"
119
},
1210
"dents_du_midi_1.jpg": {
1311
"date": "January 2020",
14-
"location": "Dents du Midi, Switzerland",
15-
"main_color": "#4279AC"
12+
"location": "Dents du Midi, Switzerland"
1613
},
1714
"dubai_1.jpg": {
1815
"date": "June 2017",
19-
"location": "Dubai, UAE",
20-
"main_color": "#4F4C42"
16+
"location": "Dubai, UAE"
2117
},
2218
"fuji_1.jpg": {
2319
"date": "October 2017",
24-
"location": "Mount Fuji, Japan",
25-
"main_color": "#68809E"
20+
"location": "Mount Fuji, Japan"
2621
},
2722
"geneva_1.jpg": {
2823
"date": "June 2016",
29-
"location": "Geneva, Switzerland",
30-
"main_color": "#717D85"
24+
"location": "Geneva, Switzerland"
3125
},
3226
"grindelwald_1.jpg": {
3327
"date": "August 2021",
34-
"location": "Grindelwald, Switzerland",
35-
"main_color": "#568AB2"
28+
"location": "Grindelwald, Switzerland"
3629
},
3730
"kawaguchiko_1.jpg": {
3831
"date": "December 2023",
39-
"location": "Kawaguchi-ko, Japan",
40-
"main_color": "#7299B2"
32+
"location": "Kawaguchi-ko, Japan"
4133
},
4234
"kyoto_2.jpg": {
4335
"date": "April 2023",
44-
"location": "Kyoto, Japan",
45-
"main_color": "#BC5B1E"
36+
"location": "Kyoto, Japan"
4637
},
4738
"lhc_1.jpg": {
4839
"date": "August 2019",
49-
"location": "LHC, France / Switzerland",
50-
"main_color": "#827366"
40+
"location": "LHC, France / Switzerland"
5141
},
5242
"malibu_1.jpg": {
5343
"date": "March 2019",
54-
"location": "Malibu, USA",
55-
"main_color": "#744930"
44+
"location": "Malibu, USA"
5645
},
5746
"marseille_1.jpg": {
5847
"date": "March 2023",
59-
"location": "Marseille, France",
60-
"main_color": "#497C88"
48+
"location": "Marseille, France"
6149
},
6250
"montreux_1.jpg": {
6351
"date": "October 2016",
64-
"location": "Montreux, Switzerland",
65-
"main_color": "#768692"
52+
"location": "Montreux, Switzerland"
6653
},
6754
"monument_valley_1.jpg": {
6855
"date": "May 2022",
69-
"location": "Monument Valley, USA",
70-
"main_color": "#514745"
56+
"location": "Monument Valley, USA"
7157
},
7258
"nantes_1.jpg": {
7359
"date": "December 2019",
74-
"location": "Nantes, France",
75-
"main_color": "#807B66"
60+
"location": "Nantes, France"
7661
},
7762
"new_delhi_1.jpg": {
7863
"date": "June 2017",
79-
"location": "New Delhi, India",
80-
"main_color": "#807B66"
64+
"location": "New Delhi, India"
8165
},
8266
"newyork_3.jpg": {
8367
"date": "October 2023",
84-
"location": "New York, USA",
85-
"main_color": "#464240"
68+
"location": "New York, USA"
8669
},
8770
"newyork_4.jpg": {
8871
"date": "October 2023",
89-
"location": "New York, USA",
90-
"main_color": "#587C7E"
72+
"location": "New York, USA"
9173
},
9274
"newyork_5.jpg": {
9375
"date": "October 2023",
94-
"location": "New York, USA",
95-
"main_color": "#3A342D"
76+
"location": "New York, USA"
9677
},
9778
"nuggets_point_1.jpg": {
9879
"date": "January 2019",
99-
"location": "Nuggets Point, New Zealand",
100-
"main_color": "#526F7F"
80+
"location": "Nuggets Point, New Zealand"
10181
},
10282
"reine_1.jpg": {
10383
"date": "September 2021",
104-
"location": "Reine, Norway",
105-
"main_color": "#7C8D9A"
84+
"location": "Reine, Norway"
10685
},
10786
"san_francisco_1.jpg": {
10887
"date": "June 2022",
109-
"location": "San Francisco, USA",
110-
"main_color": "#789DA4"
88+
"location": "San Francisco, USA"
11189
},
11290
"shenzhen_1.jpg": {
11391
"date": "August 2014",
114-
"location": "Shenzhen, China",
115-
"main_color": "#5C0C1A"
92+
"location": "Shenzhen, China"
11693
},
11794
"shikotsuko_1.jpg": {
11895
"date": "April 2023",
119-
"location": "Shikotsu-ko, Japan",
120-
"main_color": "#7D8996"
96+
"location": "Shikotsu-ko, Japan"
12197
},
12298
"singapore_1.jpg": {
12399
"date": "January 2019",
124-
"location": "Singapore",
125-
"main_color": "#483D39"
100+
"location": "Singapore"
126101
},
127102
"thun_1.jpg": {
128103
"date": "May 2016",
129-
"location": "Thun, Switzerland",
130-
"main_color": "#587FA4"
104+
"location": "Thun, Switzerland"
131105
},
132106
"tokyo_1.jpg": {
133107
"date": "April 2023",
134-
"location": "Tokyo, Japan",
135-
"main_color": "#A4A7A7"
108+
"location": "Tokyo, Japan"
136109
},
137110
"tokyo_2.jpg": {
138111
"date": "April 2023",
139-
"location": "Tokyo, Japan",
140-
"main_color": "#898D83"
112+
"location": "Tokyo, Japan"
141113
},
142114
"tongariro_1.jpg": {
143115
"date": "January 2019",
144-
"location": "Tongariro National Park, New Zealand",
145-
"main_color": "#595553"
116+
"location": "Tongariro National Park, New Zealand"
146117
},
147118
"touffailles_1.jpg": {
148119
"date": "August 2023",
149-
"location": "Touffailles, France",
150-
"main_color": "#776F5B"
120+
"location": "Touffailles, France"
151121
},
152122
"whaikiti_beach_1.jpg": {
153123
"date": "January 2019",
154-
"location": "Whaikiti Beach, New Zealand",
155-
"main_color": "#6C8296"
124+
"location": "Whaikiti Beach, New Zealand"
156125
},
157126
"yosemite_1.jpg": {
158127
"date": "May 2022",
159-
"location": "Yosemite National Park, USA",
160-
"main_color": "#445C68"
128+
"location": "Yosemite National Park, USA"
161129
},
162130
"zermatt_1.jpg": {
163131
"date": "February 2021",
164-
"location": "Zermatt, Switzerland",
165-
"main_color": "#3B6796"
132+
"location": "Zermatt, Switzerland"
166133
},
167134
"zurich_1.jpg": {
168135
"date": "August 2023",
169-
"location": "Zürich, Switzerland",
170-
"main_color": "#4F5E5D"
136+
"location": "Zürich, Switzerland"
171137
}
172138
}

0 commit comments

Comments
 (0)