PageRenderTime 50ms CodeModel.GetById 12ms app.highlight 33ms RepoModel.GetById 1ms app.codeStats 0ms

/docs/developers/usage_examples.rst

http://github.com/imageworks/OpenColorIO
ReStructuredText | 363 lines | 274 code | 89 blank | 0 comment | 0 complexity | 66f32deab9ff97cd2f5b93bad1a58fa3 MD5 | raw file
  1..
  2  SPDX-License-Identifier: CC-BY-4.0
  3  Copyright Contributors to the OpenColorIO Project.
  4
  5.. _developers-usageexamples:
  6
  7Usage Examples
  8==============
  9
 10Some examples of using the OCIO API, via both C++ and the Python bindings.
 11
 12For further examples, see the ``src/apps/`` directory in the git repository
 13
 14.. _usage_applybasic:
 15
 16Applying a basic ColorSpace transform, using the CPU
 17****************************************************
 18This describes what code is used to convert from a specified source
 19:cpp:class:`ColorSpace` to a specified destination :cpp:class:`ColorSpace`.
 20If you are using the OCIO Nuke plugins, the OCIOColorSpace node performs these
 21steps internally.
 22
 23#. **Get the Config.**
 24   This represents the entirety of the current color "universe". It can either
 25   be initialized by your app at startup or created explicitly. In common
 26   usage, you can just query :cpp:func:`GetCurrentConfig`, which will auto
 27   initialize on first use using the :envvar:`OCIO` environment variable.
 28
 29#. **Get Processor from the Config.**
 30   A processor corresponds to a 'baked' color transformation. You specify two
 31   arguments when querying a processor: the :ref:`colorspace_section` you are
 32   coming from, and the :ref:`colorspace_section` you are going to.
 33   :ref:`cfgcolorspaces_section` ColorSpaces can be either explicitly named
 34   strings (defined by the current configuration) or can be
 35   :ref:`cfgroles_section` (essentially :ref:`colorspace_section` aliases)
 36   which are consistent across configurations. Constructing a
 37   :cpp:class:`Processor` object is likely a blocking operation (thread-wise)
 38   so care should be taken to do this as infrequently as is sensible. Once per
 39   render 'setup' would be appropriate, once per scanline would be
 40   inappropriate.
 41
 42#. **Convert your image, using the Processor.**
 43   Once you have the processor, you can apply the color transformation using
 44   the "apply" function.  In :ref:`usage_applybasic_cpp`, you apply the
 45   processing in-place, by first wrapping your image in an
 46   :cpp:class:`ImageDesc` class.  This approach is intended to be used in high
 47   performance applications, and can be used on multiple threads (per scanline,
 48   per tile, etc).   In :ref:`usage_applybasic_python` you call
 49   "applyRGB" / "applyRGBA" on your sequence of pixels.   Note that in both
 50   languages, it is far more efficient to call "apply" on batches of pixels at
 51   a time.
 52
 53.. _usage_applybasic_cpp:
 54
 55C++
 56+++
 57
 58.. code-block:: cpp
 59   
 60   #include <OpenColorIO/OpenColorIO.h>
 61   namespace OCIO = OCIO_NAMESPACE;
 62   
 63   try
 64   {
 65       OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
 66       ConstProcessorRcPtr processor = config->getProcessor(OCIO::ROLE_COMPOSITING_LOG,
 67                                                            OCIO::ROLE_SCENE_LINEAR);
 68       
 69       OCIO::PackedImageDesc img(imageData, w, h, 4);
 70       processor->apply(img);
 71   }
 72   catch(OCIO::Exception & exception)
 73   {
 74       std::cerr << "OpenColorIO Error: " << exception.what() << std::endl;
 75   }
 76
 77.. _usage_applybasic_python:
 78
 79Python
 80++++++
 81
 82.. code-block:: py
 83   
 84   import PyOpenColorIO as OCIO
 85   
 86   try:
 87       config = OCIO.GetCurrentConfig()
 88       processor = config.getProcessor(OCIO.Constants.ROLE_COMPOSITING_LOG,
 89                                       OCIO.Constants.ROLE_SCENE_LINEAR)
 90       
 91       # Apply the color transform to the existing RGBA pixel data
 92       img = processor.applyRGBA(img)
 93   except Exception, e:
 94       print "OpenColorIO Error",e
 95
 96.. _usage_displayimage:
 97
 98Displaying an image, using the CPU (simple ColorSpace conversion)
 99*****************************************************************
