"""
:filename:     VideoWriterPlugin.py
:author:       roar@tordivel.no
:requirements: Scorpion 9.3.0.515 or higher
Scorpion GUI plugin for creating video from single images
::
  1.0.0.2, 11nov2015, RL: modified for autodoc
  1.0.0.1, 07jun2012, roar@tordivel.no:
    created
"""
__version__ = '1.0.0.2'
import os
import datetime
import cv
import arrToNumpy
from Scorpion import RegisterCallback,GetCameraImages,GetImageMatr,PluginChanged,GetStringValue,GetBoolValue,GetControlByHandle,SelectDirectory,SelectTagname
#-----------------------------------------------------------------------------
# VideoWriterPlugin implementation
#-----------------------------------------------------------------------------
[docs]class VideoWriterPlugin(object):
  """
  Scorpion GUI plugin for creating video from single images
  """
  def __init__(self,cntr,name):
    self.iname=cntr.iname      #full path name of control
    self.name=name             #this plugins name
    self.loading=True          #flag to disable control change event while loading
    self.writer=None           #the videowriter object
    self.filename=''           #current filename
    self.framecnt=0            #number of frames in current video
    cntr.deleteControls()      #delete previous added controls if any
    HOFFS = 80                 #horizontal position of 2'nd controls
    VOFFS = 22                 #vertical spacing
    y = VOFFS
    self.gbSetup=cntr.addControl('GroupBox',0,0)
    self.gbSetup.align=5
    self.gbSetup.caption='Video generation'
    self.lblSource=self.gbSetup.addControl('Label',8,y)
    self.lblSource.caption='Source'
    self.cbSource=self.gbSetup.addControl('ComboBox',HOFFS,self.lblSource.top-2)
    self.cbSource.style=2
    self.cbSource.onClick=self.controlChanged
    self.cbActive=self.gbSetup.addControl('CheckBox',self.cbSource.right+8,self.lblSource.top)
    self.cbActive.caption='Active'
    self.cbActive.onClick=self.controlChanged
    y+=VOFFS
    self.lblFormat=self.gbSetup.addControl('Label',8,y)
    self.lblFormat.caption='Video codec'
    self.cbFormat=self.gbSetup.addControl('ComboBox',HOFFS,self.lblFormat.top-2)
    self.cbFormat.style=2
    #self.cbFormat.items=('Uncompressed','RGB(A)','I420','IYUV','MPEG-1','MPEG-4.2','MPEG-4.3','MPEG-4','H263','H263I','FLV1')  #we don't support all
    self.cbFormat.items=('Uncompressed','RGB(A)','I420','IYUV') #most common avi formats
    self.cbFormat.itemIndex=2                                   #most compressing format
    self.cbFormat.onClick=self.controlChanged
    self.lblFps=self.gbSetup.addControl('Label',self.cbFormat.right+8,self.lblFormat.top)
    self.lblFps.caption='Playback framerate [fps]'
    self.eFps=self.gbSetup.addControl('Edit',self.lblFps.right+8,self.lblFps.top-2)
    self.eFps.width=self.eFps.height+4
    self.eFps.text='5'
    self.eFps.onChange=self.controlChanged
    y+=VOFFS
    self.lblFolder=self.gbSetup.addControl('Label',8,y)
    self.lblFolder.caption='Folder'
    self.eFolder=self.gbSetup.addControl('Edit',HOFFS,self.lblFolder.top-2)
    self.eFolder.right=self.eFps.left
    self.eFolder.text='Images'
    self.eFolder.onChange=self.controlChanged
    self.bFolder=self.gbSetup.addControl('Button',self.eFps.right-self.eFps.height,self.eFolder.top)
    self.bFolder.width=self.eFps.height
    self.bFolder.height=self.eFps.height
    self.bFolder.caption='...'
    self.bFolder.onClick=self.bFolderClick
    y+=VOFFS
    self.lblGuard=self.gbSetup.addControl('Label',8,y)
    self.lblGuard.caption='Guard'
    self.eGuard=self.gbSetup.addControl('Edit',HOFFS,self.lblGuard.top-2)
    self.eGuard.right=self.eFps.left
    self.eGuard.text=''
    self.eGuard.onChange=self.controlChanged
    self.bGuard=self.gbSetup.addControl('Button',self.eFps.right-self.eFps.height,self.eGuard.top)
    self.bGuard.width=self.eFps.height
    self.bGuard.height=self.eFps.height
    self.bGuard.caption='...'
    self.bGuard.onClick=self.bGuardClick
    y+=VOFFS
    self.pState=self.gbSetup.addControl('Panel',8,y+2)
    self.pState.width=10
    self.pState.height=10
    self.pState.bevelInner=0
    self.pState.bevelOuter=1
    self.pState.caption=''
    self.pState.color='white'
    self.lblFrameCnt=self.gbSetup.addControl('Label',self.pState.right+8,y)
    self.lblFrameCnt.caption='-'
    self.lblFilename=self.gbSetup.addControl('Label',HOFFS,y)
    self.lblFilename.caption=''
    self.updateSourceCombo()
    self.loading=False
    RegisterCallback('Actions.BeforeStart',self.actionsBeforeStart)
    RegisterCallback('Actions.AfterStop',self.actionsAfterStop)
    RegisterCallback('Actions.AfterGrab',self.actionsAfterGrab)
    RegisterCallback('System.AccessControlChanged',self.systemAccessControlChanged)
