在图片中随机改变像素,Python
我想写一个函数,能够在这张图片的天空中随机生成一定数量的星星,这个数量在m和n之间(包括m和n)。我希望这些星星可以是单个白色像素,或者是4个相邻的白色像素组成的方块。同时,我不想把单个像素的“星星”放在树枝、月亮或鸟的上面。
我该如何用Python来实现这个功能呢?有没有人能帮我一下?谢谢!
到目前为止,我已经有了一些代码:
我开始写了一些代码,但我不知道这些代码是否正确,或者我是否在正确的方向上:
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)
我完全不知道自己在做什么 :(
3 个回答
你可以试试这样的做法:
for n in xrange(number_of_stars):
# Find a good position
while True:
x, y = random_coords_in_image()
if is_sky(image, x, y):
break
# paint a star there
c = star_colour()
if large_star():
image.put(x, y, c)
image.put(x, y+1, c)
image.put(x+1, y+1, c)
image.put(x+1, y, c)
else:
image.put(x, y, c)
我用的这些函数都挺简单明了的;你可以实现一个叫 is_sky
的函数,来判断在某个地方图像的颜色是否符合一些条件。
Python的标准库里没有很强大的图像处理代码,但有一些替代方案,安装和使用都很简单。我会用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()
show
这个函数并不会在你的应用程序中显示图像(如果想这样做,你需要一些带事件循环的图形界面,比如tkinter
或PySide
);它会把文件保存到一个临时目录,然后运行一个特定平台的程序,比如预览(Preview)或xv来显示它。
我想你也会想保存这个文件。这也很简单:
name, ext = os.path.splitext(filename)
outfilename = '{}-with-stars.{}'.format(name, ext)
im.save(outfilename)
这段代码会把图像保存回一个.jpg文件,使用默认的JPEG设置,PIL会根据文件名来猜测你想要的格式。(这意味着,你可以通过使用'{}-with-stars.png'.format(name)
来保存为PNG格式。)如果你想要更多控制,PIL也可以做到,允许你指定明确的格式和格式特定的选项。
到目前为止,这只是把你现有的代码变成可以运行的东西,让你可以玩玩和开始调试;它并没有真正解决原来的问题。
我想写一个函数,随机生成一个数量(在m和n之间,包括m和n)的星星,出现在这张图片的天空中。
所以首先,你需要用这个作为循环,而不是对所有像素进行循环:
for _ in random.randint(m, n):
现在:
我希望星星随机由一个白色像素或一个由4个相邻白色像素组成的正方形构成。
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
不过,我也不想把一个“星星”(1个像素)放在树枝、月亮或鸟的上面。
你需要定义一下,怎么知道什么是树枝、月亮或鸟。你能用像素颜色来定义这些吗?
从快速的观察来看,你可能可以做到。月亮的像素比其他任何东西都亮、饱和度高、偏红等等(除了角落里的AP标志,它甚至更亮)。鸟和树枝的颜色比其他任何东西都暗。实际上,它们的区别如此明显,你可能根本不需要担心进行复杂的颜色空间计算;这可能就像这样简单:
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
(那些数字显然是随便拿的,但经过一些尝试和错误,你应该能找到可用的值。)
当然,如果你把这当作学习练习,你可能想学习正确的颜色空间计算,甚至写一个边缘检测算法,而不是依赖于这个图像如此简单易解析。
这个看起来是一个不错的例子,可以尝试一下超像素,这是由skimage实现的。对于你的问题,可能有更简单的方法。
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')
现在我们有了超像素,这样分类就简单多了,可以按超像素来进行分类。你可以在这里使用一些高级的分类方法,但这个例子比较简单,天空是蓝色的,我们就手动来做吧。
# 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)