PageRenderTime 45ms CodeModel.GetById 11ms app.highlight 27ms RepoModel.GetById 1ms app.codeStats 1ms

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