<p>There is a small issue in the original code. Indeed, real array values ranging between [0, 255]. However, the mask values ranging between [0, 3*3] (as dn = 3 in the example above).</p>

<p>After fixing the issue:
<a href="https://i.sstatic.net/KUBRK.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/KUBRK.png" alt="enter image description here"></a></p>

<p>The correct code (with image reading and all data normalized between [0, 1.0]):</p>

<pre><code>import numpy as np
from numpy.random import default_rng
import matplotlib.pyplot as plt
from PIL import Image

# Read an image from the disk
real = Image.open("example.jpeg")
real = np.array(real) / 255.0
# If it is a RGB image, transform it to a gray image
# TODO: Could use any function (like luminance)
real = (real[:,:,0] + real[:,:,1] + real[:,:,2]) / 3.0

# Create the mask by shuffling
dn = 6
rng = default_rng()
# In the original code, there was a problem here.
# Indeed, the mask values ranging between [0, dn*dn]
# So the generated mask needs to be properly normalized
dither = rng.choice(dn*dn, size=(dn,dn), replace=False) / float(dn*dn)
print(dither)

# Generate the results
res = np.zeros_like(real)
width, height = res.shape[1], res.shape[0]
for y in range (0,width):
    for x in range (0,height):
        i = x % dn
        j = y % dn
        res[x][y] = 1.0 if (real[x][y] &gt; dither[i][j]) else 0.0

# Display the result
fig = plt.figure()
ax1 = plt.subplot(1, 3, 1)
ax1.title.set_text('Real Image(300x300)')
plt.imshow(real, cmap="gray")
ax2 = plt.subplot(1, 3, 2)
ax2.title.set_text('Dithering Matrix(3x3)')
plt.imshow(dither, cmap="gray")
ax3 = plt.subplot(1, 3, 3)
ax3.title.set_text('Resultant Image(300x300)')
plt.imshow(res, cmap="gray")
plt.show()
</code></pre>

CC BY-SA 4.0
2020-02-09T16:34:03.540Z
what are you doing with `real = (real[:,:,0] + real[:,:,1] + real[:,:,2]) / 3.0`
Maifee Ul Asad
CC BY-SA 4.0
2020-02-09T22:50:18.483Z
It is a RGB to gray color coversion (here average the three color channel). Indeed, you need to convert the image before applying dithering. If you have only scalar values (as your original example), you do not need to do this conversion.
Beltegeuse
CC BY-SA 4.0
2020-02-08T20:20:03.823Z
2020-02-08T20:20:03.823Z
Beltegeuse
<p>Is just read about image-dithering ? So I tried to implement that. Here is my result. Can someone verify it?</p>

<p><a href="https://github.com/maifeeulasad/dithering-cg/blob/master/python/render.py" rel="nofollow noreferrer">My code</a> :</p>

<pre><code>import numpy as np
from numpy.random import default_rng
import matplotlib.pyplot as plt

n = 300
real = np.random.randint(low = 0, high=256, size=(n,n), dtype='l')
#print(real)

dn = 3
rng = default_rng()
dither = rng.choice(dn*dn, size=(dn,dn), replace=False)
print(dither)

res = np.zeros((n,n))
for y in range (0,n):
    for x in range (0,n):
        i = x % dn
        j = y % dn
        res[x][y] = 255 if (real[x][y] &gt; dither[i][j]) else 0
#print(res)


fig = plt.figure()

ax1 = plt.subplot(1, 3, 1)
ax1.title.set_text('Real Image(300x300)')
plt.imshow(real, cmap="gray")

ax2 = plt.subplot(1, 3, 2)
ax2.title.set_text('Dithering Matrix(3x3)')
plt.imshow(dither, cmap="gray")

ax3 = plt.subplot(1, 3, 3)
ax3.title.set_text('Resultant Image(300x300)')
plt.imshow(res, cmap="gray")

plt.show()
</code></pre>

<p>reduced value of  n to 6, so that image may become easily understandable.</p>

<p>Result :
<a href="https://i.sstatic.net/kOxfb.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/kOxfb.png" alt="enter image description here"></a></p>

