agi.image.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. package agi
  2. import (
  3. "bytes"
  4. "errors"
  5. "fmt"
  6. "image"
  7. "image/jpeg"
  8. _ "image/jpeg"
  9. "image/png"
  10. _ "image/png"
  11. "log"
  12. "os"
  13. "path/filepath"
  14. "strings"
  15. "github.com/disintegration/imaging"
  16. "github.com/oliamb/cutter"
  17. "github.com/robertkrimen/otto"
  18. "imuslab.com/arozos/mod/neuralnet"
  19. user "imuslab.com/arozos/mod/user"
  20. )
  21. /*
  22. AJGI Image Processing Library
  23. This is a library for handling image related functionalities in agi scripts.
  24. */
  25. func (g *Gateway) ImageLibRegister() {
  26. err := g.RegisterLib("imagelib", g.injectImageLibFunctions)
  27. if err != nil {
  28. log.Fatal(err)
  29. }
  30. }
  31. func (g *Gateway) injectImageLibFunctions(vm *otto.Otto, u *user.User) {
  32. //Get image dimension, requires filepath (virtual)
  33. vm.Set("_imagelib_getImageDimension", func(call otto.FunctionCall) otto.Value {
  34. imageFileVpath, err := call.Argument(0).ToString()
  35. if err != nil {
  36. g.raiseError(err)
  37. return otto.FalseValue()
  38. }
  39. fsh, imagePath, err := virtualPathToRealPath(imageFileVpath, u)
  40. if err != nil {
  41. g.raiseError(err)
  42. return otto.FalseValue()
  43. }
  44. if !fsh.FileSystemAbstraction.FileExists(imagePath) {
  45. g.raiseError(errors.New("File not exists! Given " + imagePath))
  46. return otto.FalseValue()
  47. }
  48. openingPath := imagePath
  49. var closerFunc func()
  50. if fsh.RequireBuffer {
  51. bufferPath, cf := g.getUserSpecificTempFilePath(u, imagePath)
  52. closerFunc = cf
  53. defer closerFunc()
  54. c, err := fsh.FileSystemAbstraction.ReadFile(imagePath)
  55. if err != nil {
  56. g.raiseError(errors.New("Read from file system failed: " + err.Error()))
  57. return otto.FalseValue()
  58. }
  59. os.WriteFile(bufferPath, c, 0775)
  60. openingPath = bufferPath
  61. }
  62. file, err := os.Open(openingPath)
  63. if err != nil {
  64. g.raiseError(err)
  65. return otto.FalseValue()
  66. }
  67. image, _, err := image.DecodeConfig(file)
  68. if err != nil {
  69. g.raiseError(err)
  70. return otto.FalseValue()
  71. }
  72. file.Close()
  73. rawResults := []int{image.Width, image.Height}
  74. result, _ := vm.ToValue(rawResults)
  75. return result
  76. })
  77. //Resize image, require (filepath, outputpath, width, height)
  78. vm.Set("_imagelib_resizeImage", func(call otto.FunctionCall) otto.Value {
  79. vsrc, err := call.Argument(0).ToString()
  80. if err != nil {
  81. g.raiseError(err)
  82. return otto.FalseValue()
  83. }
  84. vdest, err := call.Argument(1).ToString()
  85. if err != nil {
  86. g.raiseError(err)
  87. return otto.FalseValue()
  88. }
  89. width, err := call.Argument(2).ToInteger()
  90. if err != nil {
  91. g.raiseError(err)
  92. return otto.FalseValue()
  93. }
  94. height, err := call.Argument(3).ToInteger()
  95. if err != nil {
  96. g.raiseError(err)
  97. return otto.FalseValue()
  98. }
  99. //Convert the virtual paths to real paths
  100. srcfsh, rsrc, err := virtualPathToRealPath(vsrc, u)
  101. if err != nil {
  102. g.raiseError(err)
  103. return otto.FalseValue()
  104. }
  105. destfsh, rdest, err := virtualPathToRealPath(vdest, u)
  106. if err != nil {
  107. g.raiseError(err)
  108. return otto.FalseValue()
  109. }
  110. ext := strings.ToLower(filepath.Ext(rdest))
  111. if !inArray([]string{".jpg", ".jpeg", ".png"}, ext) {
  112. g.raiseError(errors.New("File extension not supported. Only support .jpg and .png"))
  113. return otto.FalseValue()
  114. }
  115. if destfsh.FileSystemAbstraction.FileExists(rdest) {
  116. err := destfsh.FileSystemAbstraction.Remove(rdest)
  117. if err != nil {
  118. g.raiseError(err)
  119. return otto.FalseValue()
  120. }
  121. }
  122. resizeOpeningFile := rsrc
  123. resizeWritingFile := rdest
  124. var srcCloser func()
  125. var destCloser func()
  126. if srcfsh.RequireBuffer {
  127. resizeOpeningFile, srcCloser, err = g.bufferRemoteResourcesToLocal(srcfsh, u, rsrc)
  128. if err != nil {
  129. g.raiseError(err)
  130. return otto.FalseValue()
  131. }
  132. defer srcCloser()
  133. }
  134. if destfsh.RequireBuffer {
  135. resizeWritingFile, destCloser, err = g.bufferRemoteResourcesToLocal(destfsh, u, rdest)
  136. if err != nil {
  137. g.raiseError(err)
  138. return otto.FalseValue()
  139. }
  140. defer destCloser()
  141. }
  142. //Resize the image
  143. src, err := imaging.Open(resizeOpeningFile)
  144. if err != nil {
  145. //Opening failed
  146. g.raiseError(err)
  147. return otto.FalseValue()
  148. }
  149. src = imaging.Resize(src, int(width), int(height), imaging.Lanczos)
  150. err = imaging.Save(src, resizeWritingFile)
  151. if err != nil {
  152. g.raiseError(err)
  153. return otto.FalseValue()
  154. }
  155. if destfsh.RequireBuffer {
  156. c, _ := os.ReadFile(resizeWritingFile)
  157. destfsh.FileSystemAbstraction.WriteFile(rdest, c, 0775)
  158. }
  159. return otto.TrueValue()
  160. })
  161. //Crop the given image, require (input, output, posx, posy, width, height)
  162. vm.Set("_imagelib_cropImage", func(call otto.FunctionCall) otto.Value {
  163. vsrc, err := call.Argument(0).ToString()
  164. if err != nil {
  165. g.raiseError(err)
  166. return otto.FalseValue()
  167. }
  168. vdest, err := call.Argument(1).ToString()
  169. if err != nil {
  170. g.raiseError(err)
  171. return otto.FalseValue()
  172. }
  173. posx, err := call.Argument(2).ToInteger()
  174. if err != nil {
  175. posx = 0
  176. }
  177. posy, err := call.Argument(3).ToInteger()
  178. if err != nil {
  179. posy = 0
  180. }
  181. width, err := call.Argument(4).ToInteger()
  182. if err != nil {
  183. g.raiseError(errors.New("Image width not defined"))
  184. return otto.FalseValue()
  185. }
  186. height, err := call.Argument(5).ToInteger()
  187. if err != nil {
  188. g.raiseError(errors.New("Image height not defined"))
  189. return otto.FalseValue()
  190. }
  191. //Convert the virtual paths to realpaths
  192. srcFsh, rsrc, err := virtualPathToRealPath(vsrc, u)
  193. if err != nil {
  194. g.raiseError(err)
  195. return otto.FalseValue()
  196. }
  197. srcFshAbs := srcFsh.FileSystemAbstraction
  198. destFsh, rdest, err := virtualPathToRealPath(vdest, u)
  199. if err != nil {
  200. g.raiseError(err)
  201. return otto.FalseValue()
  202. }
  203. destWritePath := rdest
  204. var destCloserFunction func()
  205. if destFsh.RequireBuffer {
  206. destWritePath, destCloserFunction = g.getUserSpecificTempFilePath(u, rdest)
  207. if err != nil {
  208. g.raiseError(err)
  209. return otto.FalseValue()
  210. }
  211. defer destCloserFunction()
  212. }
  213. //Try to read the source image
  214. imageBytes, err := srcFshAbs.ReadFile(rsrc)
  215. if err != nil {
  216. fmt.Println(err)
  217. g.raiseError(err)
  218. return otto.FalseValue()
  219. }
  220. img, _, err := image.Decode(bytes.NewReader(imageBytes))
  221. if err != nil {
  222. g.raiseError(err)
  223. return otto.FalseValue()
  224. }
  225. //Crop the image
  226. croppedImg, _ := cutter.Crop(img, cutter.Config{
  227. Width: int(width),
  228. Height: int(height),
  229. Anchor: image.Point{int(posx), int(posy)},
  230. Mode: cutter.TopLeft,
  231. })
  232. //Create the new image
  233. out, err := os.Create(destWritePath)
  234. if err != nil {
  235. g.raiseError(err)
  236. return otto.FalseValue()
  237. }
  238. if strings.ToLower(filepath.Ext(destWritePath)) == ".png" {
  239. png.Encode(out, croppedImg)
  240. } else if strings.ToLower(filepath.Ext(destWritePath)) == ".jpg" {
  241. jpeg.Encode(out, croppedImg, nil)
  242. } else {
  243. g.raiseError(errors.New("Not supported format: Only support jpg or png"))
  244. return otto.FalseValue()
  245. }
  246. out.Close()
  247. if destFsh.RequireBuffer {
  248. c, _ := os.ReadFile(destWritePath)
  249. err := destFsh.FileSystemAbstraction.WriteFile(rdest, c, 0775)
  250. if err != nil {
  251. fmt.Println(">", err.Error())
  252. }
  253. }
  254. return otto.TrueValue()
  255. })
  256. //Get the given file's thumbnail in base64
  257. vm.Set("_imagelib_loadThumbString", func(call otto.FunctionCall) otto.Value {
  258. vsrc, err := call.Argument(0).ToString()
  259. if err != nil {
  260. g.raiseError(err)
  261. return otto.FalseValue()
  262. }
  263. fsh, err := u.GetFileSystemHandlerFromVirtualPath(vsrc)
  264. if err != nil {
  265. g.raiseError(err)
  266. return otto.FalseValue()
  267. }
  268. rpath, _ := fsh.FileSystemAbstraction.VirtualPathToRealPath(vsrc, u.Username)
  269. //Get the files' thumb base64 string
  270. base64String, err := g.Option.FileSystemRender.LoadCache(fsh, rpath, false)
  271. if err != nil {
  272. return otto.FalseValue()
  273. } else {
  274. value, _ := vm.ToValue(base64String)
  275. return value
  276. }
  277. })
  278. vm.Set("_imagelib_classify", func(call otto.FunctionCall) otto.Value {
  279. vsrc, err := call.Argument(0).ToString()
  280. if err != nil {
  281. g.raiseError(err)
  282. return otto.FalseValue()
  283. }
  284. classifier, err := call.Argument(1).ToString()
  285. if err != nil {
  286. classifier = "default"
  287. }
  288. if classifier == "" || classifier == "undefined" {
  289. classifier = "default"
  290. }
  291. //Convert the vsrc to real path
  292. fsh, rsrc, err := virtualPathToRealPath(vsrc, u)
  293. if err != nil {
  294. g.raiseError(err)
  295. return otto.FalseValue()
  296. }
  297. analysisSrc := rsrc
  298. var closerFunc func()
  299. if fsh.RequireBuffer {
  300. analysisSrc, closerFunc, err = g.bufferRemoteResourcesToLocal(fsh, u, rsrc)
  301. if err != nil {
  302. g.raiseError(err)
  303. return otto.FalseValue()
  304. }
  305. defer closerFunc()
  306. }
  307. if classifier == "default" || classifier == "darknet19" {
  308. //Use darknet19 for classification
  309. r, err := neuralnet.AnalysisPhotoDarknet19(analysisSrc)
  310. if err != nil {
  311. g.raiseError(err)
  312. return otto.FalseValue()
  313. }
  314. result, err := vm.ToValue(r)
  315. if err != nil {
  316. g.raiseError(err)
  317. return otto.FalseValue()
  318. }
  319. return result
  320. } else if classifier == "yolo3" {
  321. //Use yolo3 for classification, return positions of object as well
  322. r, err := neuralnet.AnalysisPhotoYOLO3(analysisSrc)
  323. if err != nil {
  324. g.raiseError(err)
  325. return otto.FalseValue()
  326. }
  327. result, err := vm.ToValue(r)
  328. if err != nil {
  329. g.raiseError(err)
  330. return otto.FalseValue()
  331. }
  332. return result
  333. } else {
  334. //Unsupported classifier
  335. log.Println("[AGI] Unsupported image classifier name: " + classifier)
  336. g.raiseError(err)
  337. return otto.FalseValue()
  338. }
  339. })
  340. //Wrap all the native code function into an imagelib class
  341. vm.Run(`
  342. var imagelib = {};
  343. imagelib.getImageDimension = _imagelib_getImageDimension;
  344. imagelib.resizeImage = _imagelib_resizeImage;
  345. imagelib.cropImage = _imagelib_cropImage;
  346. imagelib.loadThumbString = _imagelib_loadThumbString;
  347. imagelib.classify = _imagelib_classify;
  348. `)
  349. }