diff --git a/hashcompute.go b/hashcompute.go index 8a14816..e1ac427 100644 --- a/hashcompute.go +++ b/hashcompute.go @@ -19,7 +19,7 @@ import ( // http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html func AverageHash(img image.Image) (*ImageHash, error) { if img == nil { - return nil, errors.New("Image object can not be nil") + return nil, errors.New("image object can not be nil") } // Create 64bits hash. @@ -43,7 +43,7 @@ func AverageHash(img image.Image) (*ImageHash, error) { // http://www.hackerfactor.com/blog/?/archives/529-Kind-of-Like-That.html func DifferenceHash(img image.Image) (*ImageHash, error) { if img == nil { - return nil, errors.New("Image object can not be nil") + return nil, errors.New("image object can not be nil") } dhash := NewImageHash(0, DHash) @@ -67,7 +67,7 @@ func DifferenceHash(img image.Image) (*ImageHash, error) { // http://www.hackerfactor.com/blog/index.php?/archives/432-Looks-Like-It.html func PerceptionHash(img image.Image) (*ImageHash, error) { if img == nil { - return nil, errors.New("Image object can not be nil") + return nil, errors.New("image object can not be nil") } phash := NewImageHash(0, PHash) @@ -76,12 +76,11 @@ func PerceptionHash(img image.Image) (*ImageHash, error) { pixels := pixelPool64.Get().(*[]float64) transforms.Rgb2GrayFast(resized, pixels) - transforms.DCT2DFast64(pixels) - flattens := transforms.FlattenPixelsFast64(*pixels, 8, 8) + flattens := transforms.DCT2DFast64(pixels) pixelPool64.Put(pixels) - median := etcs.MedianOfPixelsFast64(flattens) + median := etcs.MedianOfPixelsFast64(flattens[:]) for idx, p := range flattens { if p > median { @@ -106,7 +105,7 @@ var pixelPool64 = sync.Pool{ func ExtPerceptionHash(img image.Image, width, height int) (*ExtImageHash, error) { imgSize := width * height if img == nil { - return nil, errors.New("Image object can not be nil") + return nil, errors.New("image object can not be nil") } if imgSize <= 0 || imgSize&(imgSize-1) != 0 { return nil, errors.New("width * height should be power of 2") @@ -138,7 +137,7 @@ func ExtPerceptionHash(img image.Image, width, height int) (*ExtImageHash, error // Support 64bits ahash (width=8, height=8) and 256bits ahash (width=16, height=16) func ExtAverageHash(img image.Image, width, height int) (*ExtImageHash, error) { if img == nil { - return nil, errors.New("Image object can not be nil") + return nil, errors.New("image object can not be nil") } var ahash []uint64 imgSize := width * height @@ -168,7 +167,7 @@ func ExtAverageHash(img image.Image, width, height int) (*ExtImageHash, error) { // Support 64bits dhash (width=8, height=8) and 256bits dhash (width=16, height=16) func ExtDifferenceHash(img image.Image, width, height int) (*ExtImageHash, error) { if img == nil { - return nil, errors.New("Image object can not be nil") + return nil, errors.New("image object can not be nil") } var dhash []uint64 diff --git a/transforms/dct.go b/transforms/dct.go index 729500f..1cc9645 100644 --- a/transforms/dct.go +++ b/transforms/dct.go @@ -75,25 +75,49 @@ func DCT2D(input [][]float64, w int, h int) [][]float64 { } // DCT2DFast64 function returns a result of DCT2D by using the seperable property. -// Fast uses static DCT tables for improved performance. -func DCT2DFast64(input *[]float64) { - if len(*input) != 4096 { - panic("incorrect input size") +// Fast uses static DCT tables for improved performance. Returns flattened pixels. +func DCT2DFast64(input *[]float64) (flattens [64]float64) { + if len(*input) != 64*64 { + panic("incorrect input size, wanted 64x64.") } for i := 0; i < 64; i++ { // height - DCT1DFast64((*input)[i*64 : (i*64)+64]) - //forwardTransformFast((*input)[i*64:(i*64)+64], temp[:], 64) + forwardDCT64((*input)[i*64 : (i*64)+64]) } - for i := 0; i < 64; i++ { // width - row := [64]float64{} + var row [64]float64 + for i := 0; i < 8; i++ { // width for j := 0; j < 64; j++ { - row[j] = (*input)[i+((j)*64)] + row[j] = (*input)[64*j+i] } - DCT1DFast64(row[:]) - for j := 0; j < len(row); j++ { - (*input)[i+(j*64)] = row[j] + forwardDCT64(row[:]) + for j := 0; j < 8; j++ { + flattens[8*j+i] = row[j] } } + return +} + +// DCT2DFast256 function returns a result of DCT2D by using the seperable property. +// DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984. +// Fast uses static DCT tables for improved performance. Returns flattened pixels. +func DCT2DFast256(input *[]float64) (flattens [256]float64) { + if len(*input) != 256*256 { + panic("incorrect input size, wanted 256x256.") + } + for i := 0; i < 256; i++ { // height + forwardDCT256((*input)[i*256 : 256*i+256]) + } + + var row [256]float64 + for i := 0; i < 16; i++ { // width + for j := 0; j < 256; j++ { + row[j] = (*input)[256*j+i] + } + forwardDCT256(row[:]) + for j := 0; j < 16; j++ { + flattens[16*j+i] = row[j] + } + } + return flattens } diff --git a/transforms/static.go b/transforms/static.go index 15dc5a8..025dbac 100644 --- a/transforms/static.go +++ b/transforms/static.go @@ -1,19 +1,49 @@ package transforms -import "math" +func forwardDCT256(input []float64) { + var temp [256]float64 + for i := 0; i < 128; i++ { + x, y := input[i], input[256-1-i] + temp[i] = x + y + temp[i+128] = (x - y) / dct256[i] + } + forwardDCT128(temp[:128]) + forwardDCT128(temp[128:]) + for i := 0; i < 128-1; i++ { + input[i*2+0] = temp[i] + input[i*2+1] = temp[i+128] + temp[i+128+1] + } + input[256-2], input[256-1] = temp[128-1], temp[256-1] +} -// DCT1DFast64 function returns result of DCT-II. +func forwardDCT128(input []float64) { + var temp [128]float64 + for i := 0; i < 64; i++ { + x, y := input[i], input[128-1-i] + temp[i] = x + y + temp[i+64] = (x - y) / dct128[i] + } + forwardDCT64(temp[:64]) + forwardDCT64(temp[64:]) + for i := 0; i < 64-1; i++ { + input[i*2+0] = temp[i] + input[i*2+1] = temp[i+64] + temp[i+64+1] + } + input[128-2], input[128-1] = temp[64-1], temp[128-1] +} + +// forwardDCT64 function returns result of DCT-II. // DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984. // Static implementation by Evan Oberholster, 2022. -func DCT1DFast64(input []float64) { +func forwardDCT64(input []float64) { var temp [64]float64 for i := 0; i < 32; i++ { x, y := input[i], input[63-i] temp[i] = x + y temp[i+32] = (x - y) / dct64[i] } - forwardTransformStatic32(temp[:32]) - forwardTransformStatic32(temp[32:]) + forwardDCT32(temp[:32]) + forwardDCT32(temp[32:]) for i := 0; i < 32-1; i++ { input[i*2+0] = temp[i] input[i*2+1] = temp[i+32] + temp[i+32+1] @@ -21,15 +51,15 @@ func DCT1DFast64(input []float64) { input[62], input[63] = temp[31], temp[63] } -func forwardTransformStatic32(input []float64) { +func forwardDCT32(input []float64) { var temp [32]float64 for i := 0; i < 16; i++ { x, y := input[i], input[31-i] temp[i] = x + y temp[i+16] = (x - y) / dct32[i] } - forwardTransformStatic16(temp[:16]) - forwardTransformStatic16(temp[16:]) + forwardDCT16(temp[:16]) + forwardDCT16(temp[16:]) for i := 0; i < 16-1; i++ { input[i*2+0] = temp[i] input[i*2+1] = temp[i+16] + temp[i+16+1] @@ -38,15 +68,15 @@ func forwardTransformStatic32(input []float64) { input[30], input[31] = temp[15], temp[31] } -func forwardTransformStatic16(input []float64) { +func forwardDCT16(input []float64) { var temp [16]float64 for i := 0; i < 8; i++ { x, y := input[i], input[15-i] temp[i] = x + y temp[i+8] = (x - y) / dct16[i] } - forwardTransformStatic8(temp[:8]) - forwardTransformStatic8(temp[8:]) + forwardDCT8(temp[:8]) + forwardDCT8(temp[8:]) for i := 0; i < 8-1; i++ { input[i*2+0] = temp[i] input[i*2+1] = temp[i+8] + temp[i+8+1] @@ -55,54 +85,52 @@ func forwardTransformStatic16(input []float64) { input[14], input[15] = temp[7], temp[15] } -func forwardTransformStatic8(input []float64) { - var temp [8]float64 +func forwardDCT8(input []float64) { + var a, b = [4]float64{}, [4]float64{} + x0, y0 := input[0], input[7] x1, y1 := input[1], input[6] x2, y2 := input[2], input[5] x3, y3 := input[3], input[4] - temp[0] = x0 + y0 - temp[1] = x1 + y1 - temp[2] = x2 + y2 - temp[3] = x3 + y3 - temp[4] = (x0 - y0) / dct8[0] - temp[5] = (x1 - y1) / dct8[1] - temp[6] = (x2 - y2) / dct8[2] - temp[7] = (x3 - y3) / dct8[3] + a[0] = x0 + y0 + a[1] = x1 + y1 + a[2] = x2 + y2 + a[3] = x3 + y3 + b[0] = (x0 - y0) / 1.9615705608064609 + b[1] = (x1 - y1) / 1.6629392246050907 + b[2] = (x2 - y2) / 1.1111404660392046 + b[3] = (x3 - y3) / 0.3901806440322566 - forwardTransformStatic4(temp[:4]) - forwardTransformStatic4(temp[4:]) + forwardDCT4(a[:]) + forwardDCT4(b[:]) - input[0] = temp[0] - input[1] = temp[4] + temp[5] - input[2] = temp[1] - input[3] = temp[5] + temp[6] - input[4] = temp[2] - input[5] = temp[6] + temp[7] - input[6] = temp[3] - input[7] = temp[7] + input[0] = a[0] + input[1] = b[0] + b[1] + input[2] = a[1] + input[3] = b[1] + b[2] + input[4] = a[2] + input[5] = b[2] + b[3] + input[6] = a[3] + input[7] = b[3] } -func forwardTransformStatic4(input []float64) { - var ( - t0, t1, t2, t3 float64 - ) +func forwardDCT4(input []float64) { x0, y0 := input[0], input[3] x1, y1 := input[1], input[2] - t0 = x0 + y0 - t1 = x1 + y1 - t2 = (x0 - y0) / dct4[0] - t3 = (x1 - y1) / dct4[1] + t0 := x0 + y0 + t1 := x1 + y1 + t2 := (x0 - y0) / 1.8477590650225735 + t3 := (x1 - y1) / 0.7653668647301797 x, y := t0, t1 t0 += t1 - t1 = (x - y) / dct2[0] + t1 = (x - y) / 1.4142135623730951 x, y = t2, t3 t2 += t3 - t3 = (x - y) / dct2[0] + t3 = (x - y) / 1.4142135623730951 input[0] = t0 input[1] = t2 + t3 @@ -110,94 +138,48 @@ func forwardTransformStatic4(input []float64) { input[3] = t3 } -func init() { - // dct256 - for i := 0; i < 128; i++ { - dct256[i] = (math.Cos((float64(i)+0.5)*math.Pi/256) * 2) - } - // dct128 - for i := 0; i < 64; i++ { - dct128[i] = (math.Cos((float64(i)+0.5)*math.Pi/128) * 2) - } -} - // Static DCT Tables var ( - dct256 = [128]float64{} - dct128 = [64]float64{} - dct64 = [32]float64{ - (math.Cos((float64(0)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(1)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(2)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(3)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(4)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(5)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(6)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(7)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(8)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(9)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(10)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(11)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(12)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(13)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(14)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(15)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(16)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(17)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(18)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(19)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(20)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(21)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(22)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(23)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(24)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(25)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(26)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(27)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(28)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(29)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(30)+0.5)*math.Pi/64) * 2), - (math.Cos((float64(31)+0.5)*math.Pi/64) * 2), + //for i := 0; i < len(dct256); i++ { + // dct256[i] = (math.Cos((float64(i)+0.5)*math.Pi/float64(256)) * 2) + //} + dct256 = [128]float64{ + 1.9999623505652022, 1.9996611635916468, 1.9990588350021863, 1.9981554555052907, 1.9969511611465895, 1.9954461332883833, 1.9936405985823316, 1.9915348289353196, + 1.9891291414685108, 1.986423898469589, 1.983419507338199, 1.9801164205245942, 1.976515135461499, 1.9726161944891973, 1.968420184773858, 1.9639277382191105, + 1.9591395313708813, 1.9540562853155086, 1.9486787655711517, 1.9430077819725036, 1.9370441885488345, 1.9307888833953788, 1.9242428085380832, 1.917406949791743, + 1.9102823366115416, 1.9028700419380167, 1.8951711820354824, 1.8871869163239208, 1.8789184472043798, 1.8703670198778952, 1.8615339221579674, 1.8524204842766228, + 1.843028078684084, 1.8333581198420854, 1.8234120640108598, 1.8131914090298307, 1.802697694092044, 1.7919324995123704, 1.7808974464895158, 1.7695941968618756, + 1.758024452857267, 1.7461899568365802, 1.7340924910313853, 1.7217338772755346, 1.709115976730801, 1.6962406896065945, 1.6831099548737969, 1.6697257499727602, + 1.6560900905155114, 1.6422050299822093, 1.6280726594118968, 1.6136951070875987, 1.59907453821581, 1.5842131546004248, 1.5691131943111505, 1.5537769313464649, + 1.5382066752911592, 1.5224047709685236, 1.506373598087225, 1.490115570882932, 1.4736331377547398, 1.4569287808964504, 1.4400050159227635, 1.4228643914904329, + 1.4055094889144508, 1.387942921779308, 1.370167335545401, 1.352185407150632, 1.3339998446072752, 1.3156133865941575, 1.297028802044225, 1.2782488897275517, + 1.2592764778298542, 1.2401144235265784, 1.220765612552619, 1.201232958767738, 1.1815194037177486, 1.1616279161915293, 1.1415614917739347, 1.121323152394672, + 1.1009159458732098, 1.080342945459786, 1.0596072493725897, 1.0387119803311793, 1.0176602850862142, 0.9964553339455638, 0.9751003202968722, 0.9535984601266445, + 0.9319529915359323, 0.9101671742526877, 0.8882442891408585, 0.866187637706304, 0.8440005415995996, 0.8216863421158078, 0.7992483996912936, 0.7766900933976526, + 0.7540148204328366, 0.7312259956095479, 0.708327050840981, 0.6853214346239888, 0.6622126115197529, 0.6390040616320315, 0.61569928008307, 0.5923017764872479, + 0.5688150744225436, 0.545242710899898, 0.5215882358305511, 0.4978552114914405, 0.4740472119887347, 0.45016782271958555, 0.4262206398321827, 0.40220926968418386, + 0.37813732829961255, 0.3540084408242977, 0.3298262409799401, 0.3055943705168868, 0.2813164786656985, 0.25699622158758645, 0.23263726182380973, 0.20824326774410945, + 0.1838179129942654, 0.15936487594286025, 0.1348878391273282, 0.11039048869938006, 0.08587651386988192, 0.06134960635327317, 0.03681345981160964, 0.012271769298309032} + dct128 = [64]float64{ + 1.9998494036782892, 1.9986447691766989, 1.9962362258002984, 1.992625224365556, 1.9878139400047121, 1.98180527085556, 1.9746028363157169, 1.9662109748624328, + 1.9566347414392553, 1.9458799044111204, 1.9339529420897041, 1.9208610388311316, 1.9066120807083875, 1.8912146507610426, 1.87467802382515, 1.8570121609464312, + 1.8382277033801153, 1.818335966181045, 1.7973489313879076, 1.7752792408057079, 1.7521401883908132, 1.7279457122431734, 1.7027103862105304, 1.6764494111096762, + 1.6491786055700506, 1.6209143965051895, 1.5916738092177671, 1.561474457144189, 1.530334531244918, 1.4982727890469187, 1.4653085433448259, 1.4314616505676372, + 1.3967524988179458, 1.3612019955909063, 1.3248315551803436, 1.287663085779583, 1.249718976284773, 1.211022082808651, 1.1715957149128777, 1.1314636215672265, + 1.0906499768440931, 1.049179365356938, 1.0070767674514352, 0.9643675441582458, 0.92107742191648, 0.8772324770770554, 0.8328591201952746, 0.7879840801220962, + 0.7426343879036752, 0.696837360498869, 0.650620584324526, 0.6040118986384564, 0.5570393787701061, 0.5097313192090293, 0.4621162165613425, 0.4142227523844371, + 0.36607977591028207, 0.3177162866677228, 0.26916141701425245, 0.22044441458776637, 0.17159462468887976, 0.12264147260441731, 0.07361444588271798, 0.02454307657143989} + dct64 = [32]float64{ + 1.9993976373924083, 1.9945809133573804, 1.9849590691974202, 1.9705552847778824, 1.9514042600770571, 1.9275521315908797, 1.8990563611860733, 1.8659855976694777, + 1.8284195114070614, 1.7864486023910306, 1.7401739822174227, 1.6897071304994142, 1.6351696263031674, 1.5766928552532127, 1.5144176930129691, 1.448494165902934, + 1.3790810894741339, 1.3063456859075537, 1.2304631811612539, 1.151616382835691, 1.0699952397741948, 0.9857963844595683, 0.8992226593092132, 0.8104826280099796, + 0.7197900730699766, 0.627363480797783, 0.5334255149497968, 0.43820248031373954, 0.3419237775206027, 0.24482135039843256, 0.1471291271993349, 0.049082457045824535, } dct32 = [16]float64{ - (math.Cos((float64(0)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(1)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(2)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(3)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(4)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(5)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(6)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(7)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(8)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(9)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(10)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(11)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(12)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(13)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(14)+0.5)*math.Pi/32) * 2), - (math.Cos((float64(15)+0.5)*math.Pi/32) * 2), + 1.9975909124103448, 1.978353019929562, 1.9400625063890882, 1.8830881303660416, 1.8079785862468867, 1.7154572200005442, 1.6064150629612899, 1.4819022507099182, + 1.3431179096940369, 1.191398608984867, 1.0282054883864435, 0.8551101868605644, 0.6737797067844401, 0.48596035980652796, 0.2934609489107235, 0.09813534865483627, } dct16 = [8]float64{ - (math.Cos((float64(0)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(1)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(2)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(3)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(4)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(5)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(6)+0.5)*math.Pi/16) * 2), - (math.Cos((float64(7)+0.5)*math.Pi/16) * 2), - } - dct8 = [4]float64{ - (math.Cos((float64(0)+0.5)*math.Pi/8) * 2), - (math.Cos((float64(1)+0.5)*math.Pi/8) * 2), - (math.Cos((float64(2)+0.5)*math.Pi/8) * 2), - (math.Cos((float64(3)+0.5)*math.Pi/8) * 2), - } - dct4 = [2]float64{ - (math.Cos((float64(0)+0.5)*math.Pi/4) * 2), - (math.Cos((float64(1)+0.5)*math.Pi/4) * 2), - } - dct2 = [1]float64{ - (math.Cos((float64(0)+0.5)*math.Pi/2) * 2), + 1.9903694533443936, 1.9138806714644176, 1.76384252869671, 1.546020906725474, 1.2687865683272912, 0.9427934736519956, 0.5805693545089246, 0.19603428065912154, } )