Lots of documentation! It's hosted!
parent
892d511525
commit
e0d49376cd
|
@ -0,0 +1 @@
|
|||
sphinx_rtd_theme_git/sphinx_rtd_theme/
|
|
@ -0,0 +1 @@
|
|||
Subproject commit eef98b316b947a9d8854add13cba702f41f00c14
|
|
@ -0,0 +1,32 @@
|
|||
Builtin Resources
|
||||
=================
|
||||
|
||||
openlut.gamma module
|
||||
--------------------
|
||||
|
||||
.. automodule:: openlut.gamma
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
openlut.gamut module
|
||||
--------------------
|
||||
|
||||
.. automodule:: openlut.gamut
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
openlut.lib.olOpt module
|
||||
------------------------
|
||||
|
||||
olOpt is the reason openlut is snappy! It contains the lower-level, fast functions
|
||||
that drive the rest of openlut.
|
||||
|
||||
|
||||
.. automodule:: openlut.lib.olOpt
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
|
@ -63,9 +63,9 @@ author = u'Sofus Rose'
|
|||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = u'0.0.1'
|
||||
version = u'0.2.1'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = u'0.0.1'
|
||||
release = u'0.2.1'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
@ -125,16 +125,18 @@ todo_include_todos = False
|
|||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
#
|
||||
html_theme = 'alabaster'
|
||||
html_theme = 'sphinx_rtd_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#
|
||||
# html_theme_options = {}
|
||||
html_theme_options = {
|
||||
"collapse_navigation" : False
|
||||
}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
# html_theme_path = []
|
||||
html_theme_path = ["_themes",]
|
||||
|
||||
# The name for this set of Sphinx documents.
|
||||
# "<project> v<release> documentation" by default.
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
Image Input/Output
|
||||
=================
|
||||
|
||||
All image IO happens via the ColMap module.
|
||||
|
||||
openlut.ColMap module
|
||||
---------------------
|
||||
|
||||
.. automodule:: openlut.ColMap
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
|
@ -6,11 +6,16 @@
|
|||
Welcome to openlut's documentation!
|
||||
===================================
|
||||
|
||||
Contents:
|
||||
|
||||
Table of Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:maxdepth: 4
|
||||
|
||||
intro
|
||||
imageio
|
||||
transforms
|
||||
builtins
|
||||
|
||||
|
||||
Indices and tables
|
||||
|
@ -20,3 +25,12 @@ Indices and tables
|
|||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
Full Docs
|
||||
=========
|
||||
|
||||
No nice explanations here - just all the docs in a list.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
modules
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
Introduction to openlut
|
||||
======================
|
||||
|
||||
Hello! TBD
|
|
@ -1,4 +1,4 @@
|
|||
openlut
|
||||
Full Documentation
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
|
|
|
@ -8,66 +8,6 @@ Subpackages
|
|||
|
||||
openlut.lib
|
||||
|
||||
Submodules
|
||||
----------
|
||||
|
||||
openlut.ColMap module
|
||||
---------------------
|
||||
|
||||
.. automodule:: openlut.ColMap
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.ColMat module
|
||||
---------------------
|
||||
|
||||
.. automodule:: openlut.ColMat
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.Func module
|
||||
-------------------
|
||||
|
||||
.. automodule:: openlut.Func
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.LUT module
|
||||
------------------
|
||||
|
||||
.. automodule:: openlut.LUT
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.Transform module
|
||||
------------------------
|
||||
|
||||
.. automodule:: openlut.Transform
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.gamma module
|
||||
--------------------
|
||||
|
||||
.. automodule:: openlut.gamma
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.gamut module
|
||||
--------------------
|
||||
|
||||
.. automodule:: openlut.gamut
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
Module contents
|
||||
---------------
|
||||
|
||||
|
@ -75,3 +15,11 @@ Module contents
|
|||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
C++ Extension: olOpt
|
||||
--------------
|
||||
|
||||
.. automodule:: openlut.lib.olOpt
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
Transforms
|
||||
=================
|
||||
|
||||
Doing image transforms in openlut uses the :py:func:`~ColMap.apply` method to apply Transform objects. A Transform
|
||||
object is any subclass of the Transform listed below. Examples include LUT, Func, and ColMat.
|
||||
|
||||
openlut.Transform module
|
||||
------------------------
|
||||
|
||||
.. automodule:: openlut.Transform
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
||||
openlut.LUT module
|
||||
------------------
|
||||
|
||||
.. automodule:: openlut.LUT
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.Func module
|
||||
-------------------
|
||||
|
||||
.. automodule:: openlut.Func
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
openlut.ColMat module
|
||||
---------------------
|
||||
|
||||
.. automodule:: openlut.ColMat
|
||||
:members:
|
||||
:undoc-members:
|
||||
:show-inheritance:
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
#!/bin/bash
|
||||
|
||||
REMOTE=sofus@wakingnexus
|
||||
|
||||
rsync -avzP build/html/* $REMOTE:~/openlut/
|
||||
|
||||
#ssh $REMOTE 'bash -s' << 'ENDSSH'
|
||||
#
|
||||
#cd /var/www/openlut
|
||||
#chown -R www-data:www-data *
|
||||
#
|
||||
#ENDSSH
|
|
@ -1,6 +1,7 @@
|
|||
from functools import reduce
|
||||
from imp import reload
|
||||
|
||||
import numpy as np
|
||||
import openlut as ol
|
||||
from openlut.lib.files import Log
|
||||
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
import sys, os, os.path
|
||||
|
||||
from functools import reduce
|
||||
|
||||
import numpy as np
|
||||
|
||||
#~ import skimage as si
|
||||
#~ import skimage.io
|
||||
#~ si.io.use_plugin('freeimage')
|
||||
|
||||
#~ from PIL import Image
|
||||
#~ import tifffile as tff
|
||||
|
||||
import wand
|
||||
import wand.image
|
||||
import wand.display
|
||||
|
@ -24,38 +19,134 @@ from . import gamma
|
|||
from .LUT import LUT
|
||||
from .Viewer import Viewer
|
||||
|
||||
from .lib import olOpt as olo
|
||||
|
||||
class ColMap :
|
||||
def __init__(self, resX, resY, depth = 16) :
|
||||
self.depth = depth
|
||||
self.rgbArr =
|
||||
'''
|
||||
The ColMap class stores an image in its 32 bit float internal working space.
|
||||
|
||||
:var DEPTHS: A dictionary of depths in relation to the Depths dictionary.
|
||||
|
||||
ColMaps are initialized by default with 0's; a black image. You can use
|
||||
`open` to load a path, :py:func:`~fromArray` to load from a numpy array, or :py:func:`~fromBinary` to load from
|
||||
a binary representation (useful in pipes).
|
||||
|
||||
:param shape: The numpy-style shape of the empty image. Specify width, then height.
|
||||
:type shape: tuple[int, int] or tuple[int, int, int]
|
||||
:param depth: The integer depth used for int format's input and output. Set to DEPTHS['full'] by default.
|
||||
:type depth: int or None
|
||||
|
||||
:return: An empty ColMap, holding a black image of specified shape.
|
||||
:raises ValueError: When trying to use unsupported bit depth.
|
||||
:raises ValueError: When using invalid image shape.
|
||||
'''
|
||||
|
||||
DEPTHS = { 'default' : None,
|
||||
'comp' : 8,
|
||||
'half' : 16,
|
||||
'full' : 32,
|
||||
'double' : 64
|
||||
}
|
||||
|
||||
#Constructors
|
||||
def __init__(self, shape, depth = None) :
|
||||
if depth not in ColMap.DEPTHS.values :
|
||||
raise ValueError('Bit depth not supported! Supported bit depths: {}'.format(', '.join(ColMap.DEPTHS.values)))
|
||||
|
||||
if len(shape) not in (2, 3) :
|
||||
raise ValueError('Please use a valid numpy image array shape!')
|
||||
|
||||
self.depth = depth if depth is None else ColMap.DEPTHS['full'] #This represents the real precision of data.
|
||||
self.rgbArr = np.zeros((shape[0], shape[1], 3), dtype=np.float32)
|
||||
|
||||
@staticmethod
|
||||
def fromArray(imgArr, depth = 16) :
|
||||
self.depth = depth
|
||||
def fromArray(imgArr) :
|
||||
'''
|
||||
Initialize a ColMap from a numpy array of either float or int type (containing an image).
|
||||
|
||||
self.rgbArr = np.array(rgbArr, dtype=np.float32) #Enforce 32 bit floats. Save memory.
|
||||
See :py:class:`~ColMap` initialization for a lower-level constructor.
|
||||
|
||||
:param imgArr: The numpy image array. Must have shape (width, height, 3)
|
||||
:param depth: The integer depth used for int format's input and output. None will use highest available.
|
||||
:type depth: int or None
|
||||
|
||||
:return: A ColMap containing the image represented in imgArr.
|
||||
:raises ValueError: When trying to use unsupported array data type
|
||||
'''
|
||||
|
||||
#Infer bitDepth from array to create new array, nArr, which we'll use to make our ColMap.
|
||||
|
||||
if issubclass(imgArr.dtype.type, np.integer) : #If it's an integer.
|
||||
bitDepth = int(''.join([i for i in str(imgArr.dtype) if i.isdigit()]))
|
||||
|
||||
nArr = np.divide(imgArr.astype(np.float32), 2 ** bitDepth - 1)
|
||||
|
||||
elif issubclass(imgArr.dtype.type, np.floating) : #It it's a float.
|
||||
#If we're dealing with an np.float16 array, we can't exactly start giving 32 bit output.
|
||||
if int(''.join([i for i in str(imgArr.dtype) if i.isdigit()])) == 16 :
|
||||
bitDepth = 16
|
||||
else :
|
||||
bitDepth = None
|
||||
|
||||
nArr = np.array(imgArr, dtype=np.float32)
|
||||
|
||||
else :
|
||||
raise ValueError('The input image array uses an invalid data type {}! Please use any np.int or np.float variant!'.format(imgArr.dtype.type))
|
||||
|
||||
|
||||
#We're taking over the creation of img.rgbArr, so we need to do different error checking of our own.
|
||||
if len(nArr.shape) not in (2, 3) :
|
||||
raise ValueError('Please use a valid numpy image array shape!')
|
||||
|
||||
elif len(nArr.shape) == 2 :
|
||||
|
||||
#If we're dealing with a greyscale image, then we need to convert it to RGB using an optimized C++ function.
|
||||
nArr = olo.grey_to_rgb(nArr.reshape(reduce(lambda a, b: a*b, nArr.shape))).reshape((nArr.shape[0], nArr.shape[1], 3))
|
||||
|
||||
img = ColMap(nArr.shape, depth=bitDepth)
|
||||
img.rgbArr = nArr
|
||||
|
||||
return img
|
||||
|
||||
@staticmethod
|
||||
def fromIntArray(imgArr) :
|
||||
bitDepth = int(''.join([i for i in str(imgArr.dtype) if i.isdigit()]))
|
||||
|
||||
self.depth = bitDepth
|
||||
|
||||
return ColMap(np.divide(imgArr.astype(np.float32), 2 ** bitDepth - 1))
|
||||
|
||||
#Operations - returns new ColMaps.
|
||||
def apply(self, transform) :
|
||||
def fromBinary(binData, fmt, width=None, height=None) :
|
||||
'''
|
||||
Applies a Transform object by running its apply method.
|
||||
'''
|
||||
#~ return transform.apply(self)
|
||||
return ColMap.fromArray(transform.sample(self.asarray()))
|
||||
Construct a ColMap from an image in binary form. See :py:func:`~ColMap.toBinary` for the inverse.
|
||||
|
||||
* This won't work for greyscale data - it's assumed to be RGB.
|
||||
|
||||
:param bin binData: The binary data blob to open.
|
||||
:param str fmt: Wand needs to know what image format the binary data being thrown at it is in! See https://www.imagemagick.org/script/formats.php .
|
||||
:param str width: You may specify a specific width if you're having problems.
|
||||
:param str height: You may specify a specific height if you're having problems.
|
||||
:return: The image, as a ColMat.
|
||||
:rtype: :py:class:`~ColMap`
|
||||
|
||||
This is great for pipes, where you're receiving binary data through stdin.
|
||||
* Set binData to `sys.stdin.buffer.read()` in a script to pipe data into it!
|
||||
|
||||
**NOTE: Uses Wand's "blob" functionality, and as such incurs Wand's limitations.**
|
||||
'''
|
||||
with wand.image.Image(blob=binData, format=fmt, width=width, height=height) as img:
|
||||
|
||||
return ColMap.fromArray(np.fromstring(img.make_blob("RGB"), dtype='uint{}'.format(img.depth)).reshape(img.height, img.width, 3))
|
||||
|
||||
|
||||
#IO Functions
|
||||
@staticmethod
|
||||
def open(path) :
|
||||
'''
|
||||
Opens 8 and 16 bit images of many formats.
|
||||
Construct a ColMap from an image on the disk.
|
||||
|
||||
:param str path: The image path to open.
|
||||
:return: The image, as a ColMat.
|
||||
:rtype: :py:class:`~ColMap`
|
||||
|
||||
ColMap currently uses ImageMagick to open a wide range of formats, including:
|
||||
|
||||
* **EXR**: The industry standard for HDR, wide-gamut, linear-encoded images.
|
||||
* **DPX**: An older production format.
|
||||
* **PNG**: Can store 16-bit images well. Usually quite slow.
|
||||
* *Any other IM-supported formats...* See https://www.imagemagick.org/script/formats.php
|
||||
'''
|
||||
|
||||
try :
|
||||
|
@ -69,16 +160,32 @@ class ColMap :
|
|||
#Fallback to opening using Wand.
|
||||
return ColMap.openWand(path)
|
||||
|
||||
#Vendor-specific open methods.
|
||||
#Operations - returns new ColMaps.
|
||||
def apply(self, transform) :
|
||||
'''
|
||||
Apply an image transformation, in the form of a subclass of :py:class:`~Transform`.
|
||||
|
||||
#~ def openSci(path) :
|
||||
#~ return ColMap.fromIntArray(si.io.imread(path)[:,:,:3])
|
||||
You can apply LUTs, gamma functions, matrices - simply insert an instance of :py:class:`~LUT`,
|
||||
:py:class:`~Func`, :py:class:`~ColMat`, or any other :py:class:`~Transform` object to apply it
|
||||
to the image!
|
||||
|
||||
:param transform: An image transform.
|
||||
:type transform: :py:class:`~Transform`
|
||||
:return: A transformed ColMap.
|
||||
'''
|
||||
return ColMap.fromArray(transform.sample(self.asarray()))
|
||||
|
||||
#Vendor-specific open methods.
|
||||
@staticmethod
|
||||
def openWand(path) :
|
||||
'''
|
||||
Open a file using the Wand ImageMagick binding.
|
||||
Vendor-specific :py:func:`~ColMap.open` function. See :py:func:`~ColMap.open`
|
||||
|
||||
:param str path: The image path to open.
|
||||
:return: The image, as a ColMat.
|
||||
:rtype: :py:class:`~ColMap`
|
||||
'''
|
||||
|
||||
with wand.image.Image(filename=path) as img:
|
||||
#Quick inverse sRGB transform, to undo what Wand did - but not for exr's, which are linear bastards.
|
||||
if img.format != 'EXR' :
|
||||
|
@ -87,32 +194,32 @@ class ColMap :
|
|||
|
||||
img.colorspace = 'srgb' if img.format == 'DPX' else 'rgb' #Fix for IM's dpx bug.
|
||||
|
||||
return ColMap.fromIntArray(np.fromstring(img.make_blob("RGB"), dtype='uint{}'.format(img.depth)).reshape(img.height, img.width, 3))
|
||||
|
||||
@staticmethod
|
||||
def fromBinary(binData, fmt, width=None, height=None) :
|
||||
'''
|
||||
Using the Wand blob functionality, creates a ColMap from binary data. Set binData to sys.stdin.buffer.read() to activate piping!
|
||||
'''
|
||||
with wand.image.Image(blob=binData, format=fmt, width=width, height=height) as img:
|
||||
return ColMap.fromIntArray(np.fromstring(img.make_blob("RGB"), dtype='uint{}'.format(img.depth)).reshape(img.height, img.width, 3))
|
||||
|
||||
def toBinary(self, fmt, depth=16) :
|
||||
'''
|
||||
Using Wand blob functionality
|
||||
'''
|
||||
with self.asWandImg(depth) as img :
|
||||
img.format = fmt
|
||||
return img.make_blob()
|
||||
return ColMap.fromArray(np.fromstring(img.make_blob("RGB"), dtype='uint{}'.format(img.depth)).reshape(img.height, img.width, 3))
|
||||
|
||||
def save(self, path, compress = None, depth = None) :
|
||||
'''
|
||||
Save the image. The filetype will be inferred from the path, and the appropriate backend will be used.
|
||||
Save a ColMap to an image file on the disk.
|
||||
|
||||
Compression scheme will be applied based on the backend compatiblity. Wand compression types can be used: Browse then
|
||||
at http://docs.wand-py.org/en/0.4.3/wand/image.html#wand.image.COMPRESSION_TYPES .
|
||||
:param str path: The path to save the image file at. The extension specified determines the output format.
|
||||
:param compress: Compression options passed to the vendor. Currently broken.
|
||||
:type compress: str or None
|
||||
:param depth: You may override the ColMap's depth if you wish.
|
||||
:type depth: int or None
|
||||
|
||||
|
||||
ColMap currently uses ImageMagick to save a wide range of formats, including:
|
||||
|
||||
* **EXR**: The industry standard for HDR, wide-gamut, linear-encoded images.
|
||||
* **DPX**: An older production format.
|
||||
* **PNG**: Can store 16-bit images well. Usually quite slow.
|
||||
* *Any other IM-supported formats...* See https://www.imagemagick.org/script/formats.php
|
||||
|
||||
**NOTE: EXRs are only saveable as 16-bit integer, with no compression options. This is an IM/Wand library limitation.**
|
||||
'''
|
||||
if depth is None: depth = 16
|
||||
|
||||
if depth not in ColMap.DEPTHS.values :
|
||||
raise ValueError('Bit depth not supported! Supported bit depths: {}'.format(', '.join(ColMap.DEPTHS.values)))
|
||||
|
||||
try :
|
||||
saveFunction = {
|
||||
"exr" : self.saveWand,
|
||||
|
@ -126,9 +233,24 @@ class ColMap :
|
|||
#Fallback to saving using Wand.
|
||||
self.saveWand(path, compress, depth)
|
||||
|
||||
#Vendor-specific save methods
|
||||
|
||||
def saveWand(self, path, compress = None, depth = 16) :
|
||||
#Vendor-specific save methods
|
||||
|
||||
def saveWand(self, path, compress = None, depth = None) :
|
||||
'''
|
||||
Vendor-specific :py:func:`~ColMap.save` function. See :py:func:`~ColMap.save`
|
||||
|
||||
:param str path: The image path to save to.
|
||||
:param compress: Compression options passed to Wand. Currently broken.
|
||||
:param depth: You may override the ColMap's depth if you wish.
|
||||
:type depth: int or None
|
||||
|
||||
**NOTE: EXRs are only saveable as 16-bit integer, with no compression options. This is an IM/Wand library limitation.**
|
||||
'''
|
||||
|
||||
if depth not in ColMap.DEPTHS.values :
|
||||
raise ValueError('Bit depth not supported! Supported bit depths: {}'.format(', '.join(ColMap.DEPTHS.values)))
|
||||
|
||||
data = self.apply(LUT.lutFunc(gamma.sRGB)) if path[path.rfind('.')+1:] == 'dpx' else self
|
||||
i = data.asWandImg(depth)
|
||||
|
||||
|
@ -142,16 +264,17 @@ class ColMap :
|
|||
|
||||
i.save(filename=path)
|
||||
|
||||
#~ def saveSci(self, path, compress = None, depth = 16) :
|
||||
#~ if compress is not None: raise ValueError('Scipy Backend cannot compress the output image!')
|
||||
#~ si.io.imsave(path, self.asIntArray())
|
||||
|
||||
#Display Functions
|
||||
#Display Functions
|
||||
|
||||
@staticmethod
|
||||
def display(path, width = 1000) :
|
||||
'''
|
||||
Shows an image at a path without making a ColMap.
|
||||
Display an image at a path on the disk, using the builtin OpenGL Viewer.
|
||||
|
||||
:param width: The desired width of the viewer; the height is automatically gleaned from the aspect ratio.
|
||||
|
||||
For the viewer source code, see :py:class:`~Viewer`.
|
||||
'''
|
||||
|
||||
img = ColMap.open(path).rgbArr
|
||||
|
@ -163,38 +286,101 @@ class ColMap :
|
|||
Viewer.run(img, xRes, yRes, title = os.path.basename(path))
|
||||
|
||||
def show(self, width = 1000) :
|
||||
'''
|
||||
Display this ColMap using the builtin OpenGL Viewer.
|
||||
|
||||
:param width: The desired width of the viewer; the height is automatically gleaned from the aspect ratio.
|
||||
|
||||
For the viewer source code, see :py:class:`~Viewer`.
|
||||
'''
|
||||
|
||||
#Use my custom OpenGL viewer!
|
||||
Viewer.run(self.rgbArr, width, int(width * self.rgbArr.shape[0]/self.rgbArr.shape[1]))
|
||||
|
||||
@staticmethod
|
||||
def wandShow(wandImg) :
|
||||
#Do a quick sRGB transform for viewing. Must be in 'rgb' colorspace for this to take effect.
|
||||
wandImg.transform_colorspace('srgb')
|
||||
|
||||
wand.display.display(wandImg)
|
||||
#Data Output Types
|
||||
def asWandImg(self, depth = None) :
|
||||
'''
|
||||
Output this ColMap as a Wand image.
|
||||
|
||||
wandImg.transform_colorspace('rgb') #This transforms it back to linearity.
|
||||
:param depth: You may override the ColMap's depth if you wish.
|
||||
:type depth: int or None
|
||||
:return: The Wand Image.
|
||||
:rtype: wand.image
|
||||
|
||||
See http://docs.wand-py.org/en/0.4.4/index.html for Wand docs.
|
||||
'''
|
||||
|
||||
if depth not in ColMap.DEPTHS.values :
|
||||
raise ValueError('Bit depth not supported! Supported bit depths: {}'.format(', '.join(ColMap.DEPTHS.values)))
|
||||
|
||||
if depth is None :
|
||||
d = ColMap.DEPTHS['half'] if self.depth >= ColMap.DEPTHS['half'] else self.depth #Highest is half - 16.
|
||||
else :
|
||||
d = depth
|
||||
|
||||
#Data Form Functions
|
||||
def asWandImg(self, depth = 16) :
|
||||
#~ i = wand.image.Image(blob=self.asarray().tostring(), width=np.shape(self.rgbArr)[1], height=np.shape(self.rgbArr)[0], format='RGB') #Float Array
|
||||
i = wand.image.Image(blob=self.asIntArray(depth).tostring(), width=np.shape(self.rgbArr)[1], height=np.shape(self.rgbArr)[0], format='RGB')
|
||||
i = wand.image.Image(blob=self.asIntArray(d).tostring(), width=np.shape(self.rgbArr)[1], height=np.shape(self.rgbArr)[0], format='RGB')
|
||||
i.colorspace = 'rgb' #Specify, to Wand, that this image is to be treated as raw, linear, data.
|
||||
|
||||
return i
|
||||
|
||||
|
||||
def toBinary(self, fmt, depth=None) :
|
||||
'''
|
||||
Output this ColMap in binary form. See :py:func:`~ColMap.fromBinary` for the inverse.
|
||||
|
||||
:param str fmt: Wand needs to know what format to output! See https://www.imagemagick.org/script/formats.php .
|
||||
:param depth: You may override the ColMap's bit depth if you wish.
|
||||
:type depth: int or None
|
||||
:return: The image, as a ColMat.
|
||||
:rtype: :py:class:`~ColMap`
|
||||
|
||||
This is great for pipes, where you're sending binary data through stdout.
|
||||
* Use the return value as the argument of sys.stdout.write() to pipe the image to other applications!
|
||||
|
||||
**NOTE: Uses Wand's "blob" functionality, and as such incurs Wand's limitations.**
|
||||
'''
|
||||
|
||||
if depth not in ColMap.DEPTHS.values :
|
||||
raise ValueError('Bit depth not supported! Supported bit depths: {}'.format(', '.join(ColMap.DEPTHS.values)))
|
||||
|
||||
with self.asWandImg(d) as img :
|
||||
img.format = fmt
|
||||
return img.make_blob()
|
||||
|
||||
def asarray(self) :
|
||||
"""
|
||||
Returns the base float array.
|
||||
Returns the internal np.float32 image array directly.
|
||||
|
||||
:return: The internal numpy array.
|
||||
:rtype: np.array
|
||||
"""
|
||||
return self.rgbArr
|
||||
|
||||
def asIntArray(self, depth = 16, us = True) :
|
||||
u = 'u' if us else ''
|
||||
def asIntArray(self, depth = None, us = True) :
|
||||
"""
|
||||
Returns the internal image array as an int array.
|
||||
|
||||
:param depth: You may override the ColMap's bit depth if you wish.
|
||||
:type depth: int or None
|
||||
:param bool us: True will output unsigned ints, False will output signed ints.
|
||||
:return: The internal numpy array.
|
||||
:rtype: np.array
|
||||
"""
|
||||
|
||||
if depth not in ColMap.DEPTHS.values :
|
||||
raise ValueError('Bit depth not supported! Supported bit depths: {}'.format(', '.join(ColMap.DEPTHS.values)))
|
||||
|
||||
if depth is None :
|
||||
d = self.depth #No limits here.
|
||||
else :
|
||||
d = depth
|
||||
|
||||
u = 'u' if us else '' #Unsigned or no?
|
||||
return np.multiply(self.rgbArr.clip(0, 1), 2.0 ** depth - 1).astype("{0}int{1}".format(u, depth))
|
||||
|
||||
|
||||
#Overloads
|
||||
#Overloads
|
||||
def __repr__(self) :
|
||||
return 'ColMap.fromArray( \n\trgbArr = {0}\n)'.format('\n\t\t'.join([line.strip() for line in repr(self.rgbArr).split('\n')]))
|
||||
|
|
|
@ -69,29 +69,27 @@ class LUT(Transform) :
|
|||
'''
|
||||
return LUT.lutArray(splev(np.linspace(0, 1, num=len(idArr)), splrep(idArr, mapArr)))
|
||||
|
||||
#LUT Functions.
|
||||
#Transform Functions.
|
||||
def _splInterp(q, cpu, spSeq, ID, array) :
|
||||
q.put( (cpu, splev(spSeq, splrep(ID, array))) ) #Spline Interpolation. Pretty quick, considering.
|
||||
|
||||
def sample(self, fSeq, spl=True) :
|
||||
'''
|
||||
Sample the LUT using a flat float sequence (ideally a numpy array; (0..1) ).
|
||||
Apply the 1D LUT to the numpy image array, using fast C++ math.
|
||||
|
||||
Each n (dimensions) clump of arguments will be used to sample the LUT. So:
|
||||
1D LUT: in1, in2, in3 --> out1, out2, out3
|
||||
*Min 1 argument.
|
||||
Latest Performance:
|
||||
apply(ol.LUT): 0.026462205679908948,, (avg. 100 Trials) *sRGB LUT
|
||||
|
||||
3D LUT: inR, inG, inB --> outR, outG, outB
|
||||
*Min 3 arguments, len(arguments) % 3 must equal 0.
|
||||
|
||||
Returns a numpy array with identical shape to the input array.
|
||||
:return: Returns a numpy array with identical shape to the input array.
|
||||
'''
|
||||
|
||||
fSeq = np.array(fSeq)
|
||||
if self.dims == 1 :
|
||||
#If scipy isn't loaded, we can't use spline interpolation!
|
||||
if (not MOD_SCIPY) or self.size > 25 : # Auto-adapts all but the smallest LUTs to use the faster linear interpolation.
|
||||
|
||||
#Scipy must be loaded & the LUT must be rediculously small before spline interpolation sets in.
|
||||
if (not MOD_SCIPY) or self.size > 25 :
|
||||
return olo.lut1dlin(fSeq.reshape(reduce(lambda a, b: a*b, fSeq.shape)), self.array, self.range[0], self.range[1]).reshape(fSeq.shape)
|
||||
|
||||
else :
|
||||
#~ return np.interp(spSeq, self.ID, self.array) #non-threaded way.
|
||||
out = []
|
||||
|
@ -109,6 +107,7 @@ class LUT(Transform) :
|
|||
elif self.dims == 3 :
|
||||
print("3D LUT Not Implemented!")
|
||||
|
||||
#LUT Functions
|
||||
def resized(self, newSize) :
|
||||
'''
|
||||
Return the LUT, resized to newSize.
|
||||
|
|
|
@ -144,6 +144,35 @@ py::array_t<float> matr(py::array_t<float> img, py::array_t<float> mat) {
|
|||
}
|
||||
}
|
||||
|
||||
//grey_to_rgb takes a flattened greyscale image array and outputs a flattened numpy image array.
|
||||
py::array_t<float> grey_to_rgb(py::array_t<float> arr) {
|
||||
py::buffer_info bufIn = arr.request();
|
||||
|
||||
//To use with an image, MAKE SURE to flatten the 3D array to a 1D array, then back out to a 3D array after.
|
||||
if (bufIn.ndim == 1) {
|
||||
//Make numpy allocate the buffer.
|
||||
auto result = py::array_t<float>(bufIn.size * 3); //Size is multiplied by 3 - we're outputting RGB!
|
||||
|
||||
//Get the pointers that we can manipulate from C++.
|
||||
auto bufOut = result.request();
|
||||
|
||||
float *ptrIn = (float *) bufIn.ptr,
|
||||
*ptrOut = (float *) bufOut.ptr;
|
||||
|
||||
//The reason for all this bullshit as opposed to vectorizing is this pragma!!!
|
||||
#pragma omp parallel for
|
||||
for (size_t i = 0; i < bufOut.shape[0]; i+=3) {
|
||||
float val = ptrIn[(i+1)/3 - 1]; //Little bit of indexing math to get the value; remember we're skipping by threes.
|
||||
|
||||
ptrOut[i] = val;
|
||||
ptrOut[i + 1] = val;
|
||||
ptrOut[i + 2] = val;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -164,6 +193,12 @@ PYBIND11_PLUGIN(olOpt) {
|
|||
py::arg("mat")
|
||||
);
|
||||
|
||||
mod.def( "grey_to_rgb",
|
||||
&grey_to_rgb,
|
||||
"Takes a flattened 2D greyscale image array and outputs a flattened 3D numpy image array.",
|
||||
py::arg("arr")
|
||||
);
|
||||
|
||||
mod.def( "lut1dlin",
|
||||
&lut1dlin,
|
||||
"Apply any 1D LUT to a flattened numpy image array; vectorized & parallel.",
|
||||
|
|
Loading…
Reference in New Issue