Skip to content
Permalink
Browse files

Code cleanup and multipart/image processing

  • Loading branch information...
s32x committed Mar 28, 2019
1 parent e355739 commit a4c1816cff08a9ac617f220c2c9119af24d10af5
Showing with 59 additions and 44 deletions.
  1. +1 −1 Dockerfile
  2. +39 −6 classifier/classify.go
  3. +4 −28 service/classify.go
  4. +15 −9 service/demo.go
@@ -17,7 +17,7 @@ RUN go test ./...
RUN CGO_ENABLED=1 go build -o ./bin/server

# ================================ FINAL IMAGE ================================
FROM ubuntu:18.04
FROM ubuntu:latest

# Dependencies
RUN apt-get update -y && \
@@ -1,7 +1,12 @@
package classifier /* import "s32x.com/gamedetect/classifier" */

import (
"bytes"
"errors"
"image"
"image/png"
"io"
"mime/multipart"
"sort"
"strings"

@@ -16,16 +21,44 @@ type Prediction struct {
Probability float32 `json:"probability"`
}

// ClassifyImage classifies a passed images bytes Buffer and returns the
// predictions as a slice of Predictions
func (c *Classifier) ClassifyImage(filename string, img []byte) ([]Prediction, error) {
// Split out the filenames extension
fn := strings.Split(filename, ".")
// ClassifyImage takes an image Image, writes it to a bytes Buffer, performs a
// classification and returns a slice of predictions
func (c *Classifier) ClassifyImage(img image.Image) ([]Prediction, error) {
var buf bytes.Buffer
if err := png.Encode(&buf, img); err != nil {
return nil, err
}
return c.ClassifyBytes(buf.Bytes(), "png")
}

// ClassifyMultipart takes a multipart Fileheader, performs a classification
// and returns a slice of predictions
func (c *Classifier) ClassifyMultipart(fh *multipart.FileHeader) ([]Prediction, error) {
// Open the FileHeader and defer close it
file, err := fh.Open()
if err != nil {
return nil, err
}
defer file.Close()

// Read the bytes into a bytes buffer and return
var buf bytes.Buffer
if _, err := io.Copy(&buf, file); err != nil {
return nil, err
}

// Split out the filenames extension and return a full classification on
// the decoded bytes
fn := strings.Split(fh.Filename, ".")
if len(fn) < 2 {
return nil, errors.New("Invalid filename passed")
}
ext := fn[len(fn)-1]
return c.ClassifyBytes(buf.Bytes(), fn[len(fn)-1])
}

// ClassifyBytes classifies a passed images bytes and returns the predictions
// as a slice of Predictions
func (c *Classifier) ClassifyBytes(img []byte, ext string) ([]Prediction, error) {
// Create the scope and the input/output operations for normalization
s := op.NewScope()
in, out := c.NormalizeOutputs(s, ext)
@@ -1,8 +1,6 @@
package service /* import "s32x.com/gamedetect/service" */

import (
"bytes"
"io"
"net/http"
"time"

@@ -20,47 +18,25 @@ type Results struct {
// Classify is an echo Handler that processes an image and returns its
// predicted classifications
func (s *Service) Classify(c echo.Context) error {
// Decode the image from the request
filename, bytes, err := decodeFile(c, "image")
// Read the FileHeader from the request
fh, err := c.FormFile("image")
if err != nil {
return newISErr(c, err)
}

// Perform the classification and return the results
start := nowMS()
predictions, err := s.classifier.ClassifyImage(filename, bytes)
predictions, err := s.classifier.ClassifyMultipart(fh)
if err != nil {
return newISErr(c, err)
}
return c.JSON(http.StatusOK, &Results{
Filename: filename,
Filename: fh.Filename,
Predictions: predictions,
SpeedMS: nowMS() - start,
})
}

// decodeFile decodes a file from the passed echo Contexts form and returns
// both the files name and it's bytes
func decodeFile(c echo.Context, name string) (string, []byte, error) {
// Read the image from the request
fh, err := c.FormFile(name)
if err != nil {
return "", nil, err
}

// Open the image and defer close it
file, err := fh.Open()
if err != nil {
return "", nil, err
}
defer file.Close()

// Read the bytes into a bytes buffer and return
var buf bytes.Buffer
io.Copy(&buf, file)
return fh.Filename, buf.Bytes(), nil
}

// newISErr takes an error and encodes it in a map as a basic JSON response
func newISErr(c echo.Context, err error) error {
return c.JSON(http.StatusInternalServerError,
@@ -1,9 +1,9 @@
package service
package service /* import "s32x.com/gamedetect/service" */

import (
"errors"
"io"
"io/ioutil"
"log"
"net/http"
"os"
"path/filepath"
@@ -48,23 +48,25 @@ func Index(tr *TestResults) echo.HandlerFunc {
}
}

// Demo creates and returns a new echo Handler that performs demonstration
// game screenshot verifications
func Demo(tr *TestResults, classifier *classifier.Classifier) echo.HandlerFunc {
return func(c echo.Context) error {
// Decode the image from the request
filename, bytes, err := decodeFile(c, "image")
// Read the FileHeader from the request
fh, err := c.FormFile("image")
if err != nil {
return newISErr(c, err)
}

// Perform the classification and return the results
start := nowMS()
predictions, err := classifier.ClassifyImage(filename, bytes)
predictions, err := classifier.ClassifyMultipart(fh)
if err != nil {
return newISErr(c, err)
}
return c.Render(http.StatusOK, "index", map[string]interface{}{
"result": &Results{
Filename: filename,
Filename: fh.Filename,
Predictions: predictions,
SpeedMS: nowMS() - start,
},
@@ -84,8 +86,6 @@ func ProcessTestData(classifier *classifier.Classifier, testDir string) (*TestRe
return nil
}

log.Println("Performing test on file:", path)

// Get the expected value for the test data
expected := strings.Split(strings.Replace(path, testDir, "", -1),
string(os.PathSeparator))[1]
@@ -96,10 +96,16 @@ func ProcessTestData(classifier *classifier.Classifier, testDir string) (*TestRe
return err
}

// Split out the filenames extension
fn := strings.Split(filename, ".")
if len(fn) < 2 {
return errors.New("Invalid filename passed")
}

// Classify the image and calculate the speed of the classification in
// milliseconds
start := nowMS()
predictions, err := classifier.ClassifyImage(filename, bytes)
predictions, err := classifier.ClassifyBytes(bytes, fn[len(fn)-1])
if err != nil {
return err
}

0 comments on commit a4c1816

Please sign in to comment.
You can’t perform that action at this time.