I want to write a function that will create a random number (between m and n, inclusive) of stars in the sky of this picture (http://tinypic.com/r/34il9hu/6). I want the stars should be randomly composed of either a single white pixel, or a square of 4 adjacent white pixels.I also do not want to place a 'star' (of 1 pixel) over the tree branches, the moon or the bird though
How would I do this in python? Can anyone help? Thanks!
I have this so far:
I have started and have come out with this so far, I don't know if any of it is right or even if I am on the right track though:
def randomStars(small, large):
import random
file = pickAFile()
pic = makePicture(myPic)
#x = random.randrange(getWidth(pic))
#y = random.randrange(getHeight(pic))
for pixel in pic.getAllPixels():
if random.random() < 0.25:
pixel.red = random.randint(256)
pixel.green = random.randint(256)
pixel.blue = random.randint(256)
show(pic)
I have no clue what I am doing :(
This looks like a nice example to try the superpixels, as implemented by skimage. You can probably do this in an easier way for your problem.
import urllib
import random
import io
import matplotlib.pyplot as plt
import skimage.segmentation
import pandas
# Read the image
f = io.BytesIO(urllib.urlopen('http://oi46.tinypic.com/34il9hu.jpg').read())
img = plt.imread(f, format='jpg')
# Prefer to keep pixels together based on location
# But not too much, so we still get some branches.
superpixel = skimage.segmentation.slic(img, n_segments=200, ratio=20)
plt.imshow(superpixel%7, cmap='Set2')
Now that we have superpixels we can do classification a bit easier, by doing it per superpixel. You could use some fancy classification here, but this example is quite simple, with a blueish sky, let's do it by hand.
# Create a data frame with the relative blueish of every super pixel
# Convert image to hsv
hsv = matplotlib.colors.rgb_to_hsv(img.astype('float32')/255)
# Define blueish as the percentage of pixels in the blueish range of the hue space
df =pandas.DataFrame({'superpixel':superpixel.ravel(),
'blue':((hsv[:,:,0] > 0.4) & (hsv[:,:,0]<0.8)).astype('float32').ravel(),
'value':hsv[:,:,2].ravel()})
grouped = df.groupby('superpixel').mean()
# Lookup the superpixels with the least blue
blue = grouped.sort('blue', ascending=True).head(100)
# Lookup the darkest pixels
light = grouped.sort('value', ascending=True).head(50)
# If superpixels are too dark or too blue, get rid of them
mask = (np.in1d(superpixel, light.index ).reshape(superpixel.shape) |
np.in1d(superpixel, blue.index ).reshape(superpixel.shape))
# Now we can put the stars on the blueish, not too darkish areas
def randomstar(img, mask):
"""random located star"""
x,y = random.randint(1,img.shape[0]-1), random.randint(1,img.shape[1]-1)
if not mask[x-1:x+1, y-1:y+1].any():
# color not so random
img[x,y,:] = 255
img[x-1,y,:] = 255
img[x+1,y,:] = 255
img[x,y-1,:] = 255
img[x,y+1,:] = 255
for i in range(100):
randomstar(img, mask)
plt.imshow(img)
Python's standard library doesn't come with any powerful-enough image-manipulation code, but there are a few alternatives that are easy to install and use. I'll show how to do this with PIL.
from PIL import Image
def randomStars(small, large):
import random
filename = pickAFile()
pic = Image.open(filename)
max_x, max_y = pic.size
pixels = im.load()
x = random.randrange(max_x)
y = random.randrange(max_y)
for i in range(max_x):
for j in range(max_y):
if random.random() < 0.25:
red = random.randint(256)
green = random.randint(256)
blue = random.randint(256)
pixels[i, j] = (red, green, blue, 1)
im.show()
The show
function doesn't display the image in your app (for that, you'd need some kind of GUI with an event loop, like tkinter
or PySide
); it saves a file to a temporary directory and runs a platform-specific program like Preview or xv to display it.
I assume you're also going to want to save the file. That's easy too:
name, ext = os.path.splitext(filename)
outfilename = '{}-with-stars.{}'.format(name, ext)
im.save(outfilename)
This will save it back to a .jpg with default JPEG settings, relying on PIL guessing what you want from the filename. (Which means, yes, you can save it as a PNG just by using '{}-with-stars.png'.format(name)
.) If you want more control, PIL can do that too, specifying an explicit format, and format-specific options.
So far, this is just how to turn your existing code into something that works, that you can play with and start debugging; it doesn't actually answer the original problem.
I want to write a function that will create a random number (between m and n, inclusive) of stars in the sky of this picture
So first, you need this as your loop, instead of a loop over all pixels:
for _ in random.randint(m, n):
Now:
I want the stars should be randomly composed of either a single white pixel, or a square of 4 adjacent white pixels.
x, y = random.randrange(max_x), random.randrange(max_y)
if random.random() < .5:
# draw white pixel at [x, y]
pixels[x, y] = (1, 1, 1, 1)
else:
# draw square at [x, y], making sure to handle edges
I also do not want to place a 'star' (of 1 pixel) over the tree branches, the moon or the bird though
You need to define how you know what's part of a tree branch, the moon, or the bird. Can you define that in terms of pixel colors?
From a quick glance, it looks like you might be able to. The moon's pixels are all brighter, more saturated, more red-biased, etc. than anything else (except the AP logo in the corner, which is even brighter). The bird and the branches are darker than anything else. In fact, they're so distinct that you probably don't even have to worry about doing correct colorspace math; it may be as simple as something like this:
r, g, b, a = pixels[x, y]
fake_brightness = r+g+b+a
if fake_brightness < 0.2:
# Tree or bird, pick a new random position
elif 1.2 < fake_brightness < 2.8:
# Moon, pick a new random position
else:
# Sky or API logo, scribble away
(Those numbers are obviously just pulled out of thin air, but a bit of trial and error should give you usable values.)
Of course if you're doing this as a learning exercise, you probably want to learn the correct colorspace math, and maybe even write an edge-detection algorithm, rather than relying on this image being so simply-parseable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With