Skip to content
This repository was archived by the owner on Nov 12, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
157 changes: 0 additions & 157 deletions diskindexstore.go

This file was deleted.

162 changes: 78 additions & 84 deletions fingerprint.go
Original file line number Diff line number Diff line change
@@ -1,127 +1,121 @@
package simian

import (
"bytes"
"encoding/hex"
"fmt"
"image"
"image/color"
"math"

"golang.org/x/image/draw"
)

const bitsPerSample = 4
const sampleBitsMask = (1 << bitsPerSample) - 1
const samplesPerByte = 8 / bitsPerSample
const fingerprintDCTSideLength = 8
const fingerprintACShift = 7
const fingerprintDifferenceScale = 22

type Fingerprint struct {
samples []uint8
}

func (f *Fingerprint) Bytes() []byte {
packed := bytes.Buffer{}
current := byte(0)
bits := uint(8)
i := 0

for ; i < len(f.samples); i++ {
y := f.samples[i]
const SamplesPerFingerprint = fingerprintDCTSideLength * fingerprintDCTSideLength

bits -= bitsPerSample
current = (current << bitsPerSample) | (y >> (8 - bitsPerSample))

if bits == 0 {
packed.WriteByte(current)
current = 0
bits = 8
}
}
type Fingerprint [SamplesPerFingerprint]int16

if bits < 8 {
current <<= bits
packed.WriteByte(current)
func (f *Fingerprint) Difference(other *Fingerprint) float64 {
result := 0.0
for i := 0; i < SamplesPerFingerprint; i++ {
result += math.Abs(float64(f[i] - other[i]))
}

return packed.Bytes()
return result / float64(SamplesPerFingerprint*fingerprintDifferenceScale)
}

func (f *Fingerprint) Difference(to Fingerprint) (diff float64) {
return math.Min(float64(f.Distance(to))/float64(len(to.samples)*255), 1.0)
func (f *Fingerprint) Prefix(level int) []int16 {
return f[:level*level]
}

func (f *Fingerprint) Distance(to Fingerprint) (dist uint64) {
if len(f.samples) != len(to.samples) {
return math.MaxUint64
}
func NewFingerprintFromImage(src image.Image) *Fingerprint {
scaled := image.NewNRGBA(image.Rectangle{Max: image.Point{X: fingerprintDCTSideLength, Y: fingerprintDCTSideLength}})
draw.BiLinear.Scale(scaled, scaled.Bounds(), src, src.Bounds(), draw.Src, nil)

for i := 0; i < len(f.samples); i++ {
if f.samples[i] > to.samples[i] {
dist += uint64(f.samples[i] - to.samples[i])
} else {
dist += uint64(to.samples[i] - f.samples[i])
samples := make([]int8, SamplesPerFingerprint)
offset := 0

// Sample from RGBA pixel values
for i := scaled.Bounds().Min.Y; i < scaled.Bounds().Max.Y; i++ {
for j := scaled.Bounds().Min.X; j < scaled.Bounds().Max.X; j++ {
r, g, b, _ := scaled.At(j, i).RGBA()
y, _, _ := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))

val := int8(y - 128)
samples[offset] = val
offset++
}
}

return dist
}
dct := DCT(fingerprintDCTSideLength, fingerprintDCTSideLength, samples)

func (f *Fingerprint) MarshalText() (text []byte, err error) {
bytes := f.Bytes()
result := make([]byte, hex.EncodedLen(len(bytes)))

hex.Encode(result, bytes)
return result, nil
}
min := int16(math.MaxInt16)
max := int16(math.MinInt16)

func (f *Fingerprint) Size() int {
return int(math.Sqrt(float64(len(f.samples))))
}
// Find the dynamic range for DC coefficients
for i := 1; i < len(dct); i++ {
if dct[i] < min {
min = dct[i]
}
if dct[i] > max {
max = dct[i]
}
}

func (f Fingerprint) String() string {
return hex.EncodeToString(f.Bytes())
}
scale := 127.0 / float64(max-min) / 2.0

func (f *Fingerprint) UnmarshalBytes(fingerprintBytes []byte) error {
sampleCount := int(math.Sqrt(float64(len(fingerprintBytes) * samplesPerByte)))
sampleCount *= sampleCount
f.samples = make([]uint8, sampleCount)
fmt.Printf("DCT:\n")

for i := 0; i < sampleCount; i++ {
b := fingerprintBytes[i/samplesPerByte]
shift := uint(8 - bitsPerSample - (i%samplesPerByte)*bitsPerSample)
bits := b >> shift & sampleBitsMask
f.samples[i] = bits << (8 - bitsPerSample)
}
// Scale AC coefficient down by fixed amount
dct[0] >>= fingerprintACShift

return nil
}
// Scale DC coefficients down according to dynamic range
for i := 0; i < len(dct); i++ {
if i != 0 {
dct[i] = int16(float64(dct[i]) * scale)
}

func (f *Fingerprint) UnmarshalText(text []byte) error {
hexBytes := make([]byte, hex.DecodedLen(len(text)))
_, err := hex.Decode(hexBytes, text)
if err != nil {
return err
if i > 0 && i%fingerprintDCTSideLength == 0 {
fmt.Println()
}
fmt.Printf(" %5d", dct[i])
}
fmt.Println()
fmt.Println()

return f.UnmarshalBytes(hexBytes)
return dctToFingerprint(dct)
}

func NewFingerprint(src image.Image, size int) Fingerprint {
scaled := image.NewNRGBA(image.Rectangle{Max: image.Point{X: size, Y: size}})
draw.BiLinear.Scale(scaled, scaled.Bounds(), src, src.Bounds(), draw.Src, nil)
func dctToFingerprint(squareMatrix []int16) (f *Fingerprint) {
f = &Fingerprint{}

fingerprintSamples := make([]uint8, size*size)
level := 0
offset := 0

for i := scaled.Bounds().Min.Y; i < scaled.Bounds().Max.Y; i++ {
for j := scaled.Bounds().Min.X; j < scaled.Bounds().Max.X; j++ {
r, g, b, _ := scaled.At(j, i).RGBA()
y, _, _ := color.RGBToYCbCr(uint8(r>>8), uint8(g>>8), uint8(b>>8))
for i := 0; i != SamplesPerFingerprint; {
if offset == level {

// Sample the last corner of the current square
f[i] = squareMatrix[level*fingerprintDCTSideLength+level]
i++

// Start the next larger square
offset = 0
level++

} else {

// Sample one from the right and one from the bottom
f[i] = squareMatrix[offset*fingerprintDCTSideLength+level]
i++
f[i] = squareMatrix[level*fingerprintDCTSideLength+offset]
i++

fingerprintSamples[offset] = y & (sampleBitsMask << (8 - bitsPerSample))
offset++
}
}

return Fingerprint{samples: fingerprintSamples}
return
}
Loading