agi.image.go 11 KB

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