neuralnet.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package neuralnet
  2. import (
  3. "errors"
  4. "log"
  5. "os/exec"
  6. "path/filepath"
  7. "runtime"
  8. "strconv"
  9. "strings"
  10. "imuslab.com/arozos/mod/filesystem"
  11. )
  12. /*
  13. Neural Net Package
  14. Require darknet binary in system folder to work
  15. */
  16. type ImageClass struct {
  17. Name string
  18. Percentage float64
  19. Positions []int
  20. }
  21. func getDarknetBinary() (string, error) {
  22. darknetRoot := "./system/neuralnet/"
  23. binaryName := "darknet_" + runtime.GOOS + "_" + runtime.GOARCH
  24. if runtime.GOOS == "windows" {
  25. binaryName += ".exe"
  26. }
  27. expectedDarknetBinary := filepath.Join(darknetRoot, binaryName)
  28. absPath, _ := filepath.Abs(expectedDarknetBinary)
  29. if !filesystem.FileExists(absPath) {
  30. return "", errors.New("Darknet executable not found on " + absPath)
  31. }
  32. return absPath, nil
  33. }
  34. //Analysis and get what is inside the image using Darknet19, fast but only support 1 main object
  35. func AnalysisPhotoDarknet19(filename string) ([]*ImageClass, error) {
  36. results := []*ImageClass{}
  37. //Check darknet installed
  38. darknetBinary, err := getDarknetBinary()
  39. if err != nil {
  40. return results, err
  41. }
  42. //Check source image exists
  43. imageSourceAbs, err := filepath.Abs(filename)
  44. if !filesystem.FileExists(imageSourceAbs) || err != nil {
  45. return results, errors.New("Source file not found")
  46. }
  47. //Analysis the image
  48. cmd := exec.Command(darknetBinary, "classifier", "predict", "cfg/imagenet1k.data", "cfg/darknet19.cfg", "darknet19.weights", imageSourceAbs)
  49. cmd.Dir = filepath.Dir(darknetBinary)
  50. out, err := cmd.CombinedOutput()
  51. if err != nil {
  52. return results, err
  53. }
  54. //Process the output text
  55. lines := strings.Split(string(out), "\n")
  56. for _, line := range lines {
  57. if strings.Contains(line, "%:") {
  58. //This is a resulting line. Split and push it into results
  59. info := strings.Split(strings.TrimSpace(line), "%: ") //[0] => Percentage in string, [1] => tag
  60. if s, err := strconv.ParseFloat(info[0], 32); err == nil {
  61. thisClassification := ImageClass{
  62. Name: info[1],
  63. Percentage: s,
  64. Positions: []int{},
  65. }
  66. results = append(results, &thisClassification)
  67. }
  68. }
  69. }
  70. return results, nil
  71. }
  72. //Analysis what is in the image using YOLO3, very slow but support multiple objects
  73. func AnalysisPhotoYOLO3(filename string) ([]*ImageClass, error) {
  74. results := []*ImageClass{}
  75. //Check darknet installed
  76. darknetBinary, err := getDarknetBinary()
  77. if err != nil {
  78. return results, err
  79. }
  80. //Check source image exists
  81. imageSourceAbs, err := filepath.Abs(filename)
  82. if !filesystem.FileExists(imageSourceAbs) || err != nil {
  83. return results, errors.New("Source file not found")
  84. }
  85. //Check if there is yolov3.weight. If not, use yolo3_tiny.weight
  86. pretrainWeight := "yolov3.weights"
  87. networkConf := "cfg/yolov3.cfg"
  88. if !filesystem.FileExists(filepath.Join(filepath.Dir(darknetBinary), "yolov3.weight")) {
  89. //yolo3 weight not exists. Switch to yolov3_tiny (default, recommended)
  90. log.Println("[neuralnet] yolo3.weight not found. Using yolo3-tiny.weight instead")
  91. pretrainWeight = "yolov3-tiny.weights"
  92. networkConf = "cfg/yolov3-tiny.cfg"
  93. }
  94. //Analysis the image
  95. cmd := exec.Command(darknetBinary, "detect", networkConf, pretrainWeight, imageSourceAbs, "-out")
  96. cmd.Dir = filepath.Dir(darknetBinary)
  97. out, err := cmd.CombinedOutput()
  98. if err != nil {
  99. return results, err
  100. }
  101. lines := strings.Split(string(out), "\n")
  102. var previousClassificationObject *ImageClass = nil
  103. for _, line := range lines {
  104. line = strings.TrimSpace(line)
  105. if len(line) > 0 && line[len(line)-1:] == "%" && strings.Contains(line, ":") {
  106. //This is a output value
  107. //Trim out the %
  108. line = line[:len(line)-1]
  109. info := strings.Split(line, ":") //[0] => class name, [1] => percentage
  110. if s, err := strconv.ParseFloat(strings.TrimSpace(info[1]), 32); err == nil {
  111. thisClassification := ImageClass{
  112. Name: info[0],
  113. Percentage: s,
  114. }
  115. previousClassificationObject = &thisClassification
  116. results = append(results, &thisClassification)
  117. }
  118. } else if len(line) > 0 && line[:4] == "pos=" && strings.Contains(line, ",") && previousClassificationObject != nil {
  119. //This is position makeup data, append to previous classification
  120. positionsString := strings.Split(line[4:], ",")
  121. positionsInt := []int{}
  122. for _, pos := range positionsString {
  123. posInt, err := strconv.Atoi(pos)
  124. if err != nil {
  125. positionsInt = append(positionsInt, -1)
  126. } else {
  127. positionsInt = append(positionsInt, posInt)
  128. }
  129. }
  130. previousClassificationObject.Positions = positionsInt
  131. }
  132. }
  133. return results, nil
  134. }