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()

Friday, January 16, 2015

material

I made this material by breaking up cotton balls very finely and coating them with graphite powder, then mixing that up with 2 part epoxy and forming it. It is really impressively hard and strong. Beating on it with a hammer on the driveway doesn't leave any visible marks (and makes such a loud noise I might make a video), and cutting into it with a knife only makes a slight mark in the outermost surface probably just into the epoxy coating...

**After polishing the outer surface flat with Aluminum Oxide Sand paper, after the surface was flattened it smoothed the sand paper instead of further smoothing the material
I found I was able to drill holes through it without compromising the strength of the bulk structure or it breaking into pieces around the hole...

Wednesday, January 14, 2015

Sort worm

A sort worm is a creature I made whose genes work on a list, this one I made works on lists of 10 items like so:
[2, 8, 7, 4, 5, 3, 6, 9, 0, 1]
I chose to use 25 genes as being roughly 10*log(10) of the form:
[a,b] where a and b are positions on the list and the gene is expressed by comparing items in the list at those positions and swapping them if the number at the lower position is greater than the one at the higher position. "His" dna is:
[[1, 4], [7, 9], [2, 6], [0, 7], [1, 7], [6, 8], [3, 5], [3, 8], [2, 4], [2, 3], [5, 7], [0, 6], [6, 9], [8, 9], [0, 2], [3, 6], [4, 8], [0, 1], [5, 6], [7, 9], [4, 6], [1, 3], [4, 7], [6, 7], [3, 5]]

So working on the given list:
[2, 8, 7, 4, 5, 3, 6, 9, 0, 1] original list
[2, 5, 7, 4, 8, 3, 6, 9, 0, 1] swapped items 1 and 4 (0 indexed) because the rightmost is less than the leftmost of the two
[2, 5, 7, 4, 8, 3, 6, 1, 0, 9] swapped 7 and 9...
[2, 5, 6, 4, 8, 3, 7, 1, 0, 9] etc...
[1, 5, 6, 4, 8, 3, 7, 2, 0, 9]
[1, 2, 6, 4, 8, 3, 7, 5, 0, 9]
[1, 2, 6, 4, 8, 3, 0, 5, 7, 9]
[1, 2, 6, 3, 8, 4, 0, 5, 7, 9]
[1, 2, 6, 3, 8, 4, 0, 5, 7, 9]
[1, 2, 6, 3, 8, 4, 0, 5, 7, 9]
[1, 2, 3, 6, 8, 4, 0, 5, 7, 9]
[1, 2, 3, 6, 8, 4, 0, 5, 7, 9]
[0, 2, 3, 6, 8, 4, 1, 5, 7, 9]
[0, 2, 3, 6, 8, 4, 1, 5, 7, 9]
[0, 2, 3, 6, 8, 4, 1, 5, 7, 9]
[0, 2, 3, 6, 8, 4, 1, 5, 7, 9]
[0, 2, 3, 1, 8, 4, 6, 5, 7, 9]
[0, 2, 3, 1, 7, 4, 6, 5, 8, 9]
[0, 2, 3, 1, 7, 4, 6, 5, 8, 9]
[0, 2, 3, 1, 7, 4, 6, 5, 8, 9]
[0, 2, 3, 1, 7, 4, 6, 5, 8, 9]
[0, 2, 3, 1, 6, 4, 7, 5, 8, 9]
[0, 1, 3, 2, 6, 4, 7, 5, 8, 9]
[0, 1, 3, 2, 5, 4, 7, 6, 8, 9]
[0, 1, 3, 2, 5, 4, 6, 7, 8, 9]
This finished result is pretty typical that 2 items are 1 position out of order...
A lot of times it gets it perfectly and the worst I've seen it do is 4 items needing to be swapped 1 position.
Testing this dna on 100 random lists gives that this will be the average result.

