public inbox for gentoo-user@lists.gentoo.org
 help / color / mirror / Atom feed
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



  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