analytics

Wednesday, January 21, 2015

2 tone and 3 bit dithering with inverse square brightness


The above picture uses only the colors full white and full black.
The way I did it is to start with an array of boolean values the size of the picture above all set to true. Then make a list of every [x,y] value for all the pixels, and scramble the order of them. Then go through this scrambled list of values and look in the array of booleans at the truth value of any cell within a distance 5 of the pixel in question. A sum is kept of the inverses of the distances^2 of all the cells that are true out of those. The maximum this could come out to is 12.76. So the ratio of what the sum comes out to divided by 12.76 is compared to the ratio of the brightness of the grayscale image at that pixel divided by 255 (the maximum brightness). If the first ratio is greater than the second the array of booleans is set to False at that [x,y]. Then the array is converted to an image with True values making a pixel white and false black.

**Python Source Code**
from PIL import Image
import random
def main():
    im = Image.open("lion.png")
    map=[]
    for i in range(0, im.size[1]):
        row = []
        for j in range(0, im.size[0]):
            row.append(True)
        map.append(row)
    pixels = []
    for i in range(0, im.size[0]):
        for j in range(0, im.size[1]):
            pixels.append([j,i])
    random.shuffle(pixels)
 
    for p in pixels:
        sumbright = 0
     
        for x in range(-5, 6):
            for y in range(-5, 6):
                if x!=0 or y !=0:
                    if p[0] +x >= 0 and p[0]+x < len(map):
                        if p[1]+y >=0 and p[1]+y < len(map[0]):
                            d = (x**2.0 + y**2.0)
                            if d**.5 <= 5:
                                if map[p[0]+x][p[1]+y] == True:
                                    sumbright +=1.0/d
                             
                             
                             
        if 1.0*sumbright /12.78  > 1.0*im.getpixel((p[1],p[0])) / 255:
            map[p[0]][p[1]] = False
     
    im2 = Image.new("RGB", (im.size[0], im.size[1]))
    for i in range(0, im.size[0]):
        for j in range(0, im.size[1]):
            if map[j][i] == True:
                im2.putpixel((i,j), (255,255,255))
    im2.save("lion2a.png")
main()


**8 color**
Also modified the code for doing 8 color or 3 bit dithered images, full or no red, full or no green, full or no blue...
Extreme close up:
For comparison, the state of the art is Floyd Steinberg dithering which for 8 colors produces images like this:
I think this way I'm doing it is a big improvement...
**Python Source Code**
from PIL import Image
import random
def main():
    im = Image.open("parrot.png")
    map=[]
    for i in range(0, im.size[1]):
        row = []
        for j in range(0, im.size[0]):
            row.append([True, True, True])
        map.append(row)
    pixels = []
    for i in range(0, im.size[0]):
        for j in range(0, im.size[1]):
            pixels.append([j,i])
    random.shuffle(pixels)
    print(len(map[0]), len(map))
    print(im.size[0], im.size[1])
    for p in pixels:
        sumbrightred = 0
        sumbrightgreen = 0
        sumbrightblue = 0
        for x in range(-5, 6):
            for y in range(-5, 6):
                if x!=0 or y !=0:
                    if p[0] +x >= 0 and p[0]+x < len(map):
                        if p[1]+y >=0 and p[1]+y < len(map[0]):
                            d = (x**2.0 + y**2.0)
                            if d**.5 <= 5:
                                try:
                                    if map[p[0]+x][p[1]+y][0] == True:
                                        sumbrightred +=1.0/d
                                    if map[p[0]+x][p[1]+y][1] == True:
                                        sumbrightgreen +=1.0/d
                                    if map[p[0]+x][p[1]+y][2] == True:
                                        sumbrightblue +=1.0/d
                                except:
                                    print(p[0]+x, p[1]+y)
                               
        if 1.0*sumbrightred /12.78  > 1.0*im.getpixel((p[1],p[0]))[0] / 255:
            map[p[0]][p[1]][0] = False
        if 1.0*sumbrightgreen /12.78  > 1.0*im.getpixel((p[1],p[0]))[1] / 255:
            map[p[0]][p[1]][1] = False
        if 1.0*sumbrightblue /12.78  > 1.0*im.getpixel((p[1],p[0]))[2] / 255:
            map[p[0]][p[1]][2] = False
    im2 = Image.new("RGB", (im.size[0], im.size[1]))
    for i in range(0, im.size[0]):
        for j in range(0, im.size[1]):
            color = [0,0,0]
            if map[j][i][0] == True:
                color[0] = 255
            if map[j][i][1] == True:
                color[1] = 255
            if map[j][i][2] == True:
                color[2] = 255
            im2.putpixel((i,j), tuple(color))
    im2.save("parrot2.png")
main()

1 comment:

  1. Hi Ben,

    Thank you for sharing. The pictures you posted are awesome and I was very excited to try your conversion scripts. Unfortunately, my results were not so good. In the case of the black and white conversion, apparently, Steinberg performed a little bit better.

    Please check the pictures below and tell what you think. Is there any tip in choosing my base picture to improve my results?

    Base picture:
    https://drive.google.com/file/d/0B-FqW8ub8Zykb1R3Zy1zM29Zczg/view?usp=sharing

    Greyscale:
    https://drive.google.com/file/d/0B-FqW8ub8ZykQy1pcDNlR0dWUk0/view?usp=sharing

    Inverse Square Brightness:
    https://drive.google.com/file/d/0B-FqW8ub8ZykeVVhRHNVU1ppcGM/view?usp=sharing

    Steinberg:
    https://drive.google.com/file/d/0B-FqW8ub8ZykYS1wTmQxdWdGbjA/view?usp=sharing

    ReplyDelete