Skip to content

Instantly share code, notes, and snippets.

@pwfff
Created July 22, 2018 00:34
Show Gist options
  • Save pwfff/ab5c4e26c2d92a4d1b36a22e5f1ce4ee to your computer and use it in GitHub Desktop.
Save pwfff/ab5c4e26c2d92a4d1b36a22e5f1ce4ee to your computer and use it in GitHub Desktop.
package main
import (
"bufio"
"github.com/nfnt/resize"
"image"
"image/color"
"image/draw"
"image/jpeg"
_ "image/png"
"log"
"math/rand"
"os"
"sort"
"sync"
"time"
)
type sortRow struct {
start int
end int
}
type YSorter []color.Color
func (r YSorter) Len() int { return len(r) }
func (r YSorter) Swap(i, j int) { r[i], r[j] = r[j], r[i] }
func (r YSorter) Less(i, j int) bool {
r1, g1, b1, _ := r[i].RGBA()
r2, g2, b2, _ := r[j].RGBA()
y1, _, _ := color.RGBToYCbCr(uint8(r1>>8), uint8(g1>>8), uint8(b1>>8))
y2, _, _ := color.RGBToYCbCr(uint8(r2>>8), uint8(g2>>8), uint8(b2>>8))
return y1 < y2
}
func getDrawableImage(src image.Image) draw.Image {
b := src.Bounds()
rgba := image.NewRGBA(b)
draw.Draw(rgba, b, src, b.Min, draw.Src)
return rgba
}
func getImage(path string) (draw.Image, error) {
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
img, _, err := image.Decode(file)
if err != nil {
return nil, err
}
return getDrawableImage(img), nil
}
func fitImage(src, dest draw.Image) draw.Image {
bounds := src.Bounds()
srcWidth, srcHeight := uint(bounds.Max.X), uint(bounds.Max.Y)
srcRatio := float64(srcWidth) / float64(srcHeight)
bounds = dest.Bounds()
destWidth, destHeight := uint(bounds.Max.X), uint(bounds.Max.Y)
var fit image.Image
if srcRatio > 1.0 {
fit = resize.Resize(destWidth, 0, src, resize.Lanczos3)
} else {
fit = resize.Resize(0, destHeight, src, resize.Lanczos3)
}
return getDrawableImage(fit)
}
func getPixels(img draw.Image) [][]color.Color {
bounds := img.Bounds()
width, height := bounds.Max.X, bounds.Max.Y
var pixels [][]color.Color
for y := 0; y < height; y++ {
var row []color.Color
for x := 0; x < width; x++ {
row = append(row, img.At(x, y))
}
pixels = append(pixels, row)
}
return pixels
}
func main() {
rand.Seed(time.Now().UTC().UnixNano())
baseImage, err := getImage("city.jpg")
if err != nil {
log.Fatal(err)
}
basePixels := getPixels(baseImage)
maskImage, err := getImage("mask.png")
if err != nil {
log.Fatal(err)
}
maskImage = fitImage(maskImage, baseImage)
maskPixels := getPixels(maskImage)
var maskRows [][]sortRow
for y := range maskPixels {
var rows []sortRow
var start int
var inRow bool
row := maskPixels[y]
for x := range row {
pixel := row[x]
_, _, _, a := pixel.RGBA()
if a == 0 {
if inRow {
rows = append(rows, sortRow{start, x})
inRow = false
}
} else {
if !inRow {
start = x
inRow = true
} else {
if rand.Float64() > .95 {
rows = append(rows, sortRow{start, x})
inRow = false
}
}
}
}
maskRows = append(maskRows, rows)
}
var wg sync.WaitGroup
for y := range maskRows {
wg.Add(1)
go func(y int) {
defer wg.Done()
for i := range maskRows[y] {
row := maskRows[y][i]
var colors []color.Color
for x := row.start; x < row.end; x++ {
colors = append(colors, baseImage.At(x, y))
}
sort.Sort(YSorter(colors))
j := 0
for x := row.start; x < row.end; x++ {
baseImage.Set(x, y, colors[j])
j++
}
}
log.Print(y)
}(y)
}
wg.Wait()
f, err := os.Create("out.jpg")
if err != nil {
log.Fatal(err)
}
defer f.Close()
w := bufio.NewWriter(f)
jpeg.Encode(w, baseImage, nil)
log.Print(basePixels[1][1].RGBA())
log.Print(maskRows[0])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment