diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..81a4214 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +datasets +dataset +dataset_complet +*.jpg +*.jpeg +*.png diff --git a/README.md b/README.md index 56efa9c..32b3423 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,10 @@ # galdric 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 diff --git a/color.go b/color.go new file mode 100644 index 0000000..e945ac5 --- /dev/null +++ b/color.go @@ -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 +} diff --git a/config.json b/config.json new file mode 100644 index 0000000..82c99c9 --- /dev/null +++ b/config.json @@ -0,0 +1,4 @@ +{ + "imgWidth": 300, + "imgHeigh": 300 +} diff --git a/errors.go b/errors.go new file mode 100644 index 0000000..9e83a92 --- /dev/null +++ b/errors.go @@ -0,0 +1,9 @@ +package main + +import "fmt" + +func check(err error) { + if err != nil { + fmt.Println(err) + } +} diff --git a/imageOperations.go b/imageOperations.go new file mode 100644 index 0000000..9793fd1 --- /dev/null +++ b/imageOperations.go @@ -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 +} diff --git a/knn.go b/knn.go new file mode 100644 index 0000000..c030dc6 --- /dev/null +++ b/knn.go @@ -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 +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..aa9ea85 --- /dev/null +++ b/main.go @@ -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) + +} diff --git a/readConfig.go b/readConfig.go new file mode 100644 index 0000000..d2d634b --- /dev/null +++ b/readConfig.go @@ -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) +} diff --git a/readDataset.go b/readDataset.go new file mode 100644 index 0000000..2fc2339 --- /dev/null +++ b/readDataset.go @@ -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 +}