[docs]  def updateSourceCombo(self):
    '''
    populete list of images in combobox
    '''
    names=[]
    imgs=GetCameraImages()
    idx=self.cbSource.itemIndex         #selected
    if imgs.count>0:
      for i in range(imgs.count):
        names.append(imgs.get(i).name)
    self.cbSource.items=tuple(names)
    self.cbSource.itemIndex=max(0,min(idx,len(names)))
 
[docs]  def getConfig(self):
    '''
    returns plugin configuration as string
    '''
    from SPB import CreateSpb
    spb=CreateSpb()
    #create a spb instance, populate with the plugin configuration
    #and return the xml string
    spb.setInt('version',1)
    spb.setInt('source',self.cbSource.itemIndex)
    spb.setBool('active',self.cbActive.checked)
    spb.setInt('format',self.cbFormat.itemIndex)
    spb.setFloat('fps',float(self.eFps.text))
    spb.setText('folder',self.eFolder.text)
    spb.setText('guard',self.eGuard.text)
    return spb.xml
 
[docs]  def setConfig(self,value):
    '''
    set plugin configuration from the string 'value'
    '''
    self.loading=True
    from SPB import CreateSpb
    spb=CreateSpb(value)
    #extract plugin configuration from the spb object
    if spb.getInt('version')==1:
      self.cbSource.itemIndex=spb.getInt('source')
      self.cbActive.checked=spb.getBool('active')
      self.cbFormat.itemIndex=spb.getInt('format')
      self.eFps.text=str(spb.getFloat('fps'))
      self.eFolder.text=spb.getText('folder')
      self.eGuard.text=spb.getText('guard')
    self.loading=False
 
[docs]  def bFolderClick(self,sender,args):
    '''
    show folder selection dialog
    '''
    folder=os.path.join(GetStringValue('System.Profile'),self.eFolder.text)
    ok,folder=SelectDirectory(folder)
    if ok:self.eFolder.text=os.path.relpath(folder,GetStringValue('System.Profile'))
 
[docs]  def bGuardClick(self,sender,args):
    '''
    select guard tagname
    '''
    guard=SelectTagname(self.eGuard.text)
    if guard<>'':self.eGuard.text=guard
 
[docs]  def controlChanged(self,sender,args):
    '''
    notify configuration changed
    '''
    if not self.loading:
      PluginChanged(self)
 
[docs]  def enableControls(self):
    '''
    enable controls due to operation state
    '''
    running=GetBoolValue('System.Running')
    service=GetBoolValue('System.Service') or GetBoolValue('System.Settings')
    self.cbSource.enabled=service and not running
    self.cbActive.enabled=not running
    self.cbFormat.enabled=service and not running
    self.eFps.enabled=service and not running
    self.eFolder.enabled=not running
    self.bFolder.enabled=not running
    self.eGuard.enabled=service and not running
    self.bGuard.enabled=service and not running
 
[docs]  def systemAccessControlChanged(self,settings,service):
    '''
    update rights
    '''
    self.enableControls()
 
