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