From: "Francisco Ares" <frares@gmail.com>
To: gentoo-user@lists.gentoo.org
Subject: Re: [gentoo-user] [O.T] photomosaic
Date: Mon, 18 Dec 2006 22:42:12 -0200 [thread overview]
Message-ID: <543f3b9c0612181642m7f9fdd5dl36f4267fc6123361@mail.gmail.com> (raw)
In-Reply-To: <20061214151047.4bea4533@lx-arnau.pic.es>
A friend of mine built this Python script, I'm sure he doesn't mind ;)
#!/usr/bin/env python
# by Andre Bocchini
import sys
import os
import string
import getopt
import Image
import ImageFilter
import ImageStat
def usage():
""" Displays information on how to use the program. """
print
print "Usage : mosaic.py [options]"
print "Required:"
print " -i <input-img> Source image for the mosaic"
print " -s <img-dir> Directory where other images are located"
print " -o <output-img> Final mosaic image"
print " -b <blocks> Number of blocks per row in the mosaic"
print " -w <width> Final mosaic width"
print " -h <height> Final mosaic height"
print " -t <threshold> Sensitivity of the image analyzer"
print "Optional:"
print " --help Displays this help information"
print
def calculate_block_means(filename, blocks_in_row):
"""" Breaks an image into a matrix that has blocks_in_row x blocks_in_row
number of blocks, takes a mean of the RGB values of each block, and
stores this mean into a list that is returned to the caller. """
block_mean_list = []
image = Image.open(filename)
size = image.size
block_width = size[0] / blocks_in_row
block_height = size[1] / blocks_in_row
curr_block = 0
slice = Image.new("RGB", (block_width, block_height))
for i in range(0, blocks_in_row):
for j in range(0, blocks_in_row):
for y_offset in range(0, block_height):
for x_offset in range(0, block_width):
startx = j * block_width
starty = i * block_height
slice.putpixel((x_offset, y_offset),\
image.getpixel((startx + x_offset, starty + y_offset)))
slice_stat = ImageStat.Stat(slice)
slice_mean = slice_stat.mean
slice_r_mean = slice_mean[0]
slice_g_mean = slice_mean[1]
slice_b_mean = slice_mean[2]
final_slice_mean = (slice_r_mean + slice_g_mean + slice_b_mean) / 3
block_mean_list.append(final_slice_mean)
curr_block = curr_block + 1
#print ">>> Block mean for block %d: %d" % (i+j, final_slice_mean)
return block_mean_list
def build_mean_list(image_directory):
""" Scans a directory for image files, takes the mean of the RGB values
in each image file, and stores these mean values into a list. Each
element in this list contains a pair of image file name and mean RGB
value. The list is returned to the caller. """
image_mean_list = []
# In order to avoid some repetition in the images used for flat dark areas,
# we keep a "least recently used" flag for every image.
lru_list = []
images = os.listdir(image_directory)
for filename in images:
try:
# This is the next image in the list
image = Image.open(image_directory + "/" + filename)
# Getting mean values for the current image
img_stat = ImageStat.Stat(image)
img_mean = img_stat.mean
img_r_mean = img_mean[0]
img_g_mean = img_mean[1]
img_b_mean = img_mean[2]
final_img_mean = (img_r_mean + img_g_mean + img_b_mean) / 3
image_mean_list.append((image_directory + "/" + filename,\
final_img_mean))
lru_list.append(0)
except IOError:
pass
except:
print "+++ Unknown error encountered while building mean list"
return image_mean_list, lru_list
if __name__ == "__main__":
# Parse command line
try:
opts, args = getopt.getopt(sys.argv[1:], "i:s:o:b:w:t:h:", ["help"])
except getopt.GetoptError:
usage()
sys.exit(-1)
in_image_name = None
src_image_dir = None
out_image_name = None
out_image_width = -1
out_image_height = -1
blocks_in_row = -1
threshold = 10
for option, value in opts:
if option == "--help":
usage()
sys.exit(0)
if option == "-i":
in_image_name = value
if option == "-o":
out_image_name = value
if option == "-s":
src_image_dir = value
if option == "-t":
threshold = string.atoi(value)
if option == "-w":
out_image_width = string.atoi(value)
if option == "-h":
out_image_height = string.atoi(value)
if option == "-b":
blocks_in_row = string.atoi(value)
# Checking for invalid input
error = False
if in_image_name == None:
print ">>> You must specify an input image"
error = True
if out_image_name == None:
print ">>> You must specify an output image"
error = True
if src_image_dir == None:
print ">>> You must specify an image src directory"
error = True
if blocks_in_row < 0:
print ">>> You must specify a valid number of blocks in a row"
error = True
if error == True:
usage()
sys.exit(-1)
# Display useful information
print ">>> Sensitivity threshold is %d" % threshold
print ">>> Matrix will be of size %d x %d blocks" % (blocks_in_row,\
blocks_in_row)
# Calculating block sizes
print ">>> Calculating block sizes"
in_image = Image.open(in_image_name)
if out_image_width < 0:
out_image_width = in_image.size[0]
if out_image_height < 0:
out_image_height = in_image.size[1]
block_width = out_image_width / blocks_in_row
block_height = out_image_height / blocks_in_row
print ">>> Blocks will be %d x %d in size" % (block_width, block_height)
# Figuring out the mean of all the blocks in the sec image
print ">>> Calculating means of the blocks in the src image"
block_mean_list = calculate_block_means(in_image_name, blocks_in_row)
print ">>> Calculated %d means" % len(block_mean_list)
# Get a listing of the images in the source directory and build means
print ">>> Calculating means for images in " + src_image_dir
image_mean_list, lru_list = build_mean_list(src_image_dir)
print ">>> Calculated %d means" % len(image_mean_list)
# Initializing destination image
out_image = Image.new("RGB", (out_image_width, out_image_width))
# Building the final image
print ">>> Building final image"
# Go through every block in the image, figure the appropriate mean, and
# retrieve the image necessary and put it on the output image
for i in range(0, len(block_mean_list)):
closest_match = 0
block_mean = block_mean_list[i]
match_found = False
loop_count = -1 # Used to help determine whether or not
# we need to loop again with an increased threshold.
adaptive_step = 2
while match_found != True:
loop_count += 1
if loop_count > 0:
# If we go through all the images and we don't find a
# good match, we start adjusting the threshold
threshold += adaptive_step
print "+++ Increasing threshold to %d." % threshold
for j in range(0, len(image_mean_list)):
image_entry = image_mean_list[j]
image_mean = image_entry[1]
closest_match_entry = image_mean_list[closest_match]
closest_mean_match = closest_match_entry[1]
if abs(image_mean - block_mean) <= abs(threshold) and\
abs(image_mean - block_mean) <=\
abs(closest_mean_match - block_mean):
# Let's try to avoid repetition by seeing if we can find
# an alternate image that wasn't just used.
if lru_list[j] != 1:
closest_match = j
lru_list[j] = 1
match_found = True
if loop_count > 0:
threshold -= (loop_count * adaptive_step)
loop_count = -1
print "--- Restoring threshold to %d." % threshold
else:
lru_list[j] = 0
match = image_mean_list[closest_match]
image = Image.open(match[0])
tmp = image.resize((block_width, block_height))
for y_offset in range(0, block_height):
for x_offset in range(0, block_width):
starty = (i / blocks_in_row) * block_height
startx = (i % blocks_in_row) * block_width
out_image.putpixel((startx + x_offset, starty + y_offset),\
tmp.getpixel((x_offset, y_offset)))
print ">>> Built block %d of %d" % (i, len(block_mean_list))
# We're done. Let's save our Mosaic
print ">>> Cropping and saving final image"
out_image = out_image.crop((0, 0, block_width * blocks_in_row,
block_height * blocks_in_row))
out_image.save(out_image_name)
On 12/14/06, Arnau Bria <arnau@emergetux.net> wrote:
> On Thu, 14 Dec 2006 14:30:17 +0000
> Redouane Boumghar wrote:
>
> > Hello Arnau
> Hi,
>
> > I'm sorry I didn't understand what you were looking for.
> It's ok, I must improve my English!
>
> > I proposed Image Magick and it sure can do it but with a little
> > head-scratch.
> >
> > So if i may resume u need :
> > - A parent picture
> > - A list of other pictures to fill the mosaic
>
> that's it!
>
> > With Image Magick you can extract a portion (according to you
> > discretization parameters) from the parent picture and then compare
> > it with the list of pictures you have by comparing metrics like RMSE
> > and then compose your mosaic with most revelant pictures at each
> > portion of the parent one.
> >
> > I'll post a shell script for this if I have time.
> > This could be fun,
> That would be nice!
>
> > Have a good day and tell me if you find a program that does it,
> at this point I have only found metapixel :-( it's nice, but i'm
> looking for something better.
>
> > Red.
> Thanks!
> --
> Arnau Bria
> http://blog.emergetux.net
> Wiggum: Dispara a las ruedas Lou.
> Lou: eee, es un tanque jefe.
> Wiggum: Me tienes hartito con todas tus excusas.
> --
> gentoo-user@gentoo.org mailing list
>
>
--
"If you have an apple and I have an apple and we exchange apples then
you and I will still each have one apple. But if you have an idea and
I have one idea and we exchange these ideas, then each of us will have
two ideas." - George Bernard Shaw
--
gentoo-user@gentoo.org mailing list
next prev parent reply other threads:[~2006-12-19 0:46 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2006-12-14 9:55 [gentoo-user] [O.T] photomosaic Arnau Bria
2006-12-14 11:07 ` Redouane Boumghar
2006-12-14 10:30 ` Arnau Bria
2006-12-14 14:30 ` Redouane Boumghar
2006-12-14 14:10 ` Arnau Bria
2006-12-19 0:42 ` Francisco Ares [this message]
2006-12-19 23:01 ` Kumar Golap
2006-12-19 23:14 ` Alan E. Davis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=543f3b9c0612181642m7f9fdd5dl36f4267fc6123361@mail.gmail.com \
--to=frares@gmail.com \
--cc=gentoo-user@lists.gentoo.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox