gomicsv/imgdiff/imgdiff.go
2024-08-10 14:48:56 +02:00

105 lines
2.6 KiB
Go

/*
* Copyright (c) 2013-2021 Utkan Güngördü <utkan@freeconsole.org>
* Copyright (c) 2021-2024 Piotr Grabowski
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package imgdiff
import (
"image"
"image/color"
"math/bits"
"github.com/gotk3/gotk3/gdk"
)
const (
dhashImageWidth = 9
dhashImageHeight = 8
)
type Hash uint64 // Assuming (dhashImageWidth-1)*dhashImageHeight <= 64
func Distance(h1, h2 Hash) int {
return bits.OnesCount64(uint64(h1 ^ h2))
}
func init() {
if dhashImageHeight*(dhashImageWidth-1) > 64 {
panic("dhashImageHeight is too large")
}
}
func pixbufToGrayscaleImage(p *gdk.Pixbuf) *image.Gray {
nchan := p.GetNChannels()
data := p.GetPixels()
w, h := p.GetWidth(), p.GetHeight()
rowstride := p.GetRowstride()
im := image.NewGray(image.Rect(0, 0, w, h))
if nchan == 1 {
for ih := 0; ih < h; ih++ {
for iw := 0; iw < w; iw++ {
y := data[ih*rowstride+iw*nchan]
im.SetGray(iw, ih, color.Gray{Y: y})
}
}
} else if nchan == 3 || nchan == 4 {
for ih := 0; ih < h; ih++ {
for iw := 0; iw < w; iw++ {
r := data[ih*rowstride+iw*nchan]
g := data[ih*rowstride+iw*nchan+1]
b := data[ih*rowstride+iw*nchan+2]
y := uint8((19595*uint32(r) + 38470*uint32(g) + 7471*uint32(b) + 1<<15) >> 16)
im.SetGray(iw, ih, color.Gray{Y: y})
}
}
} else {
panic("unknown image depth")
}
return im
}
// http://www.hackerfactor.com/blog/?/archives/529-Kind-of-Like-That.html
func DHash(p *gdk.Pixbuf) Hash {
q, err := p.ScaleSimple(dhashImageWidth, dhashImageHeight, gdk.INTERP_TILES)
if err != nil {
panic(err.Error())
}
gray := pixbufToGrayscaleImage(q)
data := make([]byte, dhashImageWidth*dhashImageHeight)
for iy := 0; iy < dhashImageHeight; iy++ {
for ix := 0; ix < dhashImageWidth; ix++ {
data[iy*dhashImageWidth+ix] = gray.GrayAt(ix, iy).Y
}
}
var hash Hash
for iy := 0; iy < dhashImageHeight; iy++ {
for ix := 0; ix < dhashImageWidth-1; ix++ {
o := iy * dhashImageWidth
if data[o+ix+1] > data[o+ix] {
hash |= 1 << uint(iy*dhashImageHeight+ix)
}
}
}
return hash
}