100Converting an image for display is similar to a normal color space conversion.
101The only difference is that one has to first determine the name of the display
102(destination) ColorSpace by querying the config with the device name and
103transform name. 
104
105#. **Get the Config.**
106   See :ref:`usage_applybasic` for details.
107   
108#. **Lookup the display ColorSpace.**
109   The display :cpp:class:`ColorSpace` is queried from the configuration using
110   :cpp:func:`Config::getDisplayColorSpaceName`.   If the user has specified
111   value for the ``device`` or the ``displayTransformName``, use them. If these
112   values are unknown default values can be queried (as shown below).
113
114#. **Get the processor from the Config.**
115   See :ref:`usage_applybasic` for details.
116
117#. **Convert your image, using the processor.**
118   See :ref:`usage_applybasic` for details.
119
120C++
121+++
122
123.. code-block:: cpp
124   
125   #include <OpenColorIO/OpenColorIO.h>
126   namespace OCIO = OCIO_NAMESPACE;
127   
128   OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
129   
130   // If the user hasn't picked a display, use the defaults...
131   const char * device = config->getDefaultDisplayDeviceName();
132   const char * transformName = config->getDefaultDisplayTransformName(device);
133   const char * displayColorSpace = config->getDisplayColorSpaceName(device, transformName);
134   
135   ConstProcessorRcPtr processor = config->getProcessor(OCIO::ROLE_SCENE_LINEAR,
136                                                        displayColorSpace);
137   
138   OCIO::PackedImageDesc img(imageData, w, h, 4);
139   processor->apply(img);
140
141Python
142++++++
143
144.. code-block:: python
145
146    import PyOpenColorIO as OCIO
147
148    config = OCIO.GetCurrentConfig()
149
150    device = config.getDefaultDisplayDeviceName()
151    transformName = config.getDefaultDisplayTransformName(device)
152    displayColorSpace = config.getDisplayColorSpaceName(device, transformName)
153
154    processor = config.getProcessor(OCIO.Constants.ROLE_SCENE_LINEAR, displayColorSpace)
155
156    processor.applyRGB(imageData)
157
158
159Displaying an image, using the CPU (Full Display Pipeline)
160**********************************************************
161
162This alternative version allows for a more complex displayTransform, allowing
163for all of the controls typically added to real-world viewer interfaces. For
164example, options are allowed to control which channels (red, green, blue,
165alpha, luma) are visible, as well as allowing for optional color corrections
166(such as an exposure offset in scene linear). If you are using the OCIO Nuke
167plugins, the OCIODisplay node performs these steps internally.
168
169#. **Get the Config.**
170   See :ref:`usage_applybasic` for details.
171#. **Lookup the display ColorSpace.**
172   See :ref:`usage_displayimage` for details
173#. **Create a new DisplayTransform.**
174   This transform will embody the full 'display' pipeline you wish to control.
175   The user is required to call
176   :cpp:func:`DisplayTransform::setInputColorSpaceName` to set the input
177   ColorSpace, as well as
178   :cpp:func:`DisplayTransform::setDisplayColorSpaceName` (with the results of
179   :cpp:func:`Config::getDisplayColorSpaceName`).
180#. **Set any additional DisplayTransform options.**
181   If the user wants to specify a channel swizzle, a scene-linear exposure
182   offset, an artistic look, this is the place to add it. See below for an
183   example. Note that although we provide recommendations for display, any
184   transforms are allowed to be added into any of the slots. So if for your app
185   you want to add 3 transforms into a particular slot (chained together), you
186   are free to wrap them in a :cpp:class:`GroupTransform` and set it
187   accordingly!
188#. **Get the processor from the Config.**
189   The processor is then queried from the config passing the new
190   :cpp:class:`DisplayTransform` as the argument.   Once the processor has been
191   returned, the original :cpp:class:`DisplayTransform` is no longer necessary
192   to hold onto. (Though if you'd like to for re-use, there is no problem doing
193   so).
194#. **Convert your image, using the processor.**
195   See :ref:`usage_applybasic` for details.
196
197C++
198+++
199
200.. code-block:: cpp
201   
202   // Step 1: Get the config
203   OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
204   
205   // Step 2: Lookup the display ColorSpace
206   const char * device = config->getDefaultDisplayDeviceName();
207   const char * transformName = config->getDefaultDisplayTransformName(device);
208   const char * displayColorSpace = config->getDisplayColorSpaceName(device, transformName);
209   
210   // Step 3: Create a DisplayTransform, and set the input and display ColorSpaces
211   // (This example assumes the input is scene linear. Adapt as needed.)
212   
213   OCIO::DisplayTransformRcPtr transform = OCIO::DisplayTransform::Create();
214   transform->setInputColorSpaceName( OCIO::ROLE_SCENE_LINEAR );
215   transform->setDisplayColorSpaceName( displayColorSpace );
216   
217   // Step 4: Add custom transforms for a 'canonical' Display Pipeline
218   
219   // Add an fstop exposure control (in SCENE_LINEAR)
220   float gain = powf(2.0f, exposure_in_stops);
221   const float slope3f[] = { gain, gain, gain };
222   OCIO::CDLTransformRcPtr cc =  OCIO::CDLTransform::Create();
223   cc->setSlope(slope3f);
224   transform->setLinearCC(cc);
225   
226   // Add a Channel view 'swizzle'
227   
228   // 'channelHot' controls which channels are viewed.
229   int channelHot[4] = { 1, 1, 1, 1 };  // show rgb
230   //int channelHot[4] = { 1, 0, 0, 0 };  // show red
231   //int channelHot[4] = { 0, 0, 0, 1 };  // show alpha
232   //int channelHot[4] = { 1, 1, 1, 0 };  // show luma
233   
234   float lumacoef[3];
235   config.getDefaultLumaCoefs(lumacoef);
236   
237   float m44[16];
238   float offset[4];
239   OCIO::MatrixTransform::View(m44, offset, channelHot, lumacoef);
240   OCIO::MatrixTransformRcPtr swizzle = OCIO::MatrixTransform::Create();
241   swizzle->setValue(m44, offset);
242   transform->setChannelView(swizzle);
243   
244   // And then process the image normally.
245   OCIO::ConstProcessorRcPtr processor = config->getProcessor(transform);
246   
247   OCIO::PackedImageDesc img(imageData, w, h, 4);
248   processor->apply(img);
249
250Python
251++++++
252
253.. code-block:: python
254
255    import PyOpenColorIO as OCIO
256
257    # Step 1: Get the config
258    config = OCIO.GetCurrentConfig()
259
260    # Step 2: Lookup the display ColorSpace
261    display = config.getDefaultDisplay()
262    view = config.getDefaultView(display)
263
264    # Step 3: Create a DisplayTransform, and set the input, display, and view
265    # (This example assumes the input is scene linear. Adapt as needed.)
266
267    transform = OCIO.DisplayTransform()
268    transform.setInputColorSpaceName(OCIO.Constants.ROLE_SCENE_LINEAR)
269    transform.setDisplay(display)
270    transform.setView(view)
271
272    # Step 4: Add custom transforms for a 'canonical' Display Pipeline
273
274    # Add an fstop exposure control (in SCENE_LINEAR)
275    exposure = 0 # Example data: zero exposure adjustment
276    gain = 2**exposure
277    slope3f = (gain, gain, gain)
278
279    cc = OCIO.CDLTransform()
280    cc.setSlope(slope3f)
281
282    transform.setLinearCC(cc)
283
284    # Add a Channel view 'swizzle'
285
286    channelHot = (1, 1, 1, 1) # show rgb
287    # channelHot = (1, 0, 0, 0) # show red
288    # channelHot = (0, 0, 0, 1) # show alpha
289    # channelHot = (1, 1, 1, 0) # show luma
290
291    lumacoef = config.getDefaultLumaCoefs()
292
293    m44, offset = OCIO.MatrixTransform.View(channelHot, lumacoef)
294
295    swizzle = OCIO.MatrixTransform()
296    swizzle.setValue(m44, offset)
297    transform.setChannelView(swizzle)
298
299    # And then process the image normally.
300    processor = config.getProcessor(transform)
301
302    imageData = [0,0,0, 1,0,0] # Example data: A black and a red pixel
303    print processor.applyRGB(imageData)
304
305
306Displaying an image, using the GPU
307**********************************
308
309Applying OpenColorIO's color processing using GPU processing is
310straightforward, provided you have the capability to upload custom shader code
311and a custom 3D Lookup Table (3DLUT).
312
313#. **Get the Processor.**
314   This portion of the pipeline is identical to the CPU approach. Just get the
315   processor as you normally would have, see above for details.
316#. **Create a GpuShaderDesc.**
317#. **Query the GPU Shader Text + 3D LUT.**
318#. **Configure the GPU State.**
319#. **Draw your image.**
320
321C++
322+++
323
324This example is available as a working app in the OCIO source: src/apps/ociodisplay.
325
326.. code-block:: cpp
327   
328   // Step 0: Get the processor using any of the pipelines mentioned above.
329   OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
330   const char * device = config->getDefaultDisplayDeviceName();
331   const char * transformName = config->getDefaultDisplayTransformName(device);
332   const char * displayColorSpace = config->getDisplayColorSpaceName(device, transformName); 
333   ConstProcessorRcPtr processor = config->getProcessor(OCIO::ROLE_SCENE_LINEAR,
334                                                        displayColorSpace);
335   
336   // Step 1: Create a GPU Shader Description
337   GpuShaderDesc shaderDesc;
338   shaderDesc.setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_0);
339   shaderDesc.setFunctionName("OCIODisplay");
340   const int LUT3D_EDGE_SIZE = 32;
341   shaderDesc.setLut3DEdgeLen(LUT3D_EDGE_SIZE);
342   
343   // Step 2: Compute and the 3D LUT
344   // Optional Optimization:
345   //     Only do this the 3D LUT's contents
346   //     are different from the last drawn frame.
347   //     Use getGpuLut3DCacheID to compute the cacheID.
348   //     cheaply.
349   // 
350   // std::string lut3dCacheID = processor->getGpuLut3DCacheID(shaderDesc);
351   int num3Dentries = 3*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE*LUT3D_EDGE_SIZE;
352   std::vector<float> g_lut3d;
353   g_lut3d.resize(num3Dentries);
354   processor->getGpuLut3D(&g_lut3d[0], shaderDesc);
355   
356   // Load the data into an OpenGL 3D Texture
357   glGenTextures(1, &g_lut3d_textureID);
358   glBindTexture(GL_TEXTURE_3D, g_lut3d_textureID);
359   glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB,
360                LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE, LUT3D_EDGE_SIZE,
361                0, GL_RGB,GL_FLOAT, &g_lut3d[0]);
362   
363   // Step 3: Query