Vectorizing sRGB to linear conversion properly in Numpy
<p>In my image editing app I have a function for converting a 32 bit float image from sRGB to linear color space. The formula is:</p>

<pre><code>if value &lt;= 0.04045: (value / 12.92)
if value &gt; 0.04045: ((value + 0.055) / 1.055)^2.4)
</code></pre>

<p>My image is a three-dimensional <strong>numpy.ndarray</strong> named <strong>img32</strong>.</p>

<p>My implementation so far:</p>

<pre><code>boolarray = img32 &lt;= 0.04045
lower = (img32 / 12.92) * boolarray.astype(np.int)
upper = np.power(((img32 + 0.055) / 1.055), 2.4) * np.invert(boolarray).astype(np.int)
img32 = lower + upper
</code></pre>

<p>So, I am creating a new array <strong>boolarray</strong> with truth values for &lt;= 0.04045 and multiply by that.</p>

<p>What would be a better solution?</p>

<p>I tried something like:</p>

<pre><code>img32[img32 &lt; 0.04045] = img32 / 12.92
</code></pre>

<p>which works on the first step but fails on the second:</p>

<pre><code>img32[img32 &gt;= 0.04045] = np.power(((img32 + 0.055) / 1.055), 2.4)
</code></pre>

<p>probably because it doesn't work when wrapped in the <strong>np.power</strong> function</p>

<p>Any help is appreciated.</p>

<p>A clean way would be with <a href="https://docs.scipy.org/doc/numpy/reference/generated/numpy.where.html" rel="nofollow"><code>np.where</code></a> that lets us choose between two values based on a mask. In our case, the mask could be <code>img32 &gt;= 0.04045</code> and we will choose <code>((img32 + 0.055) / 1.055)**2.4</code> when <code>True</code>, else go with <code>img32/12.92</code>.</p>

<p>So, we would have an implementation like so -</p>

<pre><code>np.where( img32 &gt;= 0.04045,((img32 + 0.055) / 1.055)**2.4, img32/12.92 )
</code></pre>

<hr>

<p>If you care about memory a lot and would like to write back the results into the input array, you could it in three steps by creating and selectively set elems corresponding to those two conditions, like so -</p>

<pre><code>mask = img32 &gt;= 0.04045
img32[mask] = ((img32[mask] + 0.055) / 1.055)**2.4
img32[~mask] = img32[~mask] / 12.92
</code></pre>

<p>Sample case -</p>

<pre><code>In [143]: img32 = np.random.rand(4,5).astype(np.float32)

In [144]: img32.nbytes
Out[144]: 80

In [145]: mask.nbytes
Out[145]: 20
</code></pre>

<p>So, we are avoiding the creation of an output array that would have costed us <code>80</code> bytes and instead using <code>20</code> bytes on the mask. Thus, making a saving of <code>75%</code> of input array size there on memory. Please note that this might lead to a slight reduction in performance though.</p>

python numpy vectorization color-space srgb