1. --!movie
  2. --!encoding=utf-8
  3.  
  4. global $
  5.  
  6. global emotionApiKey
  7. global overlayInfoColor
  8.  
  9. global txt
  10. global bmp
  11. global imgOverlay
  12.  
  13. ----------------------------------------
  14. --
  15. ----------------------------------------
  16. on startMovie
  17.  
  18.   -- CONFIG ----------------------------
  19.  
  20.   -- this is a private key - please use your own key for your own projects!
  21.   emotionApiKey = "4ed9846aac5247d291d73d990fda6141"
  22.  
  23.   -- color used for face marker rects and emotion scores
  24.   overlayInfoColor = rgb(0,255,0)
  25.  
  26.   -- /CONFIG ---------------------------
  27.  
  28.   -- import libs
  29.   $.import("curl")
  30.   $.import("drop")
  31.   $.import("file")
  32.   $.import("json")
  33.  
  34.   -- window settings
  35.   w = 800
  36.   h = 600
  37.   _movie.stage.title = "Drop a JPG or PNG image file into the window"
  38.   _movie.stage.titlebarOptions.visible = TRUE
  39.   _movie.stage.rect = rect(0,0,w,h)
  40.   _movie.centerStage = 1
  41.   _movie.stage.bgColor = rgb(0,0,0)
  42.  
  43.   -- create text member for rendering emotion scores
  44.   txt = new(#text)
  45.   if $.OS="win" then
  46.     txt.font = "MS Sans Serif"
  47.   else
  48.     txt.font = "Arial"
  49.   end if
  50.   txt.fontSize = 10
  51.   txt.fixedLineSpace = 10
  52.  
  53.   -- create bitmap member for displaying the dropped image
  54.   bmp = new(#bitmap)
  55.  
  56.   -- create sprite, assign dropped image bitmap member
  57.   sprite(1).puppet = 1
  58.   sprite(1).member = bmp
  59.   sprite(1).loc = point(0,0)
  60.  
  61.   -- create bitmap member for overlay infos
  62.   m = new(#bitmap)
  63.   m.image = image(w,h,32)
  64.   m.image.fill(m.image.rect, rgb(255,255,255))
  65.   m.regPoint = point(0,0)
  66.   imgOverlay = m.image
  67.  
  68.   -- create sprite, assign overlay bitmap member
  69.   sprite(2).puppet = 1
  70.   sprite(2).member = m
  71.   sprite(2).loc = point(0,0)
  72.   sprite(2).ink = 36
  73.   sprite(2).color = overlayInfoColor
  74.  
  75.   -- force immediate update
  76.   _movie.updateStage()
  77.  
  78.   -- show the window
  79.   _movie.stage.visible = 1
  80.  
  81.   -- start drop support
  82.   $.drop.setCallback(#filesDropped)
  83.   $.drop.start()
  84.  end
  85.  
  86. ----------------------------------------
  87. -- @callback
  88. ----------------------------------------
  89. on filesDropped (tItems)
  90.   if tItems.files.count<1 then return -- no file was dropped
  91.   ok = bmp.importFileInto(tItems.files[1])
  92.   if not ok then return -- the first dropped file was not a JPG/PNG image
  93.  
  94.   -- show the dropped image with correct ratio
  95.   sprite(1).rect = findMaxRect(_movie.stage.image.rect, float(bmp.width)/bmp.height)
  96.  
  97.   -- clear previous overlay infos
  98.   imgOverlay.fill(imgOverlay.rect, rgb(255,255,255))
  99.  
  100.   -- force immediate update
  101.   _movie.updateStage()
  102.  
  103.   -- call Project Oxford Emotion Analysis API
  104.   _player.cursor(4)
  105.   res = getEmotion(tItems.files[1])
  106.   _player.cursor(0)
  107.   if integerP(res) then return _player.alert("Error:" && curl_error(res))
  108.   info = $.json.decode(res)
  109.   if ilk(info)=#propList then return _player.alert(info["error"]["message"])
  110.   cnt = info.count
  111.   if cnt=0 then return _player.alert("No faces were detected in this image")
  112.  
  113.   -- show the result in the overlay layer
  114.   img = bmp.image
  115.   props = [:]
  116.   props[#shapeType] = #rect
  117.   props[#lineSize] = 1
  118.   props[#color] = rgb(0,0,0)
  119.   repeat with i = 1 to cnt
  120.     fr = info[i]["faceRectangle"]
  121.  
  122.     -- map returned face rect to image as currently displayed in window
  123.     r = rect(fr["left"], fr["top"], fr["left"]+fr["width"], fr["top"]+fr["height"])
  124.     r = r.map(img.rect, sprite(1).rect)
  125.  
  126.     -- draw face rect
  127.     imgOverlay.draw(r, props)
  128.  
  129.     -- draw emotion scores
  130.     str = ""
  131.     scores = info[i]["scores"]
  132.     cntj = scores.count
  133.     repeat with j = 1 to cntj
  134.       put scores.getPropAt(j)&": "&scores[j]&RETURN after str
  135.     end repeat
  136.     delete the last char of str
  137.     txt.text = str
  138.     -- hilite the emotion with max. score
  139.     txt.line[scores.getPos(max(scores))].fontStyle = [#underline]
  140.     txtImg = txt.image.extractAlpha()
  141.     x = r.left + 3
  142.     y = r.bottom + 2
  143.     r = rect(x, y, x+txtImg.width, y+txtImg.height)
  144.     imgOverlay.copyPixels(txtImg, r, txtImg.rect, [#ink:36])
  145.   end repeat
  146.  
  147.   -- force immediate update
  148.   _movie.updateStage()
  149. end
  150.  
  151. ----------------------------------------
  152. -- Returns (centered) max. rect with specified ratio that fits into specified rect
  153. -- @param {rect} tRect
  154. -- @param {float} tRatio
  155. -- @return {rect}
  156. ----------------------------------------
  157. on findMaxRect (tRect, tRatio)
  158.   if tRect.width=0 OR tRect.height=0 OR tRatio=0 then return tRect
  159.   tMaxRectRatio = float(tRect.width)/tRect.height
  160.   if tMaxRectRatio>=tRatio then
  161.     h = tRect.height
  162.     w = h * tRatio
  163.     x = (tRect.width-w)/2
  164.     y = 0
  165.   else
  166.     w = tRect.width
  167.     h = w / tRatio
  168.     x = 0
  169.     y = (tRect.height-h)/2
  170.   end if
  171.   return rect(x, y, x+w, y+h)
  172. end
  173.  
  174. ----------------------------------------
  175. -- Sends image file to Project Oxford Emotion Analysis API, returns result as JSON string
  176. -- @param {string} fn
  177. -- @return {string}
  178. ----------------------------------------
  179. on getEmotion (fn)
  180.   -- get a CURL handle (=Curl Xtra instance)
  181.   ch = $.curl.init()
  182.  
  183.   -- upload this image file as raw POST data
  184.   if $.OS="win" then
  185.     ch.setSourceFile(fn)
  186.   else
  187.     ch.setSourceFile(curl_hfs2posix(fn)) -- Curl Xtra for Mac expects POSIX, not HFS pathes
  188.   end if
  189.  
  190.   -- specify options
  191.   ch.setOption($.curl.CURLOPT.URL, "https://api.projectoxford.ai/emotion/v1.0/recognize")
  192.   ch.setOption($.curl.CURLOPT.POST, 1)
  193.  
  194.   -- add custom HTTP headers
  195.   header = []
  196.   header.add("Ocp-Apim-Subscription-Key: "&emotionApiKey)
  197.   header.add("Content-Type: application/octet-stream")
  198.   header.add("Content-Length: " & $.file.size(fn))
  199.   ch.setOption($.curl.CURLOPT.HTTPHEADER, header)
  200.  
  201.   -- returnMode: 0=return error code (=default), 1=return data
  202.   return ch.exec(1)
  203. end
  204.  
[raw code]