agi.image.go 10 KB

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