[docs]  def actionsBeforeStart(self):
    '''
    start of video
    '''
    self.gbSetup.enabled=False          #enables controls
    self.framecnt=0
    self.lblFrameCnt.caption='-'
    self.enableControls()
 
[docs]  def actionsAfterStop(self):
    '''
    end of video
    '''
    self.writer=None                    #closes video and deletes writer
    self.gbSetup.enabled=True           #enables controls
    self.pState.color='white'           #stopped
    self.enableControls()
 
[docs]  def actionsAfterGrab(self):
    '''
    add image to video
    '''
    def array2cv(a):
      dtype2depth = {
            'uint8':   cv.IPL_DEPTH_8U,
            'int8':    cv.IPL_DEPTH_8S,
            'uint16':  cv.IPL_DEPTH_16U,
            'int16':   cv.IPL_DEPTH_16S,
            'int32':   cv.IPL_DEPTH_32S,
            'float32': cv.IPL_DEPTH_32F,
            'float64': cv.IPL_DEPTH_64F,
        }
      try:
        nChannels = a.shape[2]
      except:
        nChannels = 1
      cv_im = cv.CreateImageHeader((a.shape[1],a.shape[0]),dtype2depth[str(a.dtype)],nChannels)
      cv.SetData(cv_im, a.tostring(),a.dtype.itemsize*nChannels*a.shape[1])
      return cv_im
    if self.cbActive.checked and GetBoolValue('System.Running') and not GetBoolValue('System.CameraSimulation'):
      if (self.eGuard.text=='') or GetBoolValue(self.eGuard.text):
        imgname=GetCameraImages().get(self.cbSource.itemIndex).name
        src=GetImageMatr(imgname)
        if self.writer==None:
          try:
            height,width=src.dim()
            if   self.cbFormat.itemIndex==0: fcc=0                             #Uncompressed
            elif self.cbFormat.itemIndex==1: fcc=cv.CV_FOURCC('D','I','B',' ') #RGB(A)
            elif self.cbFormat.itemIndex==2: fcc=cv.CV_FOURCC('I','4','2','0') #I420
            elif self.cbFormat.itemIndex==3: fcc=cv.CV_FOURCC('I','Y','U','V') #IYUV
            elif self.cbFormat.itemIndex==4: fcc=cv.CV_FOURCC('P','I','M','1') #MPEG1
            elif self.cbFormat.itemIndex==5: fcc=cv.CV_FOURCC('M','P','4','2') #MPEG-4.2
            elif self.cbFormat.itemIndex==6: fcc=cv.CV_FOURCC('D','I','V','3') #MPEG-4.3
            elif self.cbFormat.itemIndex==7: fcc=cv.CV_FOURCC('D','I','V','X') #MPEG-4
            elif self.cbFormat.itemIndex==8: fcc=cv.CV_FOURCC('U','2','6','3') #H263
            elif self.cbFormat.itemIndex==9: fcc=cv.CV_FOURCC('I','2','6','3') #H263I
            elif self.cbFormat.itemIndex==10:fcc=cv.CV_FOURCC('F','L','V','1') #FLV1
            else:                            fcc=-1                            #select dialog
            folder=os.path.join(GetStringValue('System.Profile'),self.eFolder.text)
            fname='%s_%s.avi'%(imgname,datetime.datetime.now().strftime('%Y%m%d_%H%M%S'))
            self.filename=os.path.join(folder,fname)
            if not os.path.exists(folder): os.mkdir(folder)
            if src.elemtype()=='uint8' and self.cbFormat.itemIndex==8:
              color=False
            else:
              color=True
            self.writer=cv.CreateVideoWriter(self.filename,fcc,float(self.eFps.text),(width,height),color)
            self.lblFilename.caption=fname
            self.pState.color='red'
          except Exception,msg:
            print Exception,msg
        if self.writer:
          try:
            np=arrToNumpy.arrToNumpy(src)
            img=array2cv(np)
            cv.WriteFrame(self.writer,img)
            self.framecnt+=1
            self.lblFrameCnt.caption=str(self.framecnt)
          except Exception,msg:
            print Exception,msg
  
[docs]def CreatePlugin(hWnd, name=''):
  '''
  Scorpion Plugin Stub - Required
  '''
  cntr=GetControlByHandle(hWnd)
  return VideoWriterPlugin(cntr,name)