Analyze blocks simmilarities and filter out identical ones

This commit is contained in:
Endre Simo
2018-04-16 11:37:31 +03:00
parent 72561d6bac
commit 6c68abbfa8

162
main.go
View File

@@ -14,28 +14,35 @@ import (
"time" "time"
) )
const BlockSize int = 4 const (
const MagnitudeThreshold = 20 BlockSize int = 4
const FrequencyThreshold = 40 MagnitudeThreshold = 0.5
SymmetryThreshold = 40
)
// pixel struct contains the discrete cosine transformation R,G,B,Y values.
type pixel struct { type pixel struct {
r, g, b, y float64 r, g, b, y float64
} }
// dctPx stores the DCT pixel values.
type dctPx [][]pixel type dctPx [][]pixel
// imageBlock contains the generated block upper left position and the stored image.
type imageBlock struct { type imageBlock struct {
x int x int
y int y int
img image.Image img image.Image
} }
// vector struct contains the neighboring blocks top left position and the shift vectors between them.
type vector struct { type vector struct {
xa, ya int xa, ya int
xb, yb int xb, yb int
offsetX, offsetY int offsetX, offsetY int
} }
// feature struct contains the feature blocks x, y position and their respective values.
type feature struct { type feature struct {
x int x int
y int y int
@@ -49,7 +56,7 @@ var (
) )
func main() { func main() {
input, err := os.Open("test.jpg") input, err := os.Open("test2.jpg")
defer input.Close() defer input.Close()
if err != nil { if err != nil {
@@ -91,7 +98,7 @@ func main() {
fmt.Printf("Error encoding image file: %v", err) fmt.Printf("Error encoding image file: %v", err)
} }
// Average Red, Green and Blue // Average RGB value.
var avr, avg, avb float64 var avr, avg, avb float64
for _, block := range blocks { for _, block := range blocks {
@@ -153,7 +160,7 @@ func main() {
features = append(features, feature{x: block.x, y: block.y, val: dctPixels[0][0].g}) features = append(features, feature{x: block.x, y: block.y, val: dctPixels[0][0].g})
features = append(features, feature{x: block.x, y: block.y, val: dctPixels[0][0].b}) features = append(features, feature{x: block.x, y: block.y, val: dctPixels[0][0].b})
// Append average red, green and blue values // Append average R,G,B values to the features vector(slice).
features = append(features, feature{x: block.x, y: block.y, val: avr}) features = append(features, feature{x: block.x, y: block.y, val: avr})
features = append(features, feature{x: block.x, y: block.y, val: avb}) features = append(features, feature{x: block.x, y: block.y, val: avb})
features = append(features, feature{x: block.x, y: block.y, val: avg}) features = append(features, feature{x: block.x, y: block.y, val: avg})
@@ -173,12 +180,12 @@ func main() {
res := checkForSimilarity(vectors) res := checkForSimilarity(vectors)
fmt.Println(res) fmt.Println(res)
//fmt.Println(features)
fmt.Printf("Features length: %d", len(features)) fmt.Printf("Features length: %d", len(features))
fmt.Printf("\nDone in: %.2fs\n", time.Since(start).Seconds()) fmt.Printf("\nDone in: %.2fs\n", time.Since(start).Seconds())
} }
//convertRGBImageToYUV coverts the image from RGB to YUV color space.
func convertRGBImageToYUV(img image.Image) image.Image { func convertRGBImageToYUV(img image.Image) image.Image {
bounds := img.Bounds() bounds := img.Bounds()
dx, dy := bounds.Max.X, bounds.Max.Y dx, dy := bounds.Max.X, bounds.Max.Y
@@ -194,6 +201,72 @@ func convertRGBImageToYUV(img image.Image) image.Image {
return yuvImage return yuvImage
} }
// analyze checks weather two neighboring blocks are considered almost identical.
func analyze(blockA, blockB feature) *vector {
// Compute the euclidean distance between two neighboring blocks.
dx := float64(blockA.x) - float64(blockB.x)
dy := float64(blockA.y) - float64(blockB.y)
dist := math.Sqrt(math.Pow(dx, 2) + math.Pow(dy, 2))
res := &vector{
xa: blockA.x,
ya: blockA.y,
xb: blockB.x,
yb: blockB.y,
offsetX: int(math.Abs(dx)),
offsetY: int(math.Abs(dy)),
}
if dist < MagnitudeThreshold {
return res
}
return nil
}
type offset struct {
x, y int
}
type newVector []vector
// checkForSimilarity analyze pair of candidate and check for
// similarity by computing the accumulative number of shift vectors.
func checkForSimilarity(vect []vector) newVector {
var identicalBlocks newVector
//For each pair of candidate compute the accumulative number of the corresponding shift vectors.
duplicates := make(map[offset]int)
for _, v := range vect {
// Check for duplicate blocks
offsetX := v.offsetX
offsetY := v.offsetY
offset := &offset{offsetX, offsetY}
_, exists := duplicates[*offset]
if exists {
duplicates[*offset]++
} else {
duplicates[*offset] = 1
}
// If the accumulative number of corresponding shift vectors is greater than
// a predefined threshold, the corresponding regions are marked as suspicious.
if duplicates[*offset] > SymmetryThreshold {
identicalBlocks = append(identicalBlocks, vector{
v.xa, v.xb, v.ya, v.yb, v.offsetX, v.offsetY,
})
}
}
return identicalBlocks
}
// TODO filter out neighboring blocks.
func filterOutNeighbors() {
}
// dct computes the Discrete Cosine Transform.
// https://en.wikipedia.org/wiki/Discrete_cosine_transform
func dct(x, y, u, v, w float64) float64 { func dct(x, y, u, v, w float64) float64 {
a := math.Cos(((2.0*x + 1) * (u * math.Pi)) / (2 * w)) a := math.Cos(((2.0*x + 1) * (u * math.Pi)) / (2 * w))
b := math.Cos(((2.0*y + 1) * (v * math.Pi)) / (2 * w)) b := math.Cos(((2.0*y + 1) * (v * math.Pi)) / (2 * w))
@@ -201,6 +274,7 @@ func dct(x, y, u, v, w float64) float64 {
return a * b return a * b
} }
// idct computes the Inverse Discrete Cosine Transform. (Only for testing purposes.)
func idct(u, v, x, y, w float64) float64 { func idct(u, v, x, y, w float64) float64 {
// normalization // normalization
alpha := func(a float64) float64 { alpha := func(a float64) float64 {
@@ -247,61 +321,17 @@ func max(x, y int) float64 {
return float64(y) return float64(y)
} }
// Check weather two neighboring blocks are considered almost identical. // unique returns slice's unique values.
func analyze(blockA, blockB feature) *vector { func unique(intSlice []int) []int {
// Compute the euclidean distance between two neighboring blocks. keys := make(map[int]bool)
dx := float64(blockA.x) - float64(blockB.x) list := []int{}
dy := float64(blockA.y) - float64(blockB.y) for _, entry := range intSlice {
dist := math.Sqrt(math.Pow(dx, 2) + math.Pow(dy, 2)) if _, value := keys[entry]; !value {
keys[entry] = true
res := &vector{ list = append(list, entry)
xa: blockA.x,
ya: blockA.y,
xb: blockB.x,
yb: blockB.y,
offsetX: int(dx),
offsetY: int(dy),
}
if dist < MagnitudeThreshold {
return res
}
return nil
}
type offset struct {
x, y int
}
type newVector []vector
// Analyze pair of candidate and check for similarity by computing the accumulative number of shift vectors.
func checkForSimilarity(vect []vector) newVector {
var identicalBlocks newVector
//For each pair of candidate compute the accumulative number of the corresponding shift vectors.
duplicates := make(map[offset]int)
for _, v := range vect {
// Check if the element exist in the duplicateItems map.
offsetX := v.offsetX
offsetY := v.offsetY
offset := &offset{offsetX, offsetY}
_, exists := duplicates[*offset]
if exists {
duplicates[*offset]++
} else {
duplicates[*offset] = 1
}
// If the accumulative number of corresponding shift vectors is greater than
// a predefined threshold, the corresponding regions are marked as suspicious.
if duplicates[*offset] > FrequencyThreshold {
identicalBlocks = append(identicalBlocks, vector{
v.xa, v.xb, v.ya, v.yb, v.offsetX, v.offsetY,
})
} }
} }
return identicalBlocks return list
} }
// Implement sorting function on feature vector // Implement sorting function on feature vector
@@ -318,15 +348,3 @@ func (a featVec) Less(i, j int) bool {
} }
return a[i].val < a[j].val return a[i].val < a[j].val
} }
func unique(intSlice []int) []int {
keys := make(map[int]bool)
list := []int{}
for _, entry := range intSlice {
if _, value := keys[entry]; !value {
keys[entry] = true
list = append(list, entry)
}
}
return list
}