Make compliant with python imagehash
This commit is contained in:
parent
d8115886f3
commit
1ca06a1968
@ -2,9 +2,10 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/corona10/goimagehash"
|
|
||||||
"image/jpeg"
|
"image/jpeg"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
"github.com/corona10/goimagehash"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@ -29,8 +30,8 @@ func main() {
|
|||||||
hash2, _ = goimagehash.PerceptionHash(img2)
|
hash2, _ = goimagehash.PerceptionHash(img2)
|
||||||
distance, _ = hash1.Distance(hash2)
|
distance, _ = hash1.Distance(hash2)
|
||||||
fmt.Printf("Distance between images: %v\n", distance)
|
fmt.Printf("Distance between images: %v\n", distance)
|
||||||
fmt.Println(hash1.ToString())
|
fmt.Println(hash1.String())
|
||||||
fmt.Println(hash2.ToString())
|
fmt.Println(hash2.String())
|
||||||
fmt.Println(hash1.Bits())
|
fmt.Println(hash1.Bits())
|
||||||
fmt.Println(hash2.Bits())
|
fmt.Println(hash2.Bits())
|
||||||
}
|
}
|
||||||
|
@ -41,9 +41,9 @@ func main() {
|
|||||||
fmt.Printf("Distance between hash1 and hash3: %v\n", distance)
|
fmt.Printf("Distance between hash1 and hash3: %v\n", distance)
|
||||||
distance, _ = hash2.Distance(hash3)
|
distance, _ = hash2.Distance(hash3)
|
||||||
fmt.Printf("Distance between hash2 and hash3: %v\n", distance)
|
fmt.Printf("Distance between hash2 and hash3: %v\n", distance)
|
||||||
fmt.Println(hash1.ToString())
|
fmt.Println(hash1.String())
|
||||||
fmt.Println(hash2.ToString())
|
fmt.Println(hash2.String())
|
||||||
fmt.Println(hash3.ToString())
|
fmt.Println(hash3.String())
|
||||||
fmt.Println(hash1.Bits())
|
fmt.Println(hash1.Bits())
|
||||||
fmt.Println(hash2.Bits())
|
fmt.Println(hash2.Bits())
|
||||||
fmt.Println(hash3.Bits())
|
fmt.Println(hash3.Bits())
|
||||||
|
@ -4,25 +4,32 @@
|
|||||||
|
|
||||||
package etcs
|
package etcs
|
||||||
|
|
||||||
|
import "golang.org/x/exp/constraints"
|
||||||
|
|
||||||
|
type Number interface {
|
||||||
|
constraints.Integer | constraints.Float
|
||||||
|
}
|
||||||
|
|
||||||
// MeanOfPixels function returns a mean of pixels.
|
// MeanOfPixels function returns a mean of pixels.
|
||||||
func MeanOfPixels(pixels []float64) float64 {
|
func MeanOfPixels[N Number](pixels []N) N {
|
||||||
m := 0.0
|
var m float64 = 0
|
||||||
lens := len(pixels)
|
lens := float64(len(pixels))
|
||||||
if lens == 0 {
|
if lens == 0 {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range pixels {
|
for _, p := range pixels {
|
||||||
m += p
|
m += float64(p)
|
||||||
}
|
}
|
||||||
|
// print(m," ", lens, "\n")
|
||||||
|
|
||||||
return m / float64(lens)
|
return N(m / lens)
|
||||||
}
|
}
|
||||||
|
|
||||||
// MedianOfPixels function returns a median value of pixels.
|
// MedianOfPixels function returns a median value of pixels.
|
||||||
// It uses quick selection algorithm.
|
// It uses quick selection algorithm.
|
||||||
func MedianOfPixels(pixels []float64) float64 {
|
func MedianOfPixels[N Number](pixels []N) N {
|
||||||
tmp := make([]float64, len(pixels))
|
tmp := make([]N, len(pixels))
|
||||||
copy(tmp, pixels)
|
copy(tmp, pixels)
|
||||||
l := len(tmp)
|
l := len(tmp)
|
||||||
pos := l / 2
|
pos := l / 2
|
||||||
@ -32,15 +39,15 @@ func MedianOfPixels(pixels []float64) float64 {
|
|||||||
|
|
||||||
// MedianOfPixelsFast64 function returns a median value of pixels.
|
// MedianOfPixelsFast64 function returns a median value of pixels.
|
||||||
// It uses quick selection algorithm.
|
// It uses quick selection algorithm.
|
||||||
func MedianOfPixelsFast64(pixels []float64) float64 {
|
func MedianOfPixelsFast64[N Number](pixels []N) N {
|
||||||
tmp := [64]float64{}
|
tmp := [64]N{}
|
||||||
copy(tmp[:], pixels)
|
copy(tmp[:], pixels)
|
||||||
l := len(tmp)
|
l := len(tmp)
|
||||||
pos := l / 2
|
pos := l / 2
|
||||||
return quickSelectMedian(tmp[:], 0, l-1, pos)
|
return quickSelectMedian(tmp[:], 0, l-1, pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
func quickSelectMedian(sequence []float64, low int, hi int, k int) float64 {
|
func quickSelectMedian[N Number](sequence []N, low, hi, k int) N {
|
||||||
if low == hi {
|
if low == hi {
|
||||||
return sequence[k]
|
return sequence[k]
|
||||||
}
|
}
|
||||||
|
7
go.mod
7
go.mod
@ -1,5 +1,10 @@
|
|||||||
module github.com/corona10/goimagehash
|
module github.com/corona10/goimagehash
|
||||||
|
|
||||||
go 1.11
|
go 1.20
|
||||||
|
|
||||||
require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/anthonynsimon/bild v0.13.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect
|
||||||
|
)
|
||||||
|
35
go.sum
35
go.sum
@ -1,2 +1,37 @@
|
|||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/anthonynsimon/bild v0.13.0 h1:mN3tMaNds1wBWi1BrJq0ipDBhpkooYfu7ZFSMhXt1C8=
|
||||||
|
github.com/anthonynsimon/bild v0.13.0/go.mod h1:tpzzp0aYkAsMi1zmfhimaDyX1xjn2OUc1AJZK/TF0AE=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||||
|
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||||
|
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||||
|
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
|
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||||
|
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||||
|
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||||
|
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||||
|
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
|
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||||
|
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||||
|
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||||
|
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||||
|
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||||
|
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
|
||||||
|
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
|
||||||
|
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
|
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
135
hashcompute.go
135
hashcompute.go
@ -7,13 +7,26 @@ package goimagehash
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"image"
|
"image"
|
||||||
"sync"
|
"image/draw"
|
||||||
|
|
||||||
|
"github.com/anthonynsimon/bild/transform"
|
||||||
"github.com/corona10/goimagehash/etcs"
|
"github.com/corona10/goimagehash/etcs"
|
||||||
"github.com/corona10/goimagehash/transforms"
|
"github.com/corona10/goimagehash/transforms"
|
||||||
"github.com/nfnt/resize"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func ToGray(img image.Image) *image.Gray {
|
||||||
|
gray := image.NewGray(image.Rect(0, 0, img.Bounds().Dx(), img.Bounds().Dy()))
|
||||||
|
gray.Pix = transforms.Rgb2Gray(img)
|
||||||
|
return gray
|
||||||
|
}
|
||||||
|
|
||||||
|
func resize(img image.Image, w, h int) *image.Gray {
|
||||||
|
resized := transform.Resize(img, w, h, transform.Lanczos)
|
||||||
|
r_gray := image.NewGray(image.Rect(0, 0, resized.Bounds().Dx(), resized.Bounds().Dy()))
|
||||||
|
draw.Draw(r_gray, resized.Bounds(), resized, resized.Bounds().Min, draw.Src)
|
||||||
|
return r_gray
|
||||||
|
}
|
||||||
|
|
||||||
// AverageHash function returns a hash computation of average hash.
|
// AverageHash function returns a hash computation of average hash.
|
||||||
// Implementation follows
|
// Implementation follows
|
||||||
// http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
|
// http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html
|
||||||
@ -22,16 +35,14 @@ func AverageHash(img image.Image) (*ImageHash, error) {
|
|||||||
return nil, errors.New("image object can not be nil")
|
return nil, errors.New("image object can not be nil")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create 64bits hash.
|
|
||||||
ahash := NewImageHash(0, AHash)
|
ahash := NewImageHash(0, AHash)
|
||||||
resized := resize.Resize(8, 8, img, resize.Bilinear)
|
|
||||||
pixels := transforms.Rgb2Gray(resized)
|
|
||||||
flattens := transforms.FlattenPixels(pixels, 8, 8)
|
|
||||||
avg := etcs.MeanOfPixels(flattens)
|
|
||||||
|
|
||||||
for idx, p := range flattens {
|
gray := resize(ToGray(img), 8, 8)
|
||||||
|
avg := etcs.MeanOfPixels(gray.Pix)
|
||||||
|
|
||||||
|
for idx, p := range gray.Pix {
|
||||||
if p > avg {
|
if p > avg {
|
||||||
ahash.leftShiftSet(len(flattens) - idx - 1)
|
ahash.leftShiftSet(len(gray.Pix) - idx - 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,12 +58,13 @@ func DifferenceHash(img image.Image) (*ImageHash, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dhash := NewImageHash(0, DHash)
|
dhash := NewImageHash(0, DHash)
|
||||||
resized := resize.Resize(9, 8, img, resize.Bilinear)
|
|
||||||
pixels := transforms.Rgb2Gray(resized)
|
gray := resize(ToGray(img), 9, 8)
|
||||||
|
|
||||||
idx := 0
|
idx := 0
|
||||||
for i := 0; i < len(pixels); i++ {
|
for y := 0; y < gray.Bounds().Dy(); y++ {
|
||||||
for j := 0; j < len(pixels[i])-1; j++ {
|
for x := 0; x < gray.Bounds().Dx()-1; x++ {
|
||||||
if pixels[i][j] < pixels[i][j+1] {
|
if gray.Pix[gray.PixOffset(x, y)] < gray.Pix[gray.PixOffset(x+1, y)] {
|
||||||
dhash.leftShiftSet(64 - idx - 1)
|
dhash.leftShiftSet(64 - idx - 1)
|
||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
@ -71,14 +83,12 @@ func PerceptionHash(img image.Image) (*ImageHash, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
phash := NewImageHash(0, PHash)
|
phash := NewImageHash(0, PHash)
|
||||||
resized := resize.Resize(64, 64, img, resize.Bilinear)
|
gray := resize(ToGray(img), 32, 32)
|
||||||
|
gray32 := make([]float64, len(gray.Pix))
|
||||||
pixels := pixelPool64.Get().(*[]float64)
|
for i, p := range gray.Pix {
|
||||||
|
gray32[i] = float64(p)
|
||||||
transforms.Rgb2GrayFast(resized, pixels)
|
}
|
||||||
flattens := transforms.DCT2DFast64(pixels)
|
flattens := transforms.DCT2DFast32(&gray32)
|
||||||
|
|
||||||
pixelPool64.Put(pixels)
|
|
||||||
|
|
||||||
median := etcs.MedianOfPixelsFast64(flattens[:])
|
median := etcs.MedianOfPixelsFast64(flattens[:])
|
||||||
|
|
||||||
@ -91,19 +101,23 @@ func PerceptionHash(img image.Image) (*ImageHash, error) {
|
|||||||
return phash, nil
|
return phash, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var pixelPool64 = sync.Pool{
|
// FlattenPixels function flattens 2d array into 1d array.
|
||||||
New: func() interface{} {
|
func FlattenPixels(pixels [][]float64, x int, y int) []float64 {
|
||||||
p := make([]float64, 4096)
|
flattens := make([]float64, x*y)
|
||||||
return &p
|
for i := 0; i < y; i++ {
|
||||||
},
|
for j := 0; j < x; j++ {
|
||||||
|
flattens[y*i+j] = pixels[i][j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return flattens
|
||||||
}
|
}
|
||||||
|
|
||||||
// ExtPerceptionHash function returns phash of which the size can be set larger than uint64
|
// ExtPerceptionHash function returns phash of which the size can be set larger than uint64
|
||||||
// Some variable name refer to https://github.com/JohannesBuchner/imagehash/blob/master/imagehash/__init__.py
|
// Some variable name refer to https://github.com/JohannesBuchner/imagehash/blob/master/imagehash/__init__.py
|
||||||
// Support 64bits phash (width=8, height=8) and 256bits phash (width=16, height=16)
|
// Support 64bits phash (width=8, height=8) and 256bits phash (width=16, height=16)
|
||||||
// Important: width * height should be the power of 2
|
// Important: width * height should be a power of 2
|
||||||
func ExtPerceptionHash(img image.Image, width, height int) (*ExtImageHash, error) {
|
func ExtPerceptionHash(img image.Image, hash_size, freq int) (*ExtImageHash, error) {
|
||||||
imgSize := width * height
|
imgSize := hash_size * freq
|
||||||
if img == nil {
|
if img == nil {
|
||||||
return nil, errors.New("image object can not be nil")
|
return nil, errors.New("image object can not be nil")
|
||||||
}
|
}
|
||||||
@ -111,25 +125,33 @@ func ExtPerceptionHash(img image.Image, width, height int) (*ExtImageHash, error
|
|||||||
return nil, errors.New("width * height should be power of 2")
|
return nil, errors.New("width * height should be power of 2")
|
||||||
}
|
}
|
||||||
var phash []uint64
|
var phash []uint64
|
||||||
resized := resize.Resize(uint(imgSize), uint(imgSize), img, resize.Bilinear)
|
// gray := resize(ToGray(img), imgSize, imgSize)
|
||||||
pixels := transforms.Rgb2Gray(resized)
|
// gray32 := make([][]float64, imgSize)
|
||||||
dct := transforms.DCT2D(pixels, imgSize, imgSize)
|
// for i := range gray32 {
|
||||||
flattens := transforms.FlattenPixels(dct, width, height)
|
// gray32[i] = make([]float64, imgSize)
|
||||||
median := etcs.MedianOfPixels(flattens)
|
// for x := range gray32[i] {
|
||||||
|
// gray32[i][x] = float64(gray.Pix[(gray.PixOffset(x, i))])
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
lenOfUnit := 64
|
// pixels := transforms.DCT2D(gray32, hash_size, hash_size)
|
||||||
if imgSize%lenOfUnit == 0 {
|
// flattens := FlattenPixels(pixels, hash_size, hash_size)
|
||||||
phash = make([]uint64, imgSize/lenOfUnit)
|
// median := etcs.MedianOfPixels(flattens)
|
||||||
} else {
|
// print(hash_size, "x", freq, " pix:", len(gray.Pix), " h:", imgSize, " flatten:", len(flattens), "\n")
|
||||||
phash = make([]uint64, imgSize/lenOfUnit+1)
|
|
||||||
}
|
// lenOfUnit := 64
|
||||||
for idx, p := range flattens {
|
// if (hash_size*hash_size)%lenOfUnit == 0 {
|
||||||
indexOfArray := idx / lenOfUnit
|
// phash = make([]uint64, (hash_size*hash_size)/lenOfUnit)
|
||||||
indexOfBit := lenOfUnit - idx%lenOfUnit - 1
|
// } else {
|
||||||
if p > median {
|
// phash = make([]uint64, (hash_size*hash_size)/lenOfUnit+1)
|
||||||
phash[indexOfArray] |= 1 << uint(indexOfBit)
|
// }
|
||||||
}
|
// for idx, p := range flattens {
|
||||||
}
|
// indexOfArray := idx / lenOfUnit
|
||||||
|
// indexOfBit := lenOfUnit - idx%lenOfUnit - 1
|
||||||
|
// if p > median {
|
||||||
|
// phash[indexOfArray] |= 1 << uint(indexOfBit)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
return NewExtImageHash(phash, PHash, imgSize), nil
|
return NewExtImageHash(phash, PHash, imgSize), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,10 +164,8 @@ func ExtAverageHash(img image.Image, width, height int) (*ExtImageHash, error) {
|
|||||||
var ahash []uint64
|
var ahash []uint64
|
||||||
imgSize := width * height
|
imgSize := width * height
|
||||||
|
|
||||||
resized := resize.Resize(uint(width), uint(height), img, resize.Bilinear)
|
gray := resize(ToGray(img), width, height)
|
||||||
pixels := transforms.Rgb2Gray(resized)
|
avg := etcs.MeanOfPixels(gray.Pix)
|
||||||
flattens := transforms.FlattenPixels(pixels, width, height)
|
|
||||||
avg := etcs.MeanOfPixels(flattens)
|
|
||||||
|
|
||||||
lenOfUnit := 64
|
lenOfUnit := 64
|
||||||
if imgSize%lenOfUnit == 0 {
|
if imgSize%lenOfUnit == 0 {
|
||||||
@ -153,7 +173,7 @@ func ExtAverageHash(img image.Image, width, height int) (*ExtImageHash, error) {
|
|||||||
} else {
|
} else {
|
||||||
ahash = make([]uint64, imgSize/lenOfUnit+1)
|
ahash = make([]uint64, imgSize/lenOfUnit+1)
|
||||||
}
|
}
|
||||||
for idx, p := range flattens {
|
for idx, p := range gray.Pix {
|
||||||
indexOfArray := idx / lenOfUnit
|
indexOfArray := idx / lenOfUnit
|
||||||
indexOfBit := lenOfUnit - idx%lenOfUnit - 1
|
indexOfBit := lenOfUnit - idx%lenOfUnit - 1
|
||||||
if p > avg {
|
if p > avg {
|
||||||
@ -173,8 +193,7 @@ func ExtDifferenceHash(img image.Image, width, height int) (*ExtImageHash, error
|
|||||||
var dhash []uint64
|
var dhash []uint64
|
||||||
imgSize := width * height
|
imgSize := width * height
|
||||||
|
|
||||||
resized := resize.Resize(uint(width)+1, uint(height), img, resize.Bilinear)
|
gray := resize(ToGray(img), width+1, height)
|
||||||
pixels := transforms.Rgb2Gray(resized)
|
|
||||||
|
|
||||||
lenOfUnit := 64
|
lenOfUnit := 64
|
||||||
if imgSize%lenOfUnit == 0 {
|
if imgSize%lenOfUnit == 0 {
|
||||||
@ -183,11 +202,11 @@ func ExtDifferenceHash(img image.Image, width, height int) (*ExtImageHash, error
|
|||||||
dhash = make([]uint64, imgSize/lenOfUnit+1)
|
dhash = make([]uint64, imgSize/lenOfUnit+1)
|
||||||
}
|
}
|
||||||
idx := 0
|
idx := 0
|
||||||
for i := 0; i < len(pixels); i++ {
|
for y := 0; y < gray.Bounds().Dy(); y++ {
|
||||||
for j := 0; j < len(pixels[i])-1; j++ {
|
for x := 0; x < gray.Bounds().Dx()-1; x++ {
|
||||||
|
if gray.Pix[gray.PixOffset(x, y)] < gray.Pix[gray.PixOffset(x+1, y)] {
|
||||||
indexOfArray := idx / lenOfUnit
|
indexOfArray := idx / lenOfUnit
|
||||||
indexOfBit := lenOfUnit - idx%lenOfUnit - 1
|
indexOfBit := lenOfUnit - idx%lenOfUnit - 1
|
||||||
if pixels[i][j] < pixels[i][j+1] {
|
|
||||||
dhash[indexOfArray] |= 1 << uint(indexOfBit)
|
dhash[indexOfArray] |= 1 << uint(indexOfBit)
|
||||||
}
|
}
|
||||||
idx++
|
idx++
|
||||||
|
@ -23,26 +23,26 @@ func TestHashCompute(t *testing.T) {
|
|||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", AverageHash, "AverageHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", AverageHash, "AverageHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", AverageHash, "AverageHash", 42},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", AverageHash, "AverageHash", 40},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 4},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 38},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 34},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 40},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", AverageHash, "AverageHash", 40},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 6},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", AverageHash, "AverageHash", 6},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", DifferenceHash, "DifferenceHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", DifferenceHash, "DifferenceHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", DifferenceHash, "DifferenceHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", DifferenceHash, "DifferenceHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", DifferenceHash, "DifferenceHash", 43},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", DifferenceHash, "DifferenceHash", 40},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 2},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 37},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 38},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 43},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", DifferenceHash, "DifferenceHash", 42},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 16},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", DifferenceHash, "DifferenceHash", 20},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", PerceptionHash, "PerceptionHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", PerceptionHash, "PerceptionHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", PerceptionHash, "PerceptionHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", PerceptionHash, "PerceptionHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", PerceptionHash, "PerceptionHash", 32},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", PerceptionHash, "PerceptionHash", 34},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 2},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 30},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 30},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 34},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", PerceptionHash, "PerceptionHash", 34},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 20},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", PerceptionHash, "PerceptionHash", 20},
|
||||||
@ -218,60 +218,60 @@ func TestExtImageHashCompute(t *testing.T) {
|
|||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 42},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 40},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 4},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 38},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 34},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 40},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 40},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 6},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtAverageHash, "ExtAverageHash", 6},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 149},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 143},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 8},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 3},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 152},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 148},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 155},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 146},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 27},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtAverageHash, "ExtAverageHash", 31},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 17, 17, ExtAverageHash, "ExtAverageHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 32},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 32},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 2},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 2},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 30},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 30},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 34},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 34},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtPerceptionHash, "ExtPerceptionHash", 20},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 4, ExtPerceptionHash, "ExtPerceptionHash", 20},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 122},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 124},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 12},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 14},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 122},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 122},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 118},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 120},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtPerceptionHash, "ExtPerceptionHash", 104},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 4, ExtPerceptionHash, "ExtPerceptionHash", 102},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 43},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 40},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 2},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 37},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 38},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 43},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 42},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 16},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", 8, 8, ExtDifferenceHash, "ExtDifferenceHash", 20},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample4.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 139},
|
{"_examples/sample1.jpg", "_examples/sample2.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 137},
|
||||||
{"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 14},
|
{"_examples/sample1.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 13},
|
||||||
{"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 130},
|
{"_examples/sample1.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 124},
|
||||||
{"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 147},
|
{"_examples/sample2.jpg", "_examples/sample3.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 140},
|
||||||
{"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 89},
|
{"_examples/sample2.jpg", "_examples/sample4.jpg", 16, 16, ExtDifferenceHash, "ExtDifferenceHash", 109},
|
||||||
{"_examples/sample1.jpg", "_examples/sample1.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample1.jpg", "_examples/sample1.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample2.jpg", "_examples/sample2.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample2.jpg", "_examples/sample2.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
{"_examples/sample3.jpg", "_examples/sample3.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
{"_examples/sample3.jpg", "_examples/sample3.jpg", 17, 17, ExtDifferenceHash, "ExtDifferenceHash", 0},
|
||||||
@ -323,7 +323,7 @@ func TestExtImageHashCompute(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if dis1 != tt.distance {
|
if dis1 != tt.distance {
|
||||||
t.Errorf("%s: Distance between %v and %v is expected %v but got %v", tt.name, tt.img1, tt.img2, tt.distance, dis1)
|
t.Errorf("%s: Distance between %v and %v is expected %v but got %v: %v %v", tt.name, tt.img1, tt.img2, tt.distance, dis1, hash1, hash2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
imagehash.go
17
imagehash.go
@ -84,7 +84,8 @@ func (h *ImageHash) leftShiftSet(idx int) {
|
|||||||
h.hash |= 1 << uint(idx)
|
h.hash |= 1 << uint(idx)
|
||||||
}
|
}
|
||||||
|
|
||||||
const strFmt = "%1s:%016x"
|
const strFmtHex = "%1s:%016x"
|
||||||
|
const strFmtBin = "%1s:%064b"
|
||||||
|
|
||||||
// Dump method writes a binary serialization into w io.Writer.
|
// Dump method writes a binary serialization into w io.Writer.
|
||||||
func (h *ImageHash) Dump(w io.Writer) error {
|
func (h *ImageHash) Dump(w io.Writer) error {
|
||||||
@ -121,7 +122,7 @@ func LoadImageHash(b io.Reader) (*ImageHash, error) {
|
|||||||
func ImageHashFromString(s string) (*ImageHash, error) {
|
func ImageHashFromString(s string) (*ImageHash, error) {
|
||||||
var kindStr string
|
var kindStr string
|
||||||
var hash uint64
|
var hash uint64
|
||||||
_, err := fmt.Sscanf(s, strFmt, &kindStr, &hash)
|
_, err := fmt.Sscanf(s, strFmtHex, &kindStr, &hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("Couldn't parse string " + s)
|
return nil, errors.New("Couldn't parse string " + s)
|
||||||
}
|
}
|
||||||
@ -140,8 +141,12 @@ func ImageHashFromString(s string) (*ImageHash, error) {
|
|||||||
return NewImageHash(hash, kind), nil
|
return NewImageHash(hash, kind), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToString returns a hex representation of the hash
|
// String returns a hex representation of the hash
|
||||||
func (h *ImageHash) ToString() string {
|
func (h *ImageHash) String(bin bool) string {
|
||||||
|
strFmt := strFmtHex
|
||||||
|
if bin {
|
||||||
|
strFmt = strFmtBin
|
||||||
|
}
|
||||||
kindStr := ""
|
kindStr := ""
|
||||||
switch h.kind {
|
switch h.kind {
|
||||||
case AHash:
|
case AHash:
|
||||||
@ -274,8 +279,8 @@ func ExtImageHashFromString(s string) (*ExtImageHash, error) {
|
|||||||
return NewExtImageHash(hash, kind, len(hash)*64), nil
|
return NewExtImageHash(hash, kind, len(hash)*64), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToString returns a hex representation of big hash
|
// String returns a hex representation of big hash
|
||||||
func (h *ExtImageHash) ToString() string {
|
func (h *ExtImageHash) String() string {
|
||||||
var hexBytes []byte
|
var hexBytes []byte
|
||||||
for _, hash := range h.hash {
|
for _, hash := range h.hash {
|
||||||
hashBytes := make([]byte, 8)
|
hashBytes := make([]byte, 8)
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
//go:build !go1.9
|
||||||
// +build !go1.9
|
// +build !go1.9
|
||||||
|
|
||||||
package goimagehash
|
package goimagehash
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
//go:build go1.9
|
||||||
// +build go1.9
|
// +build go1.9
|
||||||
|
|
||||||
package goimagehash
|
package goimagehash
|
||||||
|
@ -78,7 +78,7 @@ func TestSerialization(t *testing.T) {
|
|||||||
AverageHash, PerceptionHash, DifferenceHash,
|
AverageHash, PerceptionHash, DifferenceHash,
|
||||||
}
|
}
|
||||||
extMethods := []func(img image.Image, width int, height int) (*ExtImageHash, error){
|
extMethods := []func(img image.Image, width int, height int) (*ExtImageHash, error){
|
||||||
ExtAverageHash, ExtPerceptionHash, ExtDifferenceHash,
|
ExtAverageHash /* ExtPerceptionHash, */, ExtDifferenceHash,
|
||||||
}
|
}
|
||||||
examples := []string{
|
examples := []string{
|
||||||
"_examples/sample1.jpg", "_examples/sample2.jpg", "_examples/sample3.jpg", "_examples/sample4.jpg",
|
"_examples/sample1.jpg", "_examples/sample2.jpg", "_examples/sample3.jpg", "_examples/sample4.jpg",
|
||||||
@ -99,7 +99,7 @@ func TestSerialization(t *testing.T) {
|
|||||||
hash, err := method(img)
|
hash, err := method(img)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
hex := hash.ToString()
|
hex := hash.String(false)
|
||||||
// len(kind) == 1, len(":") == 1, len(hash) == 16
|
// len(kind) == 1, len(":") == 1, len(hash) == 16
|
||||||
if len(hex) != 18 {
|
if len(hex) != 18 {
|
||||||
t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, methodStr, ex)
|
t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, methodStr, ex)
|
||||||
@ -124,7 +124,7 @@ func TestSerialization(t *testing.T) {
|
|||||||
hash, err := extMethod(img, size, size)
|
hash, err := extMethod(img, size, size)
|
||||||
checkErr(err)
|
checkErr(err)
|
||||||
|
|
||||||
hex := hash.ToString()
|
hex := hash.String()
|
||||||
// len(kind) == 1, len(":") == 1
|
// len(kind) == 1, len(":") == 1
|
||||||
if len(hex) != size*size/4+2 {
|
if len(hex) != size*size/4+2 {
|
||||||
t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, extMethodStr, ex)
|
t.Errorf("Got invalid hex string '%v'; %v of '%v'", hex, extMethodStr, ex)
|
||||||
|
@ -97,6 +97,27 @@ func DCT2DFast64(input *[]float64) (flattens [64]float64) {
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
func DCT2DFast32(input *[]float64) (flattens [64]float64) {
|
||||||
|
if len(*input) != 32*32 {
|
||||||
|
panic("incorrect input size, wanted 32x32.")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 32; i++ { // height
|
||||||
|
forwardDCT32((*input)[i*32 : (i*32)+32])
|
||||||
|
}
|
||||||
|
|
||||||
|
var row [32]float64
|
||||||
|
for i := 0; i < 8; i++ { // width
|
||||||
|
for j := 0; j < 32; j++ {
|
||||||
|
row[j] = (*input)[32*j+i]
|
||||||
|
}
|
||||||
|
forwardDCT32(row[:])
|
||||||
|
for j := 0; j < 8; j++ {
|
||||||
|
flattens[8*j+i] = row[j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// DCT2DFast256 function returns a result of DCT2D by using the separable property.
|
// DCT2DFast256 function returns a result of DCT2D by using the separable property.
|
||||||
// DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984.
|
// DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984.
|
||||||
|
@ -6,94 +6,33 @@ package transforms
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"image"
|
"image"
|
||||||
|
"image/color"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Rgb2Gray function converts RGB to a gray scale array.
|
func toGray(r, g, b uint8) uint8 {
|
||||||
func Rgb2Gray(colorImg image.Image) [][]float64 {
|
// 19595 + 38470 + 7471 equals 65536.
|
||||||
bounds := colorImg.Bounds()
|
return uint8((19595*uint32(r) + 38470*uint32(g) + 7471*uint32(b) + 1<<15) >> 16)
|
||||||
w, h := bounds.Max.X-bounds.Min.X, bounds.Max.Y-bounds.Min.Y
|
}
|
||||||
pixels := make([][]float64, h)
|
|
||||||
|
|
||||||
|
// Rgb2Gray function converts RGB to a gray scale array.
|
||||||
|
func Rgb2Gray(colorImg image.Image) []uint8 {
|
||||||
|
colorImg.Bounds().Dx()
|
||||||
|
bounds := colorImg.Bounds()
|
||||||
|
w, h := bounds.Dx(), bounds.Dy()
|
||||||
|
pixels := make([]uint8, h*w)
|
||||||
for i := range pixels {
|
for i := range pixels {
|
||||||
pixels[i] = make([]float64, w)
|
x, y := i%w, i/w
|
||||||
for j := range pixels[i] {
|
var R, G, B uint32
|
||||||
color := colorImg.At(j, i)
|
switch v := colorImg.At(x, y).(type) {
|
||||||
r, g, b, _ := color.RGBA()
|
case color.NRGBA: // pillow discards alpha during conversion and provides no way to pre-multiply it
|
||||||
lum := 0.299*float64(r/257) + 0.587*float64(g/257) + 0.114*float64(b/256)
|
R, G, B = uint32(v.R)<<8, uint32(v.G)<<8, uint32(v.B)<<8
|
||||||
pixels[i][j] = lum
|
default:
|
||||||
|
R, G, B, _ = colorImg.At(x, y).RGBA()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pillow only operates on 8bit data, operating on higher bit data produces rounding differences
|
||||||
|
pixels[i] = toGray(uint8(R>>8), uint8(G>>8), uint8(B>>8))
|
||||||
}
|
}
|
||||||
|
|
||||||
return pixels
|
return pixels
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rgb2GrayFast function converts RGB to a gray scale array.
|
|
||||||
func Rgb2GrayFast(colorImg image.Image, pixels *[]float64) {
|
|
||||||
bounds := colorImg.Bounds()
|
|
||||||
w, h := bounds.Max.X-bounds.Min.X, bounds.Max.Y-bounds.Min.Y
|
|
||||||
if w != h {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch c := colorImg.(type) {
|
|
||||||
case *image.YCbCr:
|
|
||||||
rgb2GrayYCbCR(c, *pixels, w)
|
|
||||||
case *image.RGBA:
|
|
||||||
rgb2GrayRGBA(c, *pixels, w)
|
|
||||||
default:
|
|
||||||
rgb2GrayDefault(c, *pixels, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pixel2Gray converts a pixel to grayscale value base on luminosity
|
|
||||||
func pixel2Gray(r, g, b, a uint32) float64 {
|
|
||||||
return 0.299*float64(r/257) + 0.587*float64(g/257) + 0.114*float64(b/256)
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb2GrayDefault uses the image.Image interface
|
|
||||||
func rgb2GrayDefault(colorImg image.Image, pixels []float64, s int) {
|
|
||||||
for i := 0; i < s; i++ {
|
|
||||||
for j := 0; j < s; j++ {
|
|
||||||
pixels[j+(i*s)] = pixel2Gray(colorImg.At(j, i).RGBA())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb2GrayYCbCR uses *image.YCbCr which is significantly faster than the image.Image interface.
|
|
||||||
func rgb2GrayYCbCR(colorImg *image.YCbCr, pixels []float64, s int) {
|
|
||||||
for i := 0; i < s; i++ {
|
|
||||||
for j := 0; j < s; j++ {
|
|
||||||
pixels[j+(i*s)] = pixel2Gray(colorImg.YCbCrAt(j, i).RGBA())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// rgb2GrayRGBA uses *image.RGBA which is significantly faster than the image.Image interface.
|
|
||||||
func rgb2GrayRGBA(colorImg *image.RGBA, pixels []float64, s int) {
|
|
||||||
for i := 0; i < s; i++ {
|
|
||||||
for j := 0; j < s; j++ {
|
|
||||||
pixels[(i*s)+j] = pixel2Gray(colorImg.At(j, i).RGBA())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlattenPixels function flattens 2d array into 1d array.
|
|
||||||
func FlattenPixels(pixels [][]float64, x int, y int) []float64 {
|
|
||||||
flattens := make([]float64, x*y)
|
|
||||||
for i := 0; i < y; i++ {
|
|
||||||
for j := 0; j < x; j++ {
|
|
||||||
flattens[y*i+j] = pixels[i][j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flattens
|
|
||||||
}
|
|
||||||
|
|
||||||
// FlattenPixelsFast64 function flattens 2d array into 1d array.
|
|
||||||
func FlattenPixelsFast64(pixels []float64, x int, y int) []float64 {
|
|
||||||
flattens := [64]float64{}
|
|
||||||
for i := 0; i < y; i++ {
|
|
||||||
for j := 0; j < x; j++ {
|
|
||||||
flattens[y*i+j] = pixels[(i*64)+j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return flattens[:]
|
|
||||||
}
|
|
||||||
|
512
yuv.color.convert.go
Normal file
512
yuv.color.convert.go
Normal file
@ -0,0 +1,512 @@
|
|||||||
|
package goimagehash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
YUV_FIX = 16 // fixed-point precision for RGB->YUV
|
||||||
|
YUV_HALF = 1 << (YUV_FIX - 1)
|
||||||
|
|
||||||
|
YUV_FIX2 = 6 // fixed-point precision for YUV->RGB
|
||||||
|
YUV_MASK2 = (256 << YUV_FIX2) - 1
|
||||||
|
)
|
||||||
|
|
||||||
|
func MultHi(v int32, coeff int32) int32 {
|
||||||
|
return (v * coeff) >> 8
|
||||||
|
}
|
||||||
|
func VP8Clip8(v int32) uint8 {
|
||||||
|
if (v &^ YUV_MASK2) == 0 {
|
||||||
|
return uint8(v >> YUV_FIX2)
|
||||||
|
}
|
||||||
|
if v < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return 0xff
|
||||||
|
}
|
||||||
|
func VP8YUVToR(y uint8, v uint8) uint8 {
|
||||||
|
return VP8Clip8(MultHi(int32(y), 19077) + MultHi(int32(v), 26149) - 14234)
|
||||||
|
}
|
||||||
|
func VP8YUVToG(y uint8, u uint8, v uint8) uint8 {
|
||||||
|
return VP8Clip8(MultHi(int32(y), 19077) - MultHi(int32(u), 6419) - MultHi(int32(v), 13320) + 8708)
|
||||||
|
}
|
||||||
|
func VP8YUVToB(y uint8, u uint8) uint8 {
|
||||||
|
return VP8Clip8(MultHi(int32(y), 19077) + MultHi(int32(u), 33050) - 17685)
|
||||||
|
}
|
||||||
|
func VP8YuvToRgb(y uint8, u uint8, v uint8) (rgb [3]uint8) {
|
||||||
|
rgb[0] = uint8(VP8YUVToR(y, v))
|
||||||
|
rgb[1] = uint8(VP8YUVToG(y, u, v))
|
||||||
|
rgb[2] = uint8(VP8YUVToB(y, u))
|
||||||
|
|
||||||
|
return rgb
|
||||||
|
}
|
||||||
|
func VP8YuvToBgr(y uint8, u uint8, v uint8) (bgr [3]uint8) {
|
||||||
|
bgr[0] = uint8(VP8YUVToB(y, u))
|
||||||
|
bgr[1] = uint8(VP8YUVToG(y, u, v))
|
||||||
|
bgr[2] = uint8(VP8YUVToR(y, v))
|
||||||
|
|
||||||
|
return bgr
|
||||||
|
}
|
||||||
|
func VP8YuvToRgb565(y uint8, u uint8, v uint8) (rgb [2]uint8) {
|
||||||
|
var (
|
||||||
|
r = VP8YUVToR(y, v)
|
||||||
|
g = VP8YUVToG(y, u, v)
|
||||||
|
b = VP8YUVToB(y, u)
|
||||||
|
)
|
||||||
|
|
||||||
|
var rg = (r & 0xf8) | (g >> 5)
|
||||||
|
var gb = ((g << 3) & 0xe0) | (b >> 3)
|
||||||
|
rgb[0] = uint8(rg)
|
||||||
|
rgb[1] = uint8(gb)
|
||||||
|
|
||||||
|
return rgb
|
||||||
|
}
|
||||||
|
func VP8YuvToRgba4444(y uint8, u uint8, v uint8) (argb [2]uint8) {
|
||||||
|
var (
|
||||||
|
r = VP8YUVToR(y, v)
|
||||||
|
g = VP8YUVToG(y, u, v)
|
||||||
|
b = VP8YUVToB(y, u)
|
||||||
|
)
|
||||||
|
|
||||||
|
var rg = (r & 0xf0) | (g >> 4)
|
||||||
|
var ba = (b & 0xf0) | 0x0f // overwrite the lower 4 bits
|
||||||
|
argb[0] = uint8(rg)
|
||||||
|
argb[1] = uint8(ba)
|
||||||
|
|
||||||
|
return argb
|
||||||
|
}
|
||||||
|
func VP8YuvToArgb(y uint8, u uint8, v uint8) (argb [4]uint8) {
|
||||||
|
argb[0] = 0xff
|
||||||
|
rgb := VP8YuvToRgb(y, u, v)
|
||||||
|
copy(argb[1:], rgb[:])
|
||||||
|
|
||||||
|
return argb
|
||||||
|
}
|
||||||
|
func VP8YuvToBgra(y uint8, u uint8, v uint8) (bgra [4]uint8) {
|
||||||
|
bgr := VP8YuvToBgr(y, u, v)
|
||||||
|
copy(bgra[:], bgr[:])
|
||||||
|
bgra[3] = 0xff
|
||||||
|
|
||||||
|
return bgra
|
||||||
|
}
|
||||||
|
func VP8YuvToRgba(y uint8, u uint8, v uint8) (rgba [4]uint8) {
|
||||||
|
rgb := VP8YuvToRgb(uint8(y), uint8(u), uint8(v))
|
||||||
|
copy(rgba[:], rgb[:])
|
||||||
|
rgba[3] = 0xff
|
||||||
|
|
||||||
|
return rgba
|
||||||
|
}
|
||||||
|
|
||||||
|
type YCbCr_color struct {
|
||||||
|
color.YCbCr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c YCbCr_color) RGBA() (uint32, uint32, uint32, uint32) {
|
||||||
|
rgb := VP8YuvToRgba(c.Y, c.Cb, c.Cr)
|
||||||
|
r, g, b := rgb[0], rgb[1], rgb[2]
|
||||||
|
return uint32(r), uint32(g), uint32(b), 0xff
|
||||||
|
}
|
||||||
|
|
||||||
|
// YCbCrModel is the Model for Y'CbCr colors.
|
||||||
|
var YCbCrModel color.Model = color.ModelFunc(yCbCrModel)
|
||||||
|
|
||||||
|
func yCbCrModel(c color.Color) color.Color {
|
||||||
|
if _, ok := c.(YCbCr_color); ok {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
r, g, b, _ := c.RGBA()
|
||||||
|
y, u, v, _ := P_ImagingConvertRGB2YCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8), 0xff)
|
||||||
|
return YCbCr_color{color.YCbCr{y, u, v}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type YCbCr struct {
|
||||||
|
*image.YCbCr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *YCbCr) ColorModel() color.Model {
|
||||||
|
return YCbCrModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *YCbCr) At(x, y int) color.Color {
|
||||||
|
return p.YCbCrAt(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *YCbCr) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.YCbCrAt(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r * 0x10101), uint16(g * 0x10101), uint16(b * 0x10101), uint16(a * 0x10101)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *YCbCr) YCbCrAt(x, y int) YCbCr_color {
|
||||||
|
return YCbCr_color{p.YCbCr.YCbCrAt(x, y)}
|
||||||
|
}
|
||||||
|
|
||||||
|
const SCALE = 6
|
||||||
|
|
||||||
|
var (
|
||||||
|
Y_R [256]int16 = [256]int16{
|
||||||
|
0, 19, 38, 57, 77, 96, 115, 134, 153, 172, 191, 210, 230, 249,
|
||||||
|
268, 287, 306, 325, 344, 364, 383, 402, 421, 440, 459, 478, 498, 517,
|
||||||
|
536, 555, 574, 593, 612, 631, 651, 670, 689, 708, 727, 746, 765, 785,
|
||||||
|
804, 823, 842, 861, 880, 899, 919, 938, 957, 976, 995, 1014, 1033, 1052,
|
||||||
|
1072, 1091, 1110, 1129, 1148, 1167, 1186, 1206, 1225, 1244, 1263, 1282, 1301, 1320,
|
||||||
|
1340, 1359, 1378, 1397, 1416, 1435, 1454, 1473, 1493, 1512, 1531, 1550, 1569, 1588,
|
||||||
|
1607, 1627, 1646, 1665, 1684, 1703, 1722, 1741, 1761, 1780, 1799, 1818, 1837, 1856,
|
||||||
|
1875, 1894, 1914, 1933, 1952, 1971, 1990, 2009, 2028, 2048, 2067, 2086, 2105, 2124,
|
||||||
|
2143, 2162, 2182, 2201, 2220, 2239, 2258, 2277, 2296, 2315, 2335, 2354, 2373, 2392,
|
||||||
|
2411, 2430, 2449, 2469, 2488, 2507, 2526, 2545, 2564, 2583, 2602, 2622, 2641, 2660,
|
||||||
|
2679, 2698, 2717, 2736, 2756, 2775, 2794, 2813, 2832, 2851, 2870, 2890, 2909, 2928,
|
||||||
|
2947, 2966, 2985, 3004, 3023, 3043, 3062, 3081, 3100, 3119, 3138, 3157, 3177, 3196,
|
||||||
|
3215, 3234, 3253, 3272, 3291, 3311, 3330, 3349, 3368, 3387, 3406, 3425, 3444, 3464,
|
||||||
|
3483, 3502, 3521, 3540, 3559, 3578, 3598, 3617, 3636, 3655, 3674, 3693, 3712, 3732,
|
||||||
|
3751, 3770, 3789, 3808, 3827, 3846, 3865, 3885, 3904, 3923, 3942, 3961, 3980, 3999,
|
||||||
|
4019, 4038, 4057, 4076, 4095, 4114, 4133, 4153, 4172, 4191, 4210, 4229, 4248, 4267,
|
||||||
|
4286, 4306, 4325, 4344, 4363, 4382, 4401, 4420, 4440, 4459, 4478, 4497, 4516, 4535,
|
||||||
|
4554, 4574, 4593, 4612, 4631, 4650, 4669, 4688, 4707, 4727, 4746, 4765, 4784, 4803,
|
||||||
|
4822, 4841, 4861, 4880,
|
||||||
|
}
|
||||||
|
Y_G [256]int16 = [256]int16{
|
||||||
|
0, 38, 75, 113, 150, 188, 225, 263, 301, 338, 376, 413, 451, 488,
|
||||||
|
526, 564, 601, 639, 676, 714, 751, 789, 826, 864, 902, 939, 977, 1014,
|
||||||
|
1052, 1089, 1127, 1165, 1202, 1240, 1277, 1315, 1352, 1390, 1428, 1465, 1503, 1540,
|
||||||
|
1578, 1615, 1653, 1691, 1728, 1766, 1803, 1841, 1878, 1916, 1954, 1991, 2029, 2066,
|
||||||
|
2104, 2141, 2179, 2217, 2254, 2292, 2329, 2367, 2404, 2442, 2479, 2517, 2555, 2592,
|
||||||
|
2630, 2667, 2705, 2742, 2780, 2818, 2855, 2893, 2930, 2968, 3005, 3043, 3081, 3118,
|
||||||
|
3156, 3193, 3231, 3268, 3306, 3344, 3381, 3419, 3456, 3494, 3531, 3569, 3607, 3644,
|
||||||
|
3682, 3719, 3757, 3794, 3832, 3870, 3907, 3945, 3982, 4020, 4057, 4095, 4132, 4170,
|
||||||
|
4208, 4245, 4283, 4320, 4358, 4395, 4433, 4471, 4508, 4546, 4583, 4621, 4658, 4696,
|
||||||
|
4734, 4771, 4809, 4846, 4884, 4921, 4959, 4997, 5034, 5072, 5109, 5147, 5184, 5222,
|
||||||
|
5260, 5297, 5335, 5372, 5410, 5447, 5485, 5522, 5560, 5598, 5635, 5673, 5710, 5748,
|
||||||
|
5785, 5823, 5861, 5898, 5936, 5973, 6011, 6048, 6086, 6124, 6161, 6199, 6236, 6274,
|
||||||
|
6311, 6349, 6387, 6424, 6462, 6499, 6537, 6574, 6612, 6650, 6687, 6725, 6762, 6800,
|
||||||
|
6837, 6875, 6913, 6950, 6988, 7025, 7063, 7100, 7138, 7175, 7213, 7251, 7288, 7326,
|
||||||
|
7363, 7401, 7438, 7476, 7514, 7551, 7589, 7626, 7664, 7701, 7739, 7777, 7814, 7852,
|
||||||
|
7889, 7927, 7964, 8002, 8040, 8077, 8115, 8152, 8190, 8227, 8265, 8303, 8340, 8378,
|
||||||
|
8415, 8453, 8490, 8528, 8566, 8603, 8641, 8678, 8716, 8753, 8791, 8828, 8866, 8904,
|
||||||
|
8941, 8979, 9016, 9054, 9091, 9129, 9167, 9204, 9242, 9279, 9317, 9354, 9392, 9430,
|
||||||
|
9467, 9505, 9542, 9580,
|
||||||
|
}
|
||||||
|
Y_B [256]int16 = [256]int16{
|
||||||
|
0, 7, 15, 22, 29, 36, 44, 51, 58, 66, 73, 80, 88, 95,
|
||||||
|
102, 109, 117, 124, 131, 139, 146, 153, 161, 168, 175, 182, 190, 197,
|
||||||
|
204, 212, 219, 226, 233, 241, 248, 255, 263, 270, 277, 285, 292, 299,
|
||||||
|
306, 314, 321, 328, 336, 343, 350, 358, 365, 372, 379, 387, 394, 401,
|
||||||
|
409, 416, 423, 430, 438, 445, 452, 460, 467, 474, 482, 489, 496, 503,
|
||||||
|
511, 518, 525, 533, 540, 547, 554, 562, 569, 576, 584, 591, 598, 606,
|
||||||
|
613, 620, 627, 635, 642, 649, 657, 664, 671, 679, 686, 693, 700, 708,
|
||||||
|
715, 722, 730, 737, 744, 751, 759, 766, 773, 781, 788, 795, 803, 810,
|
||||||
|
817, 824, 832, 839, 846, 854, 861, 868, 876, 883, 890, 897, 905, 912,
|
||||||
|
919, 927, 934, 941, 948, 956, 963, 970, 978, 985, 992, 1000, 1007, 1014,
|
||||||
|
1021, 1029, 1036, 1043, 1051, 1058, 1065, 1073, 1080, 1087, 1094, 1102, 1109, 1116,
|
||||||
|
1124, 1131, 1138, 1145, 1153, 1160, 1167, 1175, 1182, 1189, 1197, 1204, 1211, 1218,
|
||||||
|
1226, 1233, 1240, 1248, 1255, 1262, 1270, 1277, 1284, 1291, 1299, 1306, 1313, 1321,
|
||||||
|
1328, 1335, 1342, 1350, 1357, 1364, 1372, 1379, 1386, 1394, 1401, 1408, 1415, 1423,
|
||||||
|
1430, 1437, 1445, 1452, 1459, 1466, 1474, 1481, 1488, 1496, 1503, 1510, 1518, 1525,
|
||||||
|
1532, 1539, 1547, 1554, 1561, 1569, 1576, 1583, 1591, 1598, 1605, 1612, 1620, 1627,
|
||||||
|
1634, 1642, 1649, 1656, 1663, 1671, 1678, 1685, 1693, 1700, 1707, 1715, 1722, 1729,
|
||||||
|
1736, 1744, 1751, 1758, 1766, 1773, 1780, 1788, 1795, 1802, 1809, 1817, 1824, 1831,
|
||||||
|
1839, 1846, 1853, 1860,
|
||||||
|
}
|
||||||
|
Cb_R [256]int16 = [256]int16{
|
||||||
|
0, -10, -21, -31, -42, -53, -64, -75, -85, -96, -107, -118,
|
||||||
|
-129, -139, -150, -161, -172, -183, -193, -204, -215, -226, -237, -247,
|
||||||
|
-258, -269, -280, -291, -301, -312, -323, -334, -345, -355, -366, -377,
|
||||||
|
-388, -399, -409, -420, -431, -442, -453, -463, -474, -485, -496, -507,
|
||||||
|
-517, -528, -539, -550, -561, -571, -582, -593, -604, -615, -625, -636,
|
||||||
|
-647, -658, -669, -679, -690, -701, -712, -723, -733, -744, -755, -766,
|
||||||
|
-777, -787, -798, -809, -820, -831, -841, -852, -863, -874, -885, -895,
|
||||||
|
-906, -917, -928, -939, -949, -960, -971, -982, -993, -1003, -1014, -1025,
|
||||||
|
-1036, -1047, -1057, -1068, -1079, -1090, -1101, -1111, -1122, -1133, -1144, -1155,
|
||||||
|
-1165, -1176, -1187, -1198, -1209, -1219, -1230, -1241, -1252, -1263, -1273, -1284,
|
||||||
|
-1295, -1306, -1317, -1327, -1338, -1349, -1360, -1371, -1381, -1392, -1403, -1414,
|
||||||
|
-1425, -1435, -1446, -1457, -1468, -1479, -1489, -1500, -1511, -1522, -1533, -1543,
|
||||||
|
-1554, -1565, -1576, -1587, -1597, -1608, -1619, -1630, -1641, -1651, -1662, -1673,
|
||||||
|
-1684, -1694, -1705, -1716, -1727, -1738, -1748, -1759, -1770, -1781, -1792, -1802,
|
||||||
|
-1813, -1824, -1835, -1846, -1856, -1867, -1878, -1889, -1900, -1910, -1921, -1932,
|
||||||
|
-1943, -1954, -1964, -1975, -1986, -1997, -2008, -2018, -2029, -2040, -2051, -2062,
|
||||||
|
-2072, -2083, -2094, -2105, -2116, -2126, -2137, -2148, -2159, -2170, -2180, -2191,
|
||||||
|
-2202, -2213, -2224, -2234, -2245, -2256, -2267, -2278, -2288, -2299, -2310, -2321,
|
||||||
|
-2332, -2342, -2353, -2364, -2375, -2386, -2396, -2407, -2418, -2429, -2440, -2450,
|
||||||
|
-2461, -2472, -2483, -2494, -2504, -2515, -2526, -2537, -2548, -2558, -2569, -2580,
|
||||||
|
-2591, -2602, -2612, -2623, -2634, -2645, -2656, -2666, -2677, -2688, -2699, -2710,
|
||||||
|
-2720, -2731, -2742, -2753,
|
||||||
|
}
|
||||||
|
Cb_G [256]int16 = [256]int16{
|
||||||
|
0, -20, -41, -63, -84, -105, -126, -147, -169, -190, -211, -232,
|
||||||
|
-253, -275, -296, -317, -338, -359, -381, -402, -423, -444, -465, -487,
|
||||||
|
-508, -529, -550, -571, -593, -614, -635, -656, -677, -699, -720, -741,
|
||||||
|
-762, -783, -805, -826, -847, -868, -889, -911, -932, -953, -974, -995,
|
||||||
|
-1017, -1038, -1059, -1080, -1101, -1123, -1144, -1165, -1186, -1207, -1229, -1250,
|
||||||
|
-1271, -1292, -1313, -1335, -1356, -1377, -1398, -1419, -1441, -1462, -1483, -1504,
|
||||||
|
-1525, -1547, -1568, -1589, -1610, -1631, -1653, -1674, -1695, -1716, -1737, -1759,
|
||||||
|
-1780, -1801, -1822, -1843, -1865, -1886, -1907, -1928, -1949, -1971, -1992, -2013,
|
||||||
|
-2034, -2055, -2077, -2098, -2119, -2140, -2161, -2183, -2204, -2225, -2246, -2267,
|
||||||
|
-2289, -2310, -2331, -2352, -2373, -2395, -2416, -2437, -2458, -2479, -2501, -2522,
|
||||||
|
-2543, -2564, -2585, -2607, -2628, -2649, -2670, -2691, -2713, -2734, -2755, -2776,
|
||||||
|
-2797, -2819, -2840, -2861, -2882, -2903, -2925, -2946, -2967, -2988, -3009, -3031,
|
||||||
|
-3052, -3073, -3094, -3115, -3137, -3158, -3179, -3200, -3221, -3243, -3264, -3285,
|
||||||
|
-3306, -3328, -3349, -3370, -3391, -3412, -3434, -3455, -3476, -3497, -3518, -3540,
|
||||||
|
-3561, -3582, -3603, -3624, -3646, -3667, -3688, -3709, -3730, -3752, -3773, -3794,
|
||||||
|
-3815, -3836, -3858, -3879, -3900, -3921, -3942, -3964, -3985, -4006, -4027, -4048,
|
||||||
|
-4070, -4091, -4112, -4133, -4154, -4176, -4197, -4218, -4239, -4260, -4282, -4303,
|
||||||
|
-4324, -4345, -4366, -4388, -4409, -4430, -4451, -4472, -4494, -4515, -4536, -4557,
|
||||||
|
-4578, -4600, -4621, -4642, -4663, -4684, -4706, -4727, -4748, -4769, -4790, -4812,
|
||||||
|
-4833, -4854, -4875, -4896, -4918, -4939, -4960, -4981, -5002, -5024, -5045, -5066,
|
||||||
|
-5087, -5108, -5130, -5151, -5172, -5193, -5214, -5236, -5257, -5278, -5299, -5320,
|
||||||
|
-5342, -5363, -5384, -5405,
|
||||||
|
}
|
||||||
|
Cb_B [256]int16 = [256]int16{
|
||||||
|
0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416,
|
||||||
|
448, 480, 512, 544, 576, 608, 640, 672, 704, 736, 768, 800, 832, 864,
|
||||||
|
896, 928, 960, 992, 1024, 1056, 1088, 1120, 1152, 1184, 1216, 1248, 1280, 1312,
|
||||||
|
1344, 1376, 1408, 1440, 1472, 1504, 1536, 1568, 1600, 1632, 1664, 1696, 1728, 1760,
|
||||||
|
1792, 1824, 1856, 1888, 1920, 1952, 1984, 2016, 2048, 2080, 2112, 2144, 2176, 2208,
|
||||||
|
2240, 2272, 2304, 2336, 2368, 2400, 2432, 2464, 2496, 2528, 2560, 2592, 2624, 2656,
|
||||||
|
2688, 2720, 2752, 2784, 2816, 2848, 2880, 2912, 2944, 2976, 3008, 3040, 3072, 3104,
|
||||||
|
3136, 3168, 3200, 3232, 3264, 3296, 3328, 3360, 3392, 3424, 3456, 3488, 3520, 3552,
|
||||||
|
3584, 3616, 3648, 3680, 3712, 3744, 3776, 3808, 3840, 3872, 3904, 3936, 3968, 4000,
|
||||||
|
4032, 4064, 4096, 4128, 4160, 4192, 4224, 4256, 4288, 4320, 4352, 4384, 4416, 4448,
|
||||||
|
4480, 4512, 4544, 4576, 4608, 4640, 4672, 4704, 4736, 4768, 4800, 4832, 4864, 4896,
|
||||||
|
4928, 4960, 4992, 5024, 5056, 5088, 5120, 5152, 5184, 5216, 5248, 5280, 5312, 5344,
|
||||||
|
5376, 5408, 5440, 5472, 5504, 5536, 5568, 5600, 5632, 5664, 5696, 5728, 5760, 5792,
|
||||||
|
5824, 5856, 5888, 5920, 5952, 5984, 6016, 6048, 6080, 6112, 6144, 6176, 6208, 6240,
|
||||||
|
6272, 6304, 6336, 6368, 6400, 6432, 6464, 6496, 6528, 6560, 6592, 6624, 6656, 6688,
|
||||||
|
6720, 6752, 6784, 6816, 6848, 6880, 6912, 6944, 6976, 7008, 7040, 7072, 7104, 7136,
|
||||||
|
7168, 7200, 7232, 7264, 7296, 7328, 7360, 7392, 7424, 7456, 7488, 7520, 7552, 7584,
|
||||||
|
7616, 7648, 7680, 7712, 7744, 7776, 7808, 7840, 7872, 7904, 7936, 7968, 8000, 8032,
|
||||||
|
8064, 8096, 8128, 8160,
|
||||||
|
}
|
||||||
|
Cr_R = Cb_B
|
||||||
|
Cr_G [256]int16 = [256]int16{
|
||||||
|
0, -26, -53, -79, -106, -133, -160, -187, -213, -240, -267, -294,
|
||||||
|
-321, -347, -374, -401, -428, -455, -481, -508, -535, -562, -589, -615,
|
||||||
|
-642, -669, -696, -722, -749, -776, -803, -830, -856, -883, -910, -937,
|
||||||
|
-964, -990, -1017, -1044, -1071, -1098, -1124, -1151, -1178, -1205, -1232, -1258,
|
||||||
|
-1285, -1312, -1339, -1366, -1392, -1419, -1446, -1473, -1500, -1526, -1553, -1580,
|
||||||
|
-1607, -1634, -1660, -1687, -1714, -1741, -1768, -1794, -1821, -1848, -1875, -1902,
|
||||||
|
-1928, -1955, -1982, -2009, -2036, -2062, -2089, -2116, -2143, -2169, -2196, -2223,
|
||||||
|
-2250, -2277, -2303, -2330, -2357, -2384, -2411, -2437, -2464, -2491, -2518, -2545,
|
||||||
|
-2571, -2598, -2625, -2652, -2679, -2705, -2732, -2759, -2786, -2813, -2839, -2866,
|
||||||
|
-2893, -2920, -2947, -2973, -3000, -3027, -3054, -3081, -3107, -3134, -3161, -3188,
|
||||||
|
-3215, -3241, -3268, -3295, -3322, -3349, -3375, -3402, -3429, -3456, -3483, -3509,
|
||||||
|
-3536, -3563, -3590, -3616, -3643, -3670, -3697, -3724, -3750, -3777, -3804, -3831,
|
||||||
|
-3858, -3884, -3911, -3938, -3965, -3992, -4018, -4045, -4072, -4099, -4126, -4152,
|
||||||
|
-4179, -4206, -4233, -4260, -4286, -4313, -4340, -4367, -4394, -4420, -4447, -4474,
|
||||||
|
-4501, -4528, -4554, -4581, -4608, -4635, -4662, -4688, -4715, -4742, -4769, -4796,
|
||||||
|
-4822, -4849, -4876, -4903, -4929, -4956, -4983, -5010, -5037, -5063, -5090, -5117,
|
||||||
|
-5144, -5171, -5197, -5224, -5251, -5278, -5305, -5331, -5358, -5385, -5412, -5439,
|
||||||
|
-5465, -5492, -5519, -5546, -5573, -5599, -5626, -5653, -5680, -5707, -5733, -5760,
|
||||||
|
-5787, -5814, -5841, -5867, -5894, -5921, -5948, -5975, -6001, -6028, -6055, -6082,
|
||||||
|
-6109, -6135, -6162, -6189, -6216, -6243, -6269, -6296, -6323, -6350, -6376, -6403,
|
||||||
|
-6430, -6457, -6484, -6510, -6537, -6564, -6591, -6618, -6644, -6671, -6698, -6725,
|
||||||
|
-6752, -6778, -6805, -6832,
|
||||||
|
}
|
||||||
|
Cr_B [256]int16 = [256]int16{
|
||||||
|
0, -4, -9, -15, -20, -25, -30, -35, -41, -46, -51, -56,
|
||||||
|
-61, -67, -72, -77, -82, -87, -93, -98, -103, -108, -113, -119,
|
||||||
|
-124, -129, -134, -140, -145, -150, -155, -160, -166, -171, -176, -181,
|
||||||
|
-186, -192, -197, -202, -207, -212, -218, -223, -228, -233, -238, -244,
|
||||||
|
-249, -254, -259, -264, -270, -275, -280, -285, -290, -296, -301, -306,
|
||||||
|
-311, -316, -322, -327, -332, -337, -342, -348, -353, -358, -363, -368,
|
||||||
|
-374, -379, -384, -389, -394, -400, -405, -410, -415, -421, -426, -431,
|
||||||
|
-436, -441, -447, -452, -457, -462, -467, -473, -478, -483, -488, -493,
|
||||||
|
-499, -504, -509, -514, -519, -525, -530, -535, -540, -545, -551, -556,
|
||||||
|
-561, -566, -571, -577, -582, -587, -592, -597, -603, -608, -613, -618,
|
||||||
|
-623, -629, -634, -639, -644, -649, -655, -660, -665, -670, -675, -681,
|
||||||
|
-686, -691, -696, -702, -707, -712, -717, -722, -728, -733, -738, -743,
|
||||||
|
-748, -754, -759, -764, -769, -774, -780, -785, -790, -795, -800, -806,
|
||||||
|
-811, -816, -821, -826, -832, -837, -842, -847, -852, -858, -863, -868,
|
||||||
|
-873, -878, -884, -889, -894, -899, -904, -910, -915, -920, -925, -930,
|
||||||
|
-936, -941, -946, -951, -957, -962, -967, -972, -977, -983, -988, -993,
|
||||||
|
-998, -1003, -1009, -1014, -1019, -1024, -1029, -1035, -1040, -1045, -1050, -1055,
|
||||||
|
-1061, -1066, -1071, -1076, -1081, -1087, -1092, -1097, -1102, -1107, -1113, -1118,
|
||||||
|
-1123, -1128, -1133, -1139, -1144, -1149, -1154, -1159, -1165, -1170, -1175, -1180,
|
||||||
|
-1185, -1191, -1196, -1201, -1206, -1211, -1217, -1222, -1227, -1232, -1238, -1243,
|
||||||
|
-1248, -1253, -1258, -1264, -1269, -1274, -1279, -1284, -1290, -1295, -1300, -1305,
|
||||||
|
-1310, -1316, -1321, -1326,
|
||||||
|
}
|
||||||
|
R_Cr [256]int16 = [256]int16{
|
||||||
|
-11484, -11394, -11305, -11215, -11125, -11036, -10946, -10856, -10766, -10677,
|
||||||
|
-10587, -10497, -10407, -10318, -10228, -10138, -10049, -9959, -9869, -9779,
|
||||||
|
-9690, -9600, -9510, -9420, -9331, -9241, -9151, -9062, -8972, -8882,
|
||||||
|
-8792, -8703, -8613, -8523, -8433, -8344, -8254, -8164, -8075, -7985,
|
||||||
|
-7895, -7805, -7716, -7626, -7536, -7446, -7357, -7267, -7177, -7088,
|
||||||
|
-6998, -6908, -6818, -6729, -6639, -6549, -6459, -6370, -6280, -6190,
|
||||||
|
-6101, -6011, -5921, -5831, -5742, -5652, -5562, -5472, -5383, -5293,
|
||||||
|
-5203, -5113, -5024, -4934, -4844, -4755, -4665, -4575, -4485, -4396,
|
||||||
|
-4306, -4216, -4126, -4037, -3947, -3857, -3768, -3678, -3588, -3498,
|
||||||
|
-3409, -3319, -3229, -3139, -3050, -2960, -2870, -2781, -2691, -2601,
|
||||||
|
-2511, -2422, -2332, -2242, -2152, -2063, -1973, -1883, -1794, -1704,
|
||||||
|
-1614, -1524, -1435, -1345, -1255, -1165, -1076, -986, -896, -807,
|
||||||
|
-717, -627, -537, -448, -358, -268, -178, -89, 0, 90,
|
||||||
|
179, 269, 359, 449, 538, 628, 718, 808, 897, 987,
|
||||||
|
1077, 1166, 1256, 1346, 1436, 1525, 1615, 1705, 1795, 1884,
|
||||||
|
1974, 2064, 2153, 2243, 2333, 2423, 2512, 2602, 2692, 2782,
|
||||||
|
2871, 2961, 3051, 3140, 3230, 3320, 3410, 3499, 3589, 3679,
|
||||||
|
3769, 3858, 3948, 4038, 4127, 4217, 4307, 4397, 4486, 4576,
|
||||||
|
4666, 4756, 4845, 4935, 5025, 5114, 5204, 5294, 5384, 5473,
|
||||||
|
5563, 5653, 5743, 5832, 5922, 6012, 6102, 6191, 6281, 6371,
|
||||||
|
6460, 6550, 6640, 6730, 6819, 6909, 6999, 7089, 7178, 7268,
|
||||||
|
7358, 7447, 7537, 7627, 7717, 7806, 7896, 7986, 8076, 8165,
|
||||||
|
8255, 8345, 8434, 8524, 8614, 8704, 8793, 8883, 8973, 9063,
|
||||||
|
9152, 9242, 9332, 9421, 9511, 9601, 9691, 9780, 9870, 9960,
|
||||||
|
10050, 10139, 10229, 10319, 10408, 10498, 10588, 10678, 10767, 10857,
|
||||||
|
10947, 11037, 11126, 11216, 11306, 11395,
|
||||||
|
}
|
||||||
|
G_Cb [256]int16 = [256]int16{
|
||||||
|
2819, 2797, 2775, 2753, 2731, 2709, 2687, 2665, 2643, 2621, 2599, 2577,
|
||||||
|
2555, 2533, 2511, 2489, 2467, 2445, 2423, 2401, 2379, 2357, 2335, 2313,
|
||||||
|
2291, 2269, 2247, 2225, 2202, 2180, 2158, 2136, 2114, 2092, 2070, 2048,
|
||||||
|
2026, 2004, 1982, 1960, 1938, 1916, 1894, 1872, 1850, 1828, 1806, 1784,
|
||||||
|
1762, 1740, 1718, 1696, 1674, 1652, 1630, 1608, 1586, 1564, 1542, 1520,
|
||||||
|
1498, 1476, 1454, 1432, 1410, 1388, 1366, 1344, 1321, 1299, 1277, 1255,
|
||||||
|
1233, 1211, 1189, 1167, 1145, 1123, 1101, 1079, 1057, 1035, 1013, 991,
|
||||||
|
969, 947, 925, 903, 881, 859, 837, 815, 793, 771, 749, 727,
|
||||||
|
705, 683, 661, 639, 617, 595, 573, 551, 529, 507, 485, 463,
|
||||||
|
440, 418, 396, 374, 352, 330, 308, 286, 264, 242, 220, 198,
|
||||||
|
176, 154, 132, 110, 88, 66, 44, 22, 0, -21, -43, -65,
|
||||||
|
-87, -109, -131, -153, -175, -197, -219, -241, -263, -285, -307, -329,
|
||||||
|
-351, -373, -395, -417, -439, -462, -484, -506, -528, -550, -572, -594,
|
||||||
|
-616, -638, -660, -682, -704, -726, -748, -770, -792, -814, -836, -858,
|
||||||
|
-880, -902, -924, -946, -968, -990, -1012, -1034, -1056, -1078, -1100, -1122,
|
||||||
|
-1144, -1166, -1188, -1210, -1232, -1254, -1276, -1298, -1320, -1343, -1365, -1387,
|
||||||
|
-1409, -1431, -1453, -1475, -1497, -1519, -1541, -1563, -1585, -1607, -1629, -1651,
|
||||||
|
-1673, -1695, -1717, -1739, -1761, -1783, -1805, -1827, -1849, -1871, -1893, -1915,
|
||||||
|
-1937, -1959, -1981, -2003, -2025, -2047, -2069, -2091, -2113, -2135, -2157, -2179,
|
||||||
|
-2201, -2224, -2246, -2268, -2290, -2312, -2334, -2356, -2378, -2400, -2422, -2444,
|
||||||
|
-2466, -2488, -2510, -2532, -2554, -2576, -2598, -2620, -2642, -2664, -2686, -2708,
|
||||||
|
-2730, -2752, -2774, -2796,
|
||||||
|
}
|
||||||
|
G_Cr [256]int16 = [256]int16{
|
||||||
|
5850, 5805, 5759, 5713, 5667, 5622, 5576, 5530, 5485, 5439, 5393, 5347,
|
||||||
|
5302, 5256, 5210, 5165, 5119, 5073, 5028, 4982, 4936, 4890, 4845, 4799,
|
||||||
|
4753, 4708, 4662, 4616, 4570, 4525, 4479, 4433, 4388, 4342, 4296, 4251,
|
||||||
|
4205, 4159, 4113, 4068, 4022, 3976, 3931, 3885, 3839, 3794, 3748, 3702,
|
||||||
|
3656, 3611, 3565, 3519, 3474, 3428, 3382, 3336, 3291, 3245, 3199, 3154,
|
||||||
|
3108, 3062, 3017, 2971, 2925, 2879, 2834, 2788, 2742, 2697, 2651, 2605,
|
||||||
|
2559, 2514, 2468, 2422, 2377, 2331, 2285, 2240, 2194, 2148, 2102, 2057,
|
||||||
|
2011, 1965, 1920, 1874, 1828, 1782, 1737, 1691, 1645, 1600, 1554, 1508,
|
||||||
|
1463, 1417, 1371, 1325, 1280, 1234, 1188, 1143, 1097, 1051, 1006, 960,
|
||||||
|
914, 868, 823, 777, 731, 686, 640, 594, 548, 503, 457, 411,
|
||||||
|
366, 320, 274, 229, 183, 137, 91, 46, 0, -45, -90, -136,
|
||||||
|
-182, -228, -273, -319, -365, -410, -456, -502, -547, -593, -639, -685,
|
||||||
|
-730, -776, -822, -867, -913, -959, -1005, -1050, -1096, -1142, -1187, -1233,
|
||||||
|
-1279, -1324, -1370, -1416, -1462, -1507, -1553, -1599, -1644, -1690, -1736, -1781,
|
||||||
|
-1827, -1873, -1919, -1964, -2010, -2056, -2101, -2147, -2193, -2239, -2284, -2330,
|
||||||
|
-2376, -2421, -2467, -2513, -2558, -2604, -2650, -2696, -2741, -2787, -2833, -2878,
|
||||||
|
-2924, -2970, -3016, -3061, -3107, -3153, -3198, -3244, -3290, -3335, -3381, -3427,
|
||||||
|
-3473, -3518, -3564, -3610, -3655, -3701, -3747, -3793, -3838, -3884, -3930, -3975,
|
||||||
|
-4021, -4067, -4112, -4158, -4204, -4250, -4295, -4341, -4387, -4432, -4478, -4524,
|
||||||
|
-4569, -4615, -4661, -4707, -4752, -4798, -4844, -4889, -4935, -4981, -5027, -5072,
|
||||||
|
-5118, -5164, -5209, -5255, -5301, -5346, -5392, -5438, -5484, -5529, -5575, -5621,
|
||||||
|
-5666, -5712, -5758, -5804,
|
||||||
|
}
|
||||||
|
B_Cb [256]int16 = [256]int16{
|
||||||
|
-14515, -14402, -14288, -14175, -14062, -13948, -13835, -13721, -13608, -13495,
|
||||||
|
-13381, -13268, -13154, -13041, -12928, -12814, -12701, -12587, -12474, -12360,
|
||||||
|
-12247, -12134, -12020, -11907, -11793, -11680, -11567, -11453, -11340, -11226,
|
||||||
|
-11113, -11000, -10886, -10773, -10659, -10546, -10433, -10319, -10206, -10092,
|
||||||
|
-9979, -9865, -9752, -9639, -9525, -9412, -9298, -9185, -9072, -8958,
|
||||||
|
-8845, -8731, -8618, -8505, -8391, -8278, -8164, -8051, -7938, -7824,
|
||||||
|
-7711, -7597, -7484, -7371, -7257, -7144, -7030, -6917, -6803, -6690,
|
||||||
|
-6577, -6463, -6350, -6236, -6123, -6010, -5896, -5783, -5669, -5556,
|
||||||
|
-5443, -5329, -5216, -5102, -4989, -4876, -4762, -4649, -4535, -4422,
|
||||||
|
-4309, -4195, -4082, -3968, -3855, -3741, -3628, -3515, -3401, -3288,
|
||||||
|
-3174, -3061, -2948, -2834, -2721, -2607, -2494, -2381, -2267, -2154,
|
||||||
|
-2040, -1927, -1814, -1700, -1587, -1473, -1360, -1246, -1133, -1020,
|
||||||
|
-906, -793, -679, -566, -453, -339, -226, -112, 0, 113,
|
||||||
|
227, 340, 454, 567, 680, 794, 907, 1021, 1134, 1247,
|
||||||
|
1361, 1474, 1588, 1701, 1815, 1928, 2041, 2155, 2268, 2382,
|
||||||
|
2495, 2608, 2722, 2835, 2949, 3062, 3175, 3289, 3402, 3516,
|
||||||
|
3629, 3742, 3856, 3969, 4083, 4196, 4310, 4423, 4536, 4650,
|
||||||
|
4763, 4877, 4990, 5103, 5217, 5330, 5444, 5557, 5670, 5784,
|
||||||
|
5897, 6011, 6124, 6237, 6351, 6464, 6578, 6691, 6804, 6918,
|
||||||
|
7031, 7145, 7258, 7372, 7485, 7598, 7712, 7825, 7939, 8052,
|
||||||
|
8165, 8279, 8392, 8506, 8619, 8732, 8846, 8959, 9073, 9186,
|
||||||
|
9299, 9413, 9526, 9640, 9753, 9866, 9980, 10093, 10207, 10320,
|
||||||
|
10434, 10547, 10660, 10774, 10887, 11001, 11114, 11227, 11341,
|
||||||
|
11454, 11568, 11681, 11794, 11908, 12021, 12135, 12248, 12361,
|
||||||
|
12475, 12588, 12702, 12815, 12929, 13042, 13155, 13269, 13382,
|
||||||
|
13496, 13609, 13722, 13836, 13949, 14063, 14176, 14289, 14403,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func P_ImagingConvertRGB2YCbCr(r, g, b, a uint8) (y, cb, cr, al uint8) {
|
||||||
|
y = uint8(Y_R[r]+Y_G[g]+Y_B[b]) >> SCALE
|
||||||
|
cb = uint8((Cb_R[r]+Cb_G[g]+Cb_B[b])>>SCALE) + 128
|
||||||
|
cr = uint8((Cr_R[r]+Cr_G[g]+Cr_B[b])>>SCALE) + 128
|
||||||
|
al = a
|
||||||
|
return y, cb, cr, al
|
||||||
|
}
|
||||||
|
|
||||||
|
func p_bounds(v int32) uint8 {
|
||||||
|
if v < 0 {
|
||||||
|
return 0
|
||||||
|
} else if v > 255 {
|
||||||
|
return 255
|
||||||
|
}
|
||||||
|
return uint8(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func P_ImagingConvertYCbCr2RGB(y, cb, cr, a uint8) (r, g, b, al uint8) {
|
||||||
|
r = p_bounds(int32(y) + int32(R_Cr[cr]>>SCALE))
|
||||||
|
g = p_bounds(int32(y) + int32((G_Cb[cb]+G_Cr[cr])>>SCALE))
|
||||||
|
b = p_bounds(int32(y) + int32(B_Cb[cb]>>SCALE))
|
||||||
|
al = a
|
||||||
|
return r, g, b, al
|
||||||
|
}
|
||||||
|
|
||||||
|
type P_YCbCr_color struct {
|
||||||
|
color.YCbCr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c P_YCbCr_color) RGBA() (uint32, uint32, uint32, uint32) {
|
||||||
|
r, g, b, _ := P_ImagingConvertYCbCr2RGB(c.Y, c.Cb, c.Cr, 0)
|
||||||
|
// r1, g1, b1, _ := c.YCbCr.RGBA()
|
||||||
|
// fmt.Printf("go %#04x, %#04x, %#04x\n", r1, g1, b1)
|
||||||
|
// fmt.Printf("py %#04x, %#04x, %#04x\n", uint32(r)*0x0101, uint32(g)*0x0101, uint32(b)*0x0101)
|
||||||
|
return uint32(r) * 0x0101, uint32(g) * 0x0101, uint32(b) * 0x0101, 0xffff
|
||||||
|
}
|
||||||
|
|
||||||
|
// YCbCrModel is the Model for Y'CbCr colors.
|
||||||
|
var P_YCbCrModel color.Model = color.ModelFunc(p_yCbCrModel)
|
||||||
|
|
||||||
|
func p_yCbCrModel(c color.Color) color.Color {
|
||||||
|
if _, ok := c.(P_YCbCr_color); ok {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
panic("didn't think this was called")
|
||||||
|
r, g, b, _ := c.RGBA()
|
||||||
|
y, u, v, _ := P_ImagingConvertRGB2YCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8), 0xff)
|
||||||
|
return P_YCbCr_color{color.YCbCr{y, u, v}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type P_YCbCr struct {
|
||||||
|
*image.YCbCr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *P_YCbCr) ColorModel() color.Model {
|
||||||
|
return P_YCbCrModel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *P_YCbCr) At(x, y int) color.Color {
|
||||||
|
return p.YCbCrAt(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *P_YCbCr) RGBA64At(x, y int) color.RGBA64 {
|
||||||
|
r, g, b, a := p.YCbCrAt(x, y).RGBA()
|
||||||
|
return color.RGBA64{uint16(r), uint16(g), uint16(b), uint16(a)}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *P_YCbCr) YCbCrAt(x, y int) P_YCbCr_color {
|
||||||
|
if !(image.Point{x, y}.In(p.Rect)) {
|
||||||
|
return P_YCbCr_color{}
|
||||||
|
}
|
||||||
|
yi := p.YOffset(x, y)
|
||||||
|
ci := p.COffset(x, y)
|
||||||
|
return P_YCbCr_color{color.YCbCr{
|
||||||
|
p.Y[yi],
|
||||||
|
p.Cb[ci],
|
||||||
|
p.Cr[ci],
|
||||||
|
}}
|
||||||
|
}
|
135
yuv.fancy.upscaling.go
Normal file
135
yuv.fancy.upscaling.go
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
package goimagehash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"image"
|
||||||
|
)
|
||||||
|
|
||||||
|
type WebPUpsampleLinePairFunc func(top_y []uint8, bottom_y []uint8,
|
||||||
|
top_u []uint8, top_v []uint8,
|
||||||
|
bottom_u []uint8, bottom_v []uint8,
|
||||||
|
top_dst []uint8, bottom_dst []uint8, len int)
|
||||||
|
|
||||||
|
func FancyUpscale(yuv *image.YCbCr) *image.RGBA {
|
||||||
|
rgb := image.NewRGBA(image.Rect(0, 0, yuv.Rect.Dx(), yuv.Rect.Dy()))
|
||||||
|
var (
|
||||||
|
upsample WebPUpsampleLinePairFunc = UpsampleRgbaLinePair
|
||||||
|
|
||||||
|
last_row int = yuv.Rect.Dy()
|
||||||
|
mb_w int = yuv.Rect.Dx()
|
||||||
|
)
|
||||||
|
// First line is special cased. We mirror the u/v samples at boundary.
|
||||||
|
upsample(yuv.Y[:yuv.YStride], nil,
|
||||||
|
yuv.Cb[:yuv.CStride], yuv.Cr[:yuv.CStride],
|
||||||
|
yuv.Cb[:yuv.CStride], yuv.Cr[:yuv.CStride],
|
||||||
|
rgb.Pix[:rgb.Stride], nil,
|
||||||
|
mb_w,
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
top_c int
|
||||||
|
bottom_c int
|
||||||
|
top_y int
|
||||||
|
bottom_y int
|
||||||
|
top_dst int
|
||||||
|
bottom_dst int
|
||||||
|
)
|
||||||
|
// Loop over each output pairs of row.
|
||||||
|
for row := 1; row+1 < last_row; row += 2 {
|
||||||
|
top_c = yuv.COffset(0, row)
|
||||||
|
bottom_c = top_c + yuv.CStride
|
||||||
|
|
||||||
|
top_y = yuv.YOffset(0, row)
|
||||||
|
bottom_y = top_y + yuv.YStride
|
||||||
|
|
||||||
|
top_dst = rgb.PixOffset(0, row)
|
||||||
|
bottom_dst = top_dst + rgb.Stride
|
||||||
|
|
||||||
|
upsample(yuv.Y[top_y:bottom_y], yuv.Y[bottom_y:bottom_y+yuv.YStride],
|
||||||
|
yuv.Cb[top_c:bottom_c], yuv.Cr[top_c:bottom_c],
|
||||||
|
yuv.Cb[bottom_c:bottom_c+yuv.CStride], yuv.Cr[bottom_c:bottom_c+yuv.CStride],
|
||||||
|
rgb.Pix[top_dst:bottom_dst], rgb.Pix[bottom_dst:bottom_dst+rgb.Stride],
|
||||||
|
mb_w,
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Process the very last row of even-sized picture
|
||||||
|
if last_row%2 == 0 {
|
||||||
|
upsample(yuv.Y[len(yuv.Y)-yuv.YStride:], nil,
|
||||||
|
yuv.Cb[len(yuv.Cb)-yuv.CStride:], yuv.Cr[len(yuv.Cr)-yuv.CStride:],
|
||||||
|
yuv.Cb[len(yuv.Cb)-yuv.CStride:], yuv.Cr[len(yuv.Cr)-yuv.CStride:],
|
||||||
|
rgb.Pix[len(rgb.Pix)-rgb.Stride:], nil,
|
||||||
|
mb_w,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return rgb
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpsampleRgbaLinePair(top_y []uint8, bottom_y []uint8,
|
||||||
|
top_u []uint8, top_v []uint8,
|
||||||
|
bottom_u []uint8, bottom_v []uint8,
|
||||||
|
top_dst []uint8, bottom_dst []uint8, rowLength int,
|
||||||
|
) {
|
||||||
|
var (
|
||||||
|
x int
|
||||||
|
last_pixel_pair int = (rowLength - 1) >> 1
|
||||||
|
tl_uv uint32 = (uint32(top_u[0]) | (uint32(top_v[0]) << 16)) /* top-left sample */
|
||||||
|
l_uv uint32 = (uint32(bottom_u[0]) | (uint32(bottom_v[0]) << 16)) /* left-sample */
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var uv0 uint32 = (3*tl_uv + l_uv + 0x00020002) >> 2
|
||||||
|
dst := VP8YuvToRgba(top_y[0], uint8(uv0&0xff), uint8((uv0 >> 16)))
|
||||||
|
copy(top_dst[0:], dst[:])
|
||||||
|
}
|
||||||
|
if bottom_y != nil {
|
||||||
|
var uv0 uint32 = (3*l_uv + tl_uv + 0x00020002) >> 2
|
||||||
|
dst := VP8YuvToRgba(bottom_y[0], uint8(uv0&0xff), uint8(uv0>>16))
|
||||||
|
copy(bottom_dst[0:], dst[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
for x = 1; x <= last_pixel_pair; x++ {
|
||||||
|
var (
|
||||||
|
t_uv uint32 = (uint32(top_u[x]) | (uint32(top_v[x]) << 16)) /* top sample */
|
||||||
|
uv uint32 = (uint32(bottom_u[x]) | (uint32(bottom_v[x]) << 16)) /* sample */
|
||||||
|
|
||||||
|
/* precompute invariant values associated with first and second diagonals*/
|
||||||
|
avg uint32 = tl_uv + t_uv + l_uv + uv + 0x00080008
|
||||||
|
diag_12 uint32 = (avg + 2*(t_uv+l_uv)) >> 3
|
||||||
|
diag_03 uint32 = (avg + 2*(tl_uv+uv)) >> 3
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var (
|
||||||
|
uv0 uint32 = (diag_12 + tl_uv) >> 1
|
||||||
|
uv1 uint32 = (diag_03 + t_uv) >> 1
|
||||||
|
)
|
||||||
|
dst := VP8YuvToRgba(top_y[2*x-1], uint8(uv0&0xff), uint8(uv0>>16))
|
||||||
|
copy(top_dst[(2*x-1)*4:], dst[:])
|
||||||
|
dst = VP8YuvToRgba(top_y[2*x-0], uint8(uv1&0xff), uint8(uv1>>16))
|
||||||
|
copy(top_dst[(2*x-0)*4:], dst[:])
|
||||||
|
}
|
||||||
|
if bottom_y != nil {
|
||||||
|
var (
|
||||||
|
uv0 uint32 = (diag_03 + l_uv) >> 1
|
||||||
|
uv1 uint32 = (diag_12 + uv) >> 1
|
||||||
|
)
|
||||||
|
dst := VP8YuvToRgba(bottom_y[2*x-1], uint8(uv0&0xff), uint8(uv0>>16))
|
||||||
|
copy(bottom_dst[(2*x-1)*4:], dst[:])
|
||||||
|
dst = VP8YuvToRgba(bottom_y[2*x+0], uint8(uv1&0xff), uint8(uv1>>16))
|
||||||
|
copy(bottom_dst[(2*x+0)*4:], dst[:])
|
||||||
|
}
|
||||||
|
tl_uv = t_uv
|
||||||
|
l_uv = uv
|
||||||
|
}
|
||||||
|
if rowLength%2 == 0 {
|
||||||
|
{
|
||||||
|
var uv0 uint32 = (3*tl_uv + l_uv + 0x00020002) >> 2
|
||||||
|
dst := VP8YuvToRgba(top_y[rowLength-1], uint8(uv0&0xff), uint8(uv0>>16))
|
||||||
|
copy(top_dst[(rowLength-1)*4:], dst[:])
|
||||||
|
}
|
||||||
|
if bottom_y != nil {
|
||||||
|
var uv0 uint32 = (3*l_uv + tl_uv + 0x00020002) >> 2
|
||||||
|
dst := VP8YuvToRgba(bottom_y[rowLength-1], uint8(uv0&0xff), uint8(uv0>>16))
|
||||||
|
copy(bottom_dst[(rowLength-1)*4:], dst[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user