/
descriptor.go
100 lines (78 loc) · 2.77 KB
/
descriptor.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
package pkg
import (
"fmt"
"gocv.io/x/gocv"
"image"
"image/color"
)
const (
hueBins = 12
saturationBins = 12
brightnessBins = 4
)
func FeatureVector(i Image) ([]float64, error) {
img := gocv.IMRead(i.Path, gocv.IMReadColor)
if img.Empty() {
return nil, fmt.Errorf("error reading image from %q\n", i.Path)
}
defer img.Close()
// convert img to hsv color space
img.ConvertTo(&img, gocv.ColorBGRToHSV)
black := color.RGBA{0, 0, 0, 0}
white := color.RGBA{255, 255, 255, 0}
width, height := img.Size()[1], img.Size()[0]
cx, cy := width/2, height/2
segments := [][]int{
{0, 0, cx, cy}, // top left
{cx, 0, width, cy}, // top right
{0, cy, cx, height}, // bottom left
{cx, cy, width, height}, // bottom right
}
axesX, axesY := int((float32(width)*0.75)/2), int((float32(height)*0.75)/2)
ellipMask := gocv.NewMatWithSize(height, width, gocv.MatTypeCV8UC1)
defer ellipMask.Close()
gocv.Ellipse(&ellipMask, image.Point{cx, cy}, image.Point{axesX, axesY}, 0, 0, 360, white, -1)
segmentMask := gocv.NewMatWithSize(height, width, gocv.MatTypeCV8UC1)
defer segmentMask.Close()
var features []float64
segmentHistogram := gocv.NewMat()
defer segmentHistogram.Close()
for _, segment := range segments {
// reset mask
gocv.Rectangle(&segmentMask, image.Rect(0, 0, width, height), black, -1)
// calculate intersection of current segment and elliptic mask
gocv.Rectangle(&segmentMask, image.Rect(segment[0], segment[1], segment[2], segment[3]), white, -1)
gocv.Subtract(segmentMask, ellipMask, &segmentMask)
// ShowMask(segmentMask)
segmentFeatures, err := featuresInSegment(img, segmentMask, segmentHistogram)
if err != nil {
return nil, err
}
features = append(features, segmentFeatures...)
}
// ShowMask(ellipMask)
ellipFeatures, err := featuresInSegment(img, ellipMask, segmentHistogram)
if err != nil {
return nil, err
}
features = append(features, ellipFeatures...)
return features, nil
}
func featuresInSegment(img gocv.Mat, mask gocv.Mat, hist gocv.Mat) ([]float64, error) {
// h, s, v channel
channels := []int{0, 1, 2}
bins := []int{hueBins, saturationBins, brightnessBins}
// hue range = 0-180, saturation range = 0-256, value/brightness range = 0-256
ranges := []float64{0, 180, 0, 256, 0, 256}
gocv.CalcHist([]gocv.Mat{img}, channels, mask, &hist, bins, ranges, false)
gocv.Normalize(hist, &hist, 1, 0, gocv.NormL2)
float64Hist := gocv.NewMat()
defer float64Hist.Close()
hist.ConvertTo(&float64Hist, gocv.MatTypeCV64F)
f, err := float64Hist.DataPtrFloat64()
// copy slice to golang memory, because after returning from this function the allocated memory is released.
// accessing the underlying array would result in undefined behaviour.
fCopy := make([]float64, len(f))
copy(fCopy, f)
return fCopy, err
}