**finding the dna**
To find this above sequence of dna I:
1. Created 10 sort worms with random dna
Then repeat these steps below for 1000 generations
2. "Breed" all worms each worm with every other, where every new worm gets each gene from one of its parents to make its dna.
3. Reduce this list of worms to the 5 best performers as the average root mean squared of how out of order the list is after he uses his dna on 100 random lists.
4. Add 5 more random worms to the 5 you're left with.

At the end this worm above was the best performer.
**Source Code**
import random
class creature():
def __init__(self):
self.dna = []
def new(self, dnalength, listlength):
self.dnalength = dnalength
self.listlength = listlength
crudelist = []
for i in range(0, listlength-1):
for j in range(i+1, listlength):
crudelist.append([i,j])
random.shuffle(crudelist)
self.dna = crudelist[0:dnalength]
def childof(self, a, b):
self.dnalength = a.dnalength
self.listlength = a.listlength
for i in range(0, len(a.dna)):
g = random.randint(0, 1)
if g == 0:
self.dna.append(a.dna[i])
else:
self.dna.append(b.dna[i])
def statictest(self, numtimes, testlist):
avep = 0
for i in range(0, numtimes):
testlist = self.usegenes(testlist)
p = self.checklist(testlist)
avep += p
return avep*1.0 / numtimes
def testme(self, numtimes):
avep = 0
for i in range(0, numtimes):
testlist = []
for j in range(0, self.listlength):
testlist.append(random.random())
testlist = self.usegenes(testlist)
p = self.checklist(testlist)
avep += p
return avep*1.0 / numtimes
def checklist(self, testlist):
totalscore = 0
for i in range(0, len(testlist)):
lessthan = 0
for j in range(0, len(testlist)):
if testlist[j] < testlist[i]:
lessthan +=1
score = (i - lessthan)**2.0
totalscore +=score
def usegenes(self, testlist):
for j in range(0, self.dnalength):
if testlist[self.dna[j][0]] > testlist[self.dna[j][1]]:
temp = testlist[self.dna[j][0]]
testlist[self.dna[j][0]] = testlist[self.dna[j][1]]
testlist[self.dna[j][1]] = temp
return testlist
def main():
creatures = []
numcreatures = 400
survivors = 5
dnalength = 25
listlength = 10
numtests = 100
generations = 3000
testlist = []
performance = []
for j in range(0, listlength):
testlist.append(random.random())
for i in range(0, numcreatures):
c = creature()
c.new(dnalength, listlength)
creatures.append(c)

for i in range(0, generations):
performance = []
creatures2 = []
for j in range(0, len(creatures)):
c = creatures[j]
performance.append([c.testme(numtests), j])
performance = sorted(performance)
for j in range(0, survivors):
creatures2.append(creatures[performance[j][1]])
for j in range(0, survivors):
c = creature()
c.new(dnalength, listlength)
creatures2.append(c)
creatures = creatures2
max = len(creatures)
for k in range(0, max-1):
for l in range(k+1, max):
c = creature()
c.childof(creatures[k], creatures[l])
creatures.append(c)
print(performance)
print(creatures[0].dna)
main()

Thursday, January 1, 2015

Limit vs. Limit graphs to analyze infinite series

f(x) = 1/x^3 as x goes to infinity will reach 0, and the integral from 1 to infinity will reach 1/2. I find it interesting to parameterize a 2 variable curve where one variable s is the function's value at t and the other r is the partial integral from 1 up to t, and make a plot of s vs. r like below.
This shows the behavior of f(x) over the entire x axis from 1 to infinity as it relates to the integral to that point.

It leads to an interesting proof of the infinite sum of geometric series... Here I plot the sum of 1/2^n up to t vs. the value of each individual term at t...
The value of the first term is 1 and the sum of just that term is of course 1, and the next term is 1/2 and the sum of the first two terms is 1.5, and it precedes linearly all the way to a term being 0 and the sum of the infinite terms being 2. But if you know all the points lie on the same line you can use just the first two terms to derive a line that shows ultimately the infinite sum will reach the r axis at 1/(1-1/2). And generalize that to any geometric series.