2019-10-21 00:07:39 +00:00
|
|
|
// Copyright (c) 2019 David Vogel
|
|
|
|
//
|
|
|
|
// This software is released under the MIT License.
|
|
|
|
// https://opensource.org/licenses/MIT
|
|
|
|
|
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"image"
|
2019-10-23 01:28:37 +00:00
|
|
|
"image/color"
|
2019-10-21 00:07:39 +00:00
|
|
|
"math"
|
|
|
|
"os"
|
2019-10-23 01:28:37 +00:00
|
|
|
|
|
|
|
"golang.org/x/image/font"
|
|
|
|
"golang.org/x/image/font/basicfont"
|
|
|
|
"golang.org/x/image/math/fixed"
|
2019-10-21 00:07:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// Source: https://gist.github.com/sergiotapia/7882944
|
|
|
|
func getImageFileDimension(imagePath string) (int, int, error) {
|
|
|
|
file, err := os.Open(imagePath)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, fmt.Errorf("Can't open file %v: %w", imagePath, err)
|
|
|
|
}
|
|
|
|
defer file.Close()
|
|
|
|
|
|
|
|
image, _, err := image.DecodeConfig(file)
|
|
|
|
if err != nil {
|
|
|
|
return 0, 0, fmt.Errorf("Error decoding config of image file %v: %w", imagePath, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return image.Width, image.Height, nil
|
|
|
|
}
|
|
|
|
|
2019-10-23 01:28:37 +00:00
|
|
|
// getImageDifferenceValue returns the average quadratic difference of the (sub)pixels.
|
|
|
|
// 0 means the images are identical, +inf means that the images don't intersect.
|
|
|
|
func getImageDifferenceValue(a, b *image.RGBA, offsetA image.Point) float64 {
|
|
|
|
intersection := a.Bounds().Add(offsetA).Intersect(b.Bounds())
|
2019-10-21 00:07:39 +00:00
|
|
|
|
|
|
|
if intersection.Empty() {
|
|
|
|
return math.Inf(1)
|
|
|
|
}
|
|
|
|
|
2019-10-23 01:28:37 +00:00
|
|
|
aSub := a.SubImage(intersection.Sub(offsetA)).(*image.RGBA)
|
2019-10-21 00:07:39 +00:00
|
|
|
bSub := b.SubImage(intersection).(*image.RGBA)
|
|
|
|
|
2019-10-23 01:28:37 +00:00
|
|
|
intersectionWidth := intersection.Dx() * 4
|
|
|
|
intersectionHeight := intersection.Dy()
|
|
|
|
|
|
|
|
var value int64
|
2019-10-21 00:07:39 +00:00
|
|
|
|
2019-10-23 01:28:37 +00:00
|
|
|
for iy := 0; iy < intersectionHeight; iy++ {
|
|
|
|
aSlice := aSub.Pix[iy*aSub.Stride : iy*aSub.Stride+intersectionWidth]
|
|
|
|
bSlice := bSub.Pix[iy*bSub.Stride : iy*bSub.Stride+intersectionWidth]
|
|
|
|
for ix := 0; ix < intersectionWidth; ix += 3 {
|
|
|
|
diff := int64(aSlice[ix]) - int64(bSlice[ix])
|
|
|
|
value += diff * diff
|
2019-10-21 00:07:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-23 01:28:37 +00:00
|
|
|
return float64(value) / float64(intersectionWidth*intersectionHeight)
|
|
|
|
}
|
|
|
|
|
2019-10-23 22:28:22 +00:00
|
|
|
func gridifyRectangle(rect image.Rectangle, gridSize int) (result []image.Rectangle) {
|
|
|
|
for y := divideFloor(rect.Min.Y, gridSize); y < divideCeil(rect.Max.Y, gridSize); y++ {
|
|
|
|
for x := divideFloor(rect.Min.X, gridSize); x < divideCeil(rect.Max.X, gridSize); x++ {
|
|
|
|
tempRect := image.Rect(x*gridSize, y*gridSize, (x+1)*gridSize, (y+1)*gridSize)
|
|
|
|
if tempRect.Overlaps(rect) {
|
|
|
|
result = append(result, tempRect)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2019-10-23 01:28:37 +00:00
|
|
|
func drawLabel(img *image.RGBA, x, y int, label string) {
|
|
|
|
col := color.RGBA{200, 100, 0, 255}
|
2019-10-23 22:28:22 +00:00
|
|
|
point := fixed.Point26_6{X: fixed.Int26_6(x * 64), Y: fixed.Int26_6(y * 64)}
|
2019-10-23 01:28:37 +00:00
|
|
|
|
|
|
|
d := &font.Drawer{
|
|
|
|
Dst: img,
|
|
|
|
Src: image.NewUniform(col),
|
|
|
|
Face: basicfont.Face7x13,
|
|
|
|
Dot: point,
|
|
|
|
}
|
|
|
|
d.DrawString(label)
|
|
|
|
}
|
|
|
|
|
|
|
|
func intAbs(x int) int {
|
|
|
|
if x < 0 {
|
|
|
|
return -x
|
|
|
|
}
|
|
|
|
return x
|
|
|
|
}
|
|
|
|
|
|
|
|
func pointAbs(p image.Point) image.Point {
|
|
|
|
if p.X < 0 {
|
|
|
|
p.X = -p.X
|
|
|
|
}
|
|
|
|
if p.Y < 0 {
|
|
|
|
p.Y = -p.Y
|
|
|
|
}
|
|
|
|
return p
|
2019-10-21 00:07:39 +00:00
|
|
|
}
|
2019-10-23 22:28:22 +00:00
|
|
|
|
|
|
|
// Integer division that rounds to the next integer towards negative infinity
|
|
|
|
func divideFloor(a, b int) int {
|
|
|
|
temp := a / b
|
|
|
|
|
|
|
|
if ((a ^ b) < 0) && (a%b != 0) {
|
|
|
|
return temp - 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return temp
|
|
|
|
}
|
|
|
|
|
|
|
|
// Integer division that rounds to the next integer towards positive infinity
|
|
|
|
func divideCeil(a, b int) int {
|
|
|
|
temp := a / b
|
|
|
|
|
|
|
|
if ((a ^ b) >= 0) && (a%b != 0) {
|
|
|
|
return temp + 1
|
|
|
|
}
|
|
|
|
|
|
|
|
return temp
|
|
|
|
}
|