/src/mari/ociodisplay.py

http://github.com/imageworks/OpenColorIO · Python · 242 lines · 212 code · 0 blank · 30 comment · 0 complexity · 628ce8dd89b77ce86f546699216592d7 MD5 · raw file

  1. """
  2. This script allows the use of OpenColorIO display transforms (3d luts) in
  3. the Mari Viewer. Requires Mari 1.3v2+.
  4. This example is not represntative of the final Mari OCIO workflow, merely
  5. an API demonstration. This code is a work in progress, to demonstrate the
  6. integration of OpenColorIO and Mari. The APIs this code relies on are subject
  7. to change at any time, and as such should not be relied on for production use
  8. (yet).
  9. LINUX testing instructions:
  10. * Build OCIO
  11. mkdir -p dist_mari
  12. mkdir -p build_mari && cd build_mari
  13. cmake -D CMAKE_BUILD_TYPE=Release \
  14. -D CMAKE_INSTALL_PREFIX=../dist_mari \
  15. -D PYTHON=/usr/bin/python2.6 \
  16. -D OCIO_NAMESPACE=OpenColorIO_Mari \
  17. ../
  18. make install -j8
  19. * Set $OCIO color environment
  20. setenv OCIO setenv OCIO <YOURDIR>/ocio.configs/spi-vfx/config.ocio
  21. (Profiles available for download from opencolorio.org)
  22. * Run Mari with OpenColorIO added to the LD_LIBRARY_PATH, and Python
  23. env LD_LIBRARY_PATH=<YOURDIR>/dist_mari/lib/ PYTHONPATH=<YOURDIR>/dist_mari/lib/python2.6 mari
  24. * Source this script in the python console.
  25. Also - IMPORTANT - you must enable 'Use Color Correction' in the Color Manager.
  26. """
  27. import mari, time, PythonQt
  28. QtGui = PythonQt.QtGui
  29. QtCore = PythonQt.QtCore
  30. try:
  31. import PyOpenColorIO as OCIO
  32. mari.app.log("OCIODisplay: %s" % OCIO.__file__)
  33. except Exception,e:
  34. OCIO = None
  35. mari.app.log("OCIODisplay: Error: Could not import OpenColorIO python bindings.")
  36. mari.app.log("OCIODisplay: Please confirm PYTHONPATH has dir containing PyOpenColorIO.so")
  37. __all__ = [ 'OCIO', 'CreateOCIODisplayTransform', 'OCIODisplayUI']
  38. LUT3D_SIZE = 32
  39. WINDOW_NAME = "OpenColorIO Display Manager"
  40. CREATE_FLOATING = True
  41. class OCIODisplayUI(QtGui.QWidget):
  42. def __init__(self):
  43. QtGui.QWidget.__init__(self)
  44. QtGui.QGridLayout(self)
  45. self.setWindowTitle(WINDOW_NAME)
  46. self.setMinimumWidth(300)
  47. config = OCIO.GetCurrentConfig()
  48. self.__inputColorSpace = OCIO.Constants.ROLE_DEFAULT
  49. inputColorSpaces = [ OCIO.Constants.ROLE_TEXTURE_PAINT,
  50. 'dt8',
  51. OCIO.Constants.ROLE_SCENE_LINEAR ]
  52. for cs in inputColorSpaces:
  53. if config.getColorSpace(cs) is None: continue
  54. self.__inputColorSpace = cs
  55. break
  56. self.__fStopOffset = 0.0
  57. self.__viewGamma = 1.0
  58. self.__swizzle = (True, True, True, True)
  59. self.__filter_cacheID = None
  60. self.__filter = None
  61. self.__texture3d_cacheID = None
  62. self.__counter_hack = 0
  63. self.__buildUI()
  64. self.__rebuildFilter()
  65. def __buildUI(self):
  66. config = OCIO.GetCurrentConfig()
  67. self.layout().addWidget( QtGui.QLabel("Input Color Space", self), 0, 0)
  68. csWidget = QtGui.QComboBox(self)
  69. self.layout().addWidget( csWidget, 0, 1)
  70. csIndex = None
  71. for name in (cs.getName() for cs in config.getColorSpaces()):
  72. csWidget.addItem(name)
  73. if name == self.__inputColorSpace:
  74. csIndex = csWidget.count - 1
  75. if csIndex is not None:
  76. csWidget.setCurrentIndex(csIndex)
  77. csWidget.connect( QtCore.SIGNAL('currentIndexChanged(const QString &)'), self.__csChanged)
  78. # This doesnt work until the Horizontal enum is exposed.
  79. """
  80. self.__fstopWidget_numStops = 3.0
  81. self.__fstopWidget_ticksPerStop = 4
  82. self.layout().addWidget( QtGui.QLabel("FStop", self), 1, 0)
  83. fstopWidget = QtGui.QSlider(Horizontal, self)
  84. self.layout().addWidget( fstopWidget, 1, 1)
  85. fstopWidget.setMinimum(int(-self.__fstopWidget_numStops*self.__fstopWidget_ticksPerStop))
  86. fstopWidget.setMaximum(int(self.__fstopWidget_numStops*self.__fstopWidget_ticksPerStop))
  87. fstopWidget.setTickInterval(self.__fstopWidget_ticksPerStop)
  88. """
  89. def __csChanged(self, text):
  90. text = str(text)
  91. if text != self.__inputColorSpace:
  92. self.__inputColorSpace = text
  93. self.__rebuildFilter()
  94. def __rebuildFilter(self):
  95. config = OCIO.GetCurrentConfig()
  96. display = config.getDefaultDisplay()
  97. view = config.getDefaultView(display)
  98. transform = CreateOCIODisplayTransform(config, self.__inputColorSpace,
  99. display, view,
  100. self.__swizzle,
  101. self.__fStopOffset, self.__viewGamma)
  102. processor = config.getProcessor(transform)
  103. shaderDesc = dict( [('language', OCIO.Constants.GPU_LANGUAGE_GLSL_1_3),
  104. ('functionName', 'display_ocio_$ID_'),
  105. ('lut3DEdgeLen', LUT3D_SIZE)] )
  106. filterCacheID = processor.getGpuShaderTextCacheID(shaderDesc)
  107. if filterCacheID != self.__filter_cacheID:
  108. self.__filter_cacheID = filterCacheID
  109. mari.app.log("OCIODisplay: Creating filter %s" % self.__filter_cacheID)
  110. desc = "sampler3D lut3d_ocio_$ID_;\n"
  111. desc += processor.getGpuShaderText(shaderDesc)
  112. body = "{ Out = display_ocio_$ID_(Out, lut3d_ocio_$ID_); }"
  113. # Clear the existing color managment filter stack and create a new filter
  114. # HACK: Increment a counter by 1 each time to force a refresh
  115. #self.__counter_hack += 1
  116. #name = "OCIO %s %s %s v%d" % (display, view, self.__inputColorSpace, self.__counter_hack)
  117. name = "OCIO %s %s %s" % (display, view, self.__inputColorSpace)
  118. self.__filter = None
  119. self.__texture3d_cacheID = None
  120. mari.gl_render.clearPostFilterStack()
  121. self.__filter = mari.gl_render.createPostFilter(name, desc, body)
  122. mari.gl_render.appendPostFilter(self.__filter)
  123. else:
  124. mari.app.log('OCIODisplay: no shader text update required')
  125. texture3d_cacheID = processor.getGpuLut3DCacheID(shaderDesc)
  126. if texture3d_cacheID != self.__texture3d_cacheID:
  127. lut3d = processor.getGpuLut3D(shaderDesc)
  128. mari.app.log("OCIODisplay: Updating 3dlut %s" % texture3d_cacheID)
  129. if self.__texture3d_cacheID is None:
  130. self.__filter.setTexture3D("lut3d_ocio_$ID_",
  131. LUT3D_SIZE, LUT3D_SIZE, LUT3D_SIZE,
  132. self.__filter.FORMAT_RGB, lut3d)
  133. else:
  134. self.__filter.updateTexture3D( "lut3d_ocio_$ID_", lut3d)
  135. self.__texture3d_cacheID = texture3d_cacheID
  136. else:
  137. mari.app.log("OCIODisplay: No lut3d update required")
  138. def CreateOCIODisplayTransform(config,
  139. inputColorSpace,
  140. display, view,
  141. swizzle,
  142. fstopOffset, viewGamma):
  143. displayTransform = OCIO.DisplayTransform()
  144. displayTransform.setInputColorSpaceName( inputColorSpace )
  145. displayColorSpace = config.getDisplayColorSpaceName(display, view)
  146. displayTransform.setDisplayColorSpaceName( displayColorSpace )
  147. # Add the channel sizzle
  148. lumacoef = config.getDefaultLumaCoefs()
  149. mtx, offset = OCIO.MatrixTransform.View(swizzle, lumacoef)
  150. transform = OCIO.MatrixTransform()
  151. transform.setValue(mtx, offset)
  152. displayTransform.setChannelView(transform)
  153. # Add the linear fstop gain
  154. gain = 2**fstopOffset
  155. mtx, offset = OCIO.MatrixTransform.Scale((gain,gain,gain,gain))
  156. transform = OCIO.MatrixTransform()
  157. transform.setValue(mtx, offset)
  158. displayTransform.setLinearCC(transform)
  159. # Add the post-display CC
  160. transform = OCIO.ExponentTransform()
  161. transform.setValue([1.0 / max(1e-6, v) for v in \
  162. (viewGamma, viewGamma, viewGamma, viewGamma)])
  163. displayTransform.setDisplayCC(transform)
  164. return displayTransform
  165. """
  166. SWIZZLE_COLOR = (True, True, True, True)
  167. SWIZZLE_RED = (True, False, False, False)
  168. SWIZZLE_GREEN = (False, True, False, False)
  169. SWIZZLE_BLUE = (False, False, True, False)
  170. SWIZZLE_ALPHA = (False, False, False, True)
  171. SWIZZLE_LUMA = (True, True, True, False)
  172. Timings
  173. OCIO Setup: 0.5 ms
  174. OCIO 3D Lut creation: 14.7 ms
  175. Mari Setup: 21.3 ms
  176. Mari Texture Upload: 44.2 ms
  177. """
  178. if __name__ == '__main__':
  179. if not hasattr(mari.gl_render,"createPostFilter"):
  180. mari.app.log("OCIODisplay: Error: This version of Mari does not support the mari.gl_render.createPostFilter API")
  181. else:
  182. if OCIO is not None:
  183. if CREATE_FLOATING:
  184. w = OCIODisplayUI()
  185. w.show()
  186. else:
  187. if WINDOW_NAME in mari.app.tabNames():
  188. mari.app.removeTab(WINDOW_NAME)
  189. w = OCIODisplayUI()
  190. mari.app.addTab(WINDOW_NAME, w)