Нашел на /r/proceduralgeneration пост в котором некто написал фильтр для изображений, который превращает исходное изображение в условно абстрактное. Код был написан на Lua, я же решил переписать предложенный метод на Go. Во-первых я никогда не писал что-то связанное с обработкой изображений на гошке, а во-вторых почему бы и нет.
package main
import (
"image"
"image/color"
"image/jpeg"
"os"
"sort"
)
func main() {
file, err := os.Open("in.jpg")
if err != nil {
panic(err)
}
defer file.Close()
image.RegisterFormat("jpeg", "jpeg", jpeg.Decode, jpeg.DecodeConfig)
img, _, err := image.Decode(file)
if err != nil {
panic(err)
}
rec := img.Bounds()
cimg := image.NewRGBA(rec)
m := make([]color.Color, rec.Dy())
for x := 0; x < rec.Dx(); x++ {
for y := 0; y < rec.Dy(); y++ {
m[y] = img.At(x, y)
}
sort.SliceStable(m, func(i, j int) bool {
return luma(m[i]) < luma(m[j])
})
for y := range m {
cimg.Set(x, y, m[y])
}
}
out, err := os.Create("out.jpg")
if err != nil {
panic(err)
}
err = jpeg.Encode(out, cimg, &jpeg.Options{Quality: 80})
if err != nil {
panic(err)
}
}
func luma(c color.Color) float32 {
r, g, b, _ := c.RGBA()
return 0.2126*float32(r>>8) +
0.7152*float32(g>>8) +
0.0722*float32(b>>8)
}
TL;DR Что тут происходит? Берем пиксельную сетку изображения, делим на столбцы и в каждом из столбцов сортируем пиксели по относительной яркости.
Для вычисления относительной яркости нужно взять каждый пиксель, разложить на RGB цвета и умножить их на коэффициенты. Почему именно такие коэффициенты лучше почитать в статье на хабре. Результат выходит приличный, для изображений содержащих большой однородный задний фон.
Второй интересный момент момент это то, как в Go реализованы возвращаемые значения цветов. Все цвета хранятся в uint32 в диапазоне от 0
до 0xFFFF
, что позволяет хранить эффективные значения для 16-битного диапазона. Как обычно, самый исчерпывающий ответ о том как это работает можно найти на SO, где же еще. Но так как формула относительной яркости работает с 8-битными значениями, то конвертируем 16-битное представление обратно в 8 бит используя битовый сдвиг вправо.