CC BY-SA 4.0
2020-02-07T07:57:09.760Z
It might be helpful to use a much higher resolution input image, at 6x6 it's not east to see what's happening across a range of brightnesses
PaulHK
CC BY-SA 4.0
2020-02-07T09:04:41.890Z
[here](https://i.sstatic.net/qEKEG.png) is a snap for 90x90, seems abnormal.. that's why asking ... can you verify the code..
`for y in range (0,n):
    for x in range (0,n):
        i = x % dn
        j = y % dn
        res[x][y] = 255 if (real[x][y] > dither[i][j]) else 0`
Maifee Ul Asad
CC BY-SA 4.0
2020-02-08T10:34:23.410Z
Your algorithm looks sound, but I'm not a python programmer, so I can't say if there is a logic problem in your code, I'm not sure about your use of range(0,n) in both the X/Y for loops? Maybe a better test image would be a vertical gradient (say 256x256) so you can see how it behaves across an entire range of inputs.
PaulHK
<image>
basic of image dithering
Maifee Ul Asad
# basic of image dithering

(Asked by: username_0 on 2020-02-06)

Is just read about image-dithering ? So I tried to implement that. Here is my result. Can someone verify it?

[My code](https://github.com/username_0/dithering-cg/blob/master/python/render.py) :

```
import numpy as np
from numpy.random import default_rng
import matplotlib.pyplot as plt

n = 300
real = np.random.randint(low = 0, high=256, size=(n,n), dtype='l')
#print(real)

dn = 3
rng = default_rng()
dither = rng.choice(dn*dn, size=(dn,dn), replace=False)
print(dither)

res = np.zeros((n,n))
for y in range (0,n):
    for x in range (0,n):
        i = x % dn
        j = y % dn
        res[x][y] = 255 if (real[x][y] > dither[i][j]) else 0
#print(res)

fig = plt.figure()

ax1 = plt.subplot(1, 3, 1)
ax1.title.set_text('Real Image(300x300)')
plt.imshow(real, cmap="gray")

ax2 = plt.subplot(1, 3, 2)
ax2.title.set_text('Dithering Matrix(3x3)')
plt.imshow(dither, cmap="gray")

ax3 = plt.subplot(1, 3, 3)
ax3.title.set_text('Resultant Image(300x300)')
plt.imshow(res, cmap="gray")

plt.show()

```

reduced value of n to 6, so that image may become easily understandable.

Result :
[![enter image description here](https://i.sstatic.net/kOxfb.png)](https://i.sstatic.net/kOxfb.png)

**Tags:** image

> **Comments:**
> **username_1**: It might be helpful to use a much higher resolution input image, at 6x6 it's not east to see what's happening across a range of brightnesses  
> **username_0**: [here](https://i.sstatic.net/qEKEG.png) is a snap for 90x90, seems abnormal.. that's why asking ... can you verify the code.. `for y in range (0,n):     for x in range (0,n):         i = x % dn         j = y % dn         res[x][y] = 255 if (real[x][y] > dither[i][j]) else 0`  
> **username_1**: Your algorithm looks sound, but I'm not a python programmer, so I can't say if there is a logic problem in your code, I'm not sure about your use of range(0,n) in both the X/Y for loops? Maybe a better test image would be a vertical gradient (say 256x256) so you can see how it behaves across an entire range of inputs.  

---

## 1 answers

### Answer by username_2 on 2020-02-08. Score: 1 | Accepted answer

There is a small issue in the original code. Indeed, real array values ranging between [0, 255]. However, the mask values ranging between [0, 3\*3] (as dn = 3 in the example above).

After fixing the issue:
[![enter image description here](https://i.sstatic.net/KUBRK.png)](https://i.sstatic.net/KUBRK.png)

The correct code (with image reading and all data normalized between [0, 1.0]):

```
import numpy as np
from numpy.random import default_rng
import matplotlib.pyplot as plt
from PIL import Image

# Read an image from the disk
real = Image.open("example.jpeg")
real = np.array(real) / 255.0
# If it is a RGB image, transform it to a gray image
# TODO: Could use any function (like luminance)
real = (real[:,:,0] + real[:,:,1] + real[:,:,2]) / 3.0

# Create the mask by shuffling
dn = 6
rng = default_rng()
# In the original code, there was a problem here.
# Indeed, the mask values ranging between [0, dn*dn]
# So the generated mask needs to be properly normalized
dither = rng.choice(dn*dn, size=(dn,dn), replace=False) / float(dn*dn)
print(dither)

# Generate the results
res = np.zeros_like(real)
width, height = res.shape[1], res.shape[0]
for y in range (0,width):
    for x in range (0,height):
        i = x % dn
        j = y % dn
        res[x][y] = 1.0 if (real[x][y] > dither[i][j]) else 0.0

# Display the result
fig = plt.figure()
ax1 = plt.subplot(1, 3, 1)
ax1.title.set_text('Real Image(300x300)')
plt.imshow(real, cmap="gray")
ax2 = plt.subplot(1, 3, 2)
ax2.title.set_text('Dithering Matrix(3x3)')
plt.imshow(dither, cmap="gray")
ax3 = plt.subplot(1, 3, 3)
ax3.title.set_text('Resultant Image(300x300)')
plt.imshow(res, cmap="gray")
plt.show()

```

> **Comments:**
> **username_0**: what are you doing with `real = (real[:,:,0] + real[:,:,1] + real[:,:,2]) / 3.0`  
> **username_2**: It is a RGB to gray color coversion (here average the three color channel). Indeed, you need to convert the image before applying dithering. If you have only scalar values (as your original example), you do not need to do this conversion.  

---

