agi.image.go 11 KB

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