@ -0,0 +1,6 @@ |
|||||
|
datasets |
||||
|
dataset |
||||
|
dataset_complet |
||||
|
*.jpg |
||||
|
*.jpeg |
||||
|
*.png |
@ -1,2 +1,10 @@ |
|||||
# galdric |
# galdric |
||||
machine learning server, for image classification |
machine learning server, for image classification |
||||
|
|
||||
|
|
||||
|
|
||||
|
- Reads all the datasets in the folder /dataset |
||||
|
- Each image is resized to the same size, configured in the config.json |
||||
|
- For the input images, calculates the euclidean distances |
||||
|
- Gets the nearest neighbour |
||||
|
- Show the result, that is the label of the object in the image |
@ -0,0 +1,57 @@ |
|||||
|
package main |
||||
|
|
||||
|
import "fmt" |
||||
|
|
||||
|
//Color struct, defines the color
|
||||
|
type Color struct{} |
||||
|
|
||||
|
var c Color |
||||
|
|
||||
|
//DarkGray color
|
||||
|
func (c Color) DarkGray(t string) { |
||||
|
fmt.Print("\x1b[30;1m") //dark gray
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
||||
|
|
||||
|
//Red color
|
||||
|
func (c Color) Red(t string) { |
||||
|
fmt.Print("\x1b[31;1m") //red
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
||||
|
|
||||
|
//Green color
|
||||
|
func (c Color) Green(t string) { |
||||
|
fmt.Print("\x1b[32;1m") //green
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
||||
|
|
||||
|
//Yellow color
|
||||
|
func (c Color) Yellow(t string) { |
||||
|
fmt.Print("\x1b[33;1m") //yellow
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
||||
|
|
||||
|
//Blue color
|
||||
|
func (c Color) Blue(t string) { |
||||
|
fmt.Print("\x1b[34;1m") //blue
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
||||
|
|
||||
|
//Purple color
|
||||
|
func (c Color) Purple(t string) { |
||||
|
fmt.Print("\x1b[35;1m") //purple
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
||||
|
|
||||
|
//Cyan color
|
||||
|
func (c Color) Cyan(t string) { |
||||
|
fmt.Print("\x1b[36;1m") //cyan
|
||||
|
fmt.Println(t) |
||||
|
fmt.Print("\x1b[0m") //defaultColor
|
||||
|
} |
@ -0,0 +1,4 @@ |
|||||
|
{ |
||||
|
"imgWidth": 300, |
||||
|
"imgHeigh": 300 |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
package main |
||||
|
|
||||
|
import "fmt" |
||||
|
|
||||
|
func check(err error) { |
||||
|
if err != nil { |
||||
|
fmt.Println(err) |
||||
|
} |
||||
|
} |
@ -0,0 +1,85 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"bytes" |
||||
|
"image" |
||||
|
"image/jpeg" |
||||
|
"image/png" |
||||
|
|
||||
|
"github.com/nfnt/resize" |
||||
|
) |
||||
|
|
||||
|
type imgRGBA [][]float64 |
||||
|
|
||||
|
func dataToImage(data []byte, imageName string) (image.Image, error) { |
||||
|
//var histogram imgRGBA
|
||||
|
reader := bytes.NewReader(data) |
||||
|
//var imageExtension = strings.Split(imageName, ".")[1]
|
||||
|
var img image.Image |
||||
|
var err error |
||||
|
/*switch imageExtension { |
||||
|
case "png": |
||||
|
img, err = png.Decode(reader) |
||||
|
case "jpg": |
||||
|
img, err = jpeg.Decode(reader) |
||||
|
case "jpeg": |
||||
|
img, err = jpeg.Decode(reader) |
||||
|
default: |
||||
|
img = nil |
||||
|
} |
||||
|
*/ |
||||
|
img, err = jpeg.Decode(reader) |
||||
|
if err != nil { |
||||
|
return img, err |
||||
|
} |
||||
|
return img, err |
||||
|
} |
||||
|
|
||||
|
func imageToData(img image.Image, imageName string) ([]byte, error) { |
||||
|
buf := new(bytes.Buffer) |
||||
|
//var imageExtension = strings.Split(imageName, ".")[1]
|
||||
|
var err error |
||||
|
/*switch imageExtension { |
||||
|
case "png": |
||||
|
err = png.Encode(buf, img) |
||||
|
case "jpg": |
||||
|
err = jpeg.Encode(buf, img, nil) |
||||
|
case "jpeg": |
||||
|
err = jpeg.Encode(buf, img, nil) |
||||
|
default: |
||||
|
img = nil |
||||
|
}*/ |
||||
|
err = jpeg.Encode(buf, img, nil) |
||||
|
if err != nil { |
||||
|
return buf.Bytes(), err |
||||
|
} |
||||
|
return buf.Bytes(), err |
||||
|
} |
||||
|
|
||||
|
func imageToPNG(img image.Image) ([]byte, error) { |
||||
|
buf := new(bytes.Buffer) |
||||
|
var err error |
||||
|
err = png.Encode(buf, img) |
||||
|
return buf.Bytes(), err |
||||
|
} |
||||
|
|
||||
|
func imageToHistogram(img image.Image) [][]float64 { |
||||
|
bounds := img.Bounds() |
||||
|
|
||||
|
//generate the histogram
|
||||
|
var histogram [][]float64 |
||||
|
for y := bounds.Min.Y; y < bounds.Max.Y; y++ { |
||||
|
for x := bounds.Min.X; x < bounds.Max.X; x++ { |
||||
|
r, g, b, a := img.At(x, y).RGBA() |
||||
|
var pixel []float64 |
||||
|
pixel = append(pixel, float64(r), float64(g), float64(b), float64(a)) |
||||
|
histogram = append(histogram, pixel) |
||||
|
} |
||||
|
} |
||||
|
return histogram |
||||
|
} |
||||
|
|
||||
|
func Resize(img image.Image) image.Image { |
||||
|
r := resize.Resize(uint(config.ImgWidth), uint(config.ImgHeigh), img, resize.Lanczos3) |
||||
|
return r |
||||
|
} |
@ -0,0 +1,30 @@ |
|||||
|
package main |
||||
|
|
||||
|
func euclideanDist(img1, img2 [][]float64) float64 { |
||||
|
var dist float64 |
||||
|
|
||||
|
for i := 0; i < len(img1); i++ { |
||||
|
for j := 0; j < len(img1[i]); j++ { |
||||
|
dist += (img1[i][j] - img2[i][j]) * (img1[i][j] - img2[i][j]) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return dist |
||||
|
} |
||||
|
|
||||
|
func knn(dataset Dataset, input [][]float64) string { |
||||
|
d := euclideanDist(dataset["Leopards"][0], input) |
||||
|
label := "lamp" |
||||
|
for k, v := range dataset { |
||||
|
//fmt.Println(k)
|
||||
|
for i := 0; i < len(v); i++ { |
||||
|
//fmt.Println(i)
|
||||
|
dNew := euclideanDist(v[i], input) |
||||
|
if dNew < d { |
||||
|
d = dNew |
||||
|
label = k |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return label |
||||
|
} |
@ -0,0 +1,31 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"strconv" |
||||
|
"time" |
||||
|
) |
||||
|
|
||||
|
func main() { |
||||
|
readConfig("./config.json") |
||||
|
|
||||
|
c.Cyan("reading images datasets") |
||||
|
tStart := time.Now() |
||||
|
dataset := readDataset("./dataset") |
||||
|
fmt.Print("time spend reading images: ") |
||||
|
fmt.Println(time.Since(tStart)) |
||||
|
fmt.Println("total folders scanned: " + strconv.Itoa(len(dataset))) |
||||
|
|
||||
|
numImages := 0 |
||||
|
for _, v := range dataset { |
||||
|
numImages = numImages + len(v) |
||||
|
} |
||||
|
c.Cyan("total images in dataset: " + strconv.Itoa(numImages)) |
||||
|
|
||||
|
//we have the images in the dataset variable
|
||||
|
//now, can take images
|
||||
|
testFile := readImage("./test.jpg") |
||||
|
r := knn(dataset, testFile) |
||||
|
fmt.Println("seems to be a " + r) |
||||
|
|
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"encoding/json" |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
) |
||||
|
|
||||
|
type Config struct { |
||||
|
ImgWidth int `json:"imgWidth"` |
||||
|
ImgHeigh int `json:"imgHeigh"` |
||||
|
} |
||||
|
|
||||
|
var config Config |
||||
|
|
||||
|
func readConfig(path string) { |
||||
|
file, err := ioutil.ReadFile(path) |
||||
|
if err != nil { |
||||
|
fmt.Println("error: ", err) |
||||
|
} |
||||
|
content := string(file) |
||||
|
json.Unmarshal([]byte(content), &config) |
||||
|
} |
@ -0,0 +1,74 @@ |
|||||
|
package main |
||||
|
|
||||
|
import ( |
||||
|
"fmt" |
||||
|
"io/ioutil" |
||||
|
"strconv" |
||||
|
) |
||||
|
|
||||
|
//each image is [][]float64, is a array of pixels
|
||||
|
type ImgDataset [][][]float64 |
||||
|
|
||||
|
type Dataset map[string]ImgDataset |
||||
|
|
||||
|
func byteArrayToFloat64Array(b []byte) []float64 { |
||||
|
var f []float64 |
||||
|
for i := 0; i < len(b); i++ { |
||||
|
val, _ := strconv.ParseFloat(string(b[i]), 64) |
||||
|
/*fmt.Print(string(b[i]) + "-") |
||||
|
fmt.Println(val)*/ |
||||
|
f = append(f, val) |
||||
|
} |
||||
|
return f |
||||
|
} |
||||
|
|
||||
|
func readImage(path string) [][]float64 { |
||||
|
//open image file
|
||||
|
/*reader, err := os.Open(path) |
||||
|
check(err) |
||||
|
defer reader.Close()*/ |
||||
|
|
||||
|
dat, err := ioutil.ReadFile(path) |
||||
|
check(err) |
||||
|
|
||||
|
imageRaw, err := dataToImage(dat, path) |
||||
|
check(err) |
||||
|
|
||||
|
//resize the image to standard size
|
||||
|
image := Resize(imageRaw) |
||||
|
|
||||
|
//convert the image to histogram(RGBA)
|
||||
|
histogram := imageToHistogram(image) |
||||
|
//convert image to bytes
|
||||
|
/*imgBytes, err := imageToData(image, path) |
||||
|
check(err)*/ |
||||
|
|
||||
|
//imgFloat := byteArrayToFloat64Array(imgBytes)
|
||||
|
return histogram |
||||
|
} |
||||
|
func readDataset(path string) map[string]ImgDataset { |
||||
|
//dataset := make(map[string]ImgDataset)
|
||||
|
dataset := make(Dataset) |
||||
|
|
||||
|
folders, _ := ioutil.ReadDir(path) |
||||
|
for _, folder := range folders { |
||||
|
fmt.Println(folder.Name()) |
||||
|
|
||||
|
var imgDataset ImgDataset |
||||
|
|
||||
|
folderFiles, _ := ioutil.ReadDir(path + "/" + folder.Name()) |
||||
|
for _, file := range folderFiles { |
||||
|
image := readImage(path + "/" + folder.Name() + "/" + file.Name()) |
||||
|
|
||||
|
imgDataset = append(imgDataset, image) |
||||
|
|
||||
|
/*fmt.Println(folder.Name()) |
||||
|
fmt.Println(file.Name())*/ |
||||
|
} |
||||
|
|
||||
|
//add the foldername to the Dataset map
|
||||
|
dataset[folder.Name()] = imgDataset |
||||
|
} |
||||
|
|
||||
|
return dataset |
||||
|
} |