Coder Social home page Coder Social logo

mosaic's Introduction

mosaic

This utility can be used to generate photo-mosaic images, to use it you must have Python installed, along with the Pillow imaging library.

As well as an image to use for the photo-mosaic (most common image formats are supported), you will need a large collection of different images to be used as tiles. The tile images can be any shape or size (the utility will automatically crop and resize them) but for good results you will need a lot of them - a few hundred at least. One convenient way of generating large numbers of tile images is to extract screenshots from video files using ffmpeg.

Run the utility from the command line, as follows:

python mosaic.py <image> <tiles directory>
  • The image argument should contain the path to the image for which you want to build the mosaic
  • The tiles directory argument should contain the path to the directory containing the tile images (the directory will be searched recursively, so it doesn't matter if some of the images are contained in sub-directories)

For example:

python mosaic.py game_of_thrones_poster.jpg /home/admin/images/screenshots

The images below show an example of how the mosaic tiles are matched to the details of the original image:

Mosaic Image
Original

Mosaic Image Detail
Mosaic Detail (click through for full mosaic ~15MB)

Producing large, highly detailed mosaics can take some time - you should experiment with the various configuration parameters explained in the source code to find the right balance between image quality and render time.

In particular the TILE_MATCH_RES parameter can have a big impact on both these factors - its value determines how closely the program examines each tile when trying to find the best fit for a particular segment of the image. Setting TILE_MATCH_RES to '1' simply finds the average colour of each tile, and picks the one that most closely matches the average colour of the image segment. As the value is increased, the tile is examined in more detail. Setting TILE_MATCH_RES to equal TILE_SIZE will cause the utility to examine each pixel in the tile individually, producing the best possible match (during my testing I didn't find a very noticeable improvement beyond a value of 5, but YMMV).

By default the utility will configure itself to use all available CPUs/CPU-cores on the host system, if you want to leave some processing power spare for other tasks then adjust the WORKER_COUNT parameter accordingly.

mosaic's People

Contributors

codebox avatar hatns avatar mrebbinghaus avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mosaic's Issues

Playing with this and I got this odd error.

C:\code\sold>python driver.py flag.jpg Images
'python' is not recognized as an internal or external command,
operable program or batch file.

C:\code\sold>c:\Python27\python.exe driver.py flag.jpg Images
Reading tiles from 'Images'...
Processed 60 tiles.
Processing main image...
Main image processed.
Building mosaic, press Ctrl-C to abort...
Traceback (most recent call last):
Traceback (most recent call last):
File "driver.py", line 234, in
File "", line 1, in
File "c:\Python27\lib\multiprocessing\forking.py", line 381, in main
mosaic(sys.argv[1], sys.argv[2])
File "driver.py", line 227, in mosaic
self = load(from_parent)
File "c:\Python27\lib\pickle.py", line 1384, in load
compose(image_data, tiles_data)
File "driver.py", line 199, in compose
return Unpickler(file).load()
File "c:\Python27\lib\pickle.py", line 864, in load
Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large)).start()
dispatchkey
File "c:\Python27\lib\multiprocessing\process.py", line 130, in start
File "c:\Python27\lib\pickle.py", line 886, in load_eof
self._popen = Popen(self)
File "c:\Python27\lib\multiprocessing\forking.py", line 277, in init
raise EOFError
EOFErrordump(process_obj, to_child, HIGHEST_PROTOCOL)

File "c:\Python27\lib\multiprocessing\forking.py", line 199, in dump
ForkingPickler(file, protocol).dump(obj)
File "c:\Python27\lib\pickle.py", line 224, in dump
self.save(obj)
File "c:\Python27\lib\pickle.py", line 331, in save
self.save_reduce(obj=obj, *rv)
File "c:\Python27\lib\pickle.py", line 425, in save_reduce
save(state)
File "c:\Python27\lib\pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "c:\Python27\lib\pickle.py", line 655, in save_dict
self._batch_setitems(obj.iteritems())
File "c:\Python27\lib\pickle.py", line 687, in _batch_setitems
save(v)
File "c:\Python27\lib\pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "c:\Python27\lib\pickle.py", line 554, in save_tuple
save(element)
File "c:\Python27\lib\pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "c:\Python27\lib\pickle.py", line 731, in save_inst
save(stuff)
File "c:\Python27\lib\pickle.py", line 286, in save
f(self, obj) # Call unbound method with explicit self
File "c:\Python27\lib\pickle.py", line 655, in save_dict
self._batch_setitems(obj.iteritems())
File "c:\Python27\lib\pickle.py", line 687, in _batch_setitems
save(v)
File "c:\Python27\lib\pickle.py", line 313, in save
(t.name, obj))
pickle.PicklingError: Can't pickle 'ImagingCore' object: <ImagingCore object at 0x02F74830>

Verify the main image exists before loading titles

Hi, you should test if the main image exists before loading all titles. I forgot the extension and just after some time it gave the error that it was wrong and I lost all the progress and had to restart.

errors

Hi, just d/l'd this for a test drive.
Every config is the default out of the box but I get the following errors:

python3 /Volumes/SSD2/Users/martin/Downloads/_CleanShotX-Save/mosaic.py 2019-02-04--16-11-41.jpeg '/Volumes/Creation/Thai-beach' Processing main image... Main image processed. Reading tiles from /Volumes/Creation/Thai-beach... Processed 0 tiles. Building mosaic, press Ctrl-C to abort... Process Process-1: Traceback (most recent call last): File "/usr/local/Cellar/[email protected]/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/process.py", line 315, in _bootstrap self.run() File "/usr/local/Cellar/[email protected]/3.9.7/Frameworks/Python.framework/Versions/3.9/lib/python3.9/multiprocessing/process.py", line 108, in run self._target(*self._args, **self._kwargs) File "/Volumes/SSD2/Users/martin/Downloads/_CleanShotX-Save/mosaic.py", line 169, in build_mosaic tile_data = all_tile_data_large[best_fit_tile_index] TypeError: list indices must be integers or slices, not NoneType

Any ideas? running on macOS Monterey 12.1. Python

MemoryError

i seem to be getting a memory error:

Reading tiles from C:\Users\vorte\Desktop\16x\assets\minecraft\textures\block\...
Processed 907 tiles.
Processing main image...
Traceback (most recent call last):
  File "C:\Users\vorte\Desktop\findstuff\mosaic.py", line 234, in <module>
    mosaic(sys.argv[1], sys.argv[2])
  File "C:\Users\vorte\Desktop\findstuff\mosaic.py", line 226, in mosaic
    image_data = TargetImage(img_path).get_data()
  File "C:\Users\vorte\Desktop\findstuff\mosaic.py", line 70, in get_data
    large_img = img.resize((w, h), Image.ANTIALIAS)
  File "C:\Users\vorte\AppData\Local\Programs\Python\Python38-32\lib\site-packages\PIL\Image.py", line 1873, in resize
    return self._new(self.im.resize(size, resample, box))
MemoryError

what would be causing this? my config is:

# Change these 3 config parameters to suit your needs...
TILE_SIZE = 16  # height/width of mosaic tiles in pixels
TILE_MATCH_RES = 8  # tile matching resolution (higher values give better fit but require more processing)
ENLARGEMENT = 16  # the mosaic image will be this many times wider and taller than the original

the input file is 256*256 and all of the tiles are 16x16. any idea how i can fix this, i have 16gb of memory so i dont see why this is a huge problem. all of the 907 images are 2.35 MB, and the largest of the files is around 50kb. the program also has just under 11gb to work with.

list index out of range

Hi, im working with this script and i found this problem...

C:\Users\Daniel Doyharzabal\Desktop\Imagenes>python original.py nene_original.jpg C:\ImageOutput
Reading tiles from 'C:\ImageOutput'...
Processed 2391 tiles.
Processing main image...
Main image processed.
Building mosaic, press Ctrl-C to abort...
Process Process-2:
Traceback (most recent call last):
File "C:\Python27\lib\multiprocessing\process.py", line 258, in _bootstrap
self.run()
File "C:\Python27\lib\multiprocessing\process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 122, in fit_tiles
tile_index = tile_fitter.get_best_fit_tile(img_data)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 105, in get_best_fit_tile
diff = self.__get_tile_diff(img_data, tile_data, min_diff)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 92, in __get_tile_diff
diff += ((t1[i][0] - t2[i][0])**2 + (t1[i][1] - t2[i][1])**2 + (t1[i][2] - t2[i][2])**2)
IndexError: list index out of range
Process Process-3:
Traceback (most recent call last):
File "C:\Python27\lib\multiprocessing\process.py", line 258, in _bootstrap
self.run()
File "C:\Python27\lib\multiprocessing\process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 122, in fit_tiles
tile_index = tile_fitter.get_best_fit_tile(img_data)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 105, in get_best_fit_tile
diff = self.__get_tile_diff(img_data, tile_data, min_diff)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 92, in __get_tile_diff
diff += ((t1[i][0] - t2[i][0])**2 + (t1[i][1] - t2[i][1])**2 + (t1[i][2] - t2[i][2])**2)
IndexError: list index out of range
Process Process-4:
Traceback (most recent call last):
File "C:\Python27\lib\multiprocessing\process.py", line 258, in _bootstrap
self.run()
File "C:\Python27\lib\multiprocessing\process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 122, in fit_tiles
tile_index = tile_fitter.get_best_fit_tile(img_data)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 105, in get_best_fit_tile
diff = self.__get_tile_diff(img_data, tile_data, min_diff)
File "C:\Users\Daniel Doyharzabal\Desktop\Imagenes\original.py", line 92, in __get_tile_diff
diff += ((t1[i][0] - t2[i][0])**2 + (t1[i][1] - t2[i][1])**2 + (t1[i][2] - t2[i][2])**2)
IndexError: list index out of range

The error does not always appear. At first I thought that by removing the pyc file generated by the interpreter, the error would be solved, but it was not.

I'm working on Windows 10, with python 2.7 64 bit.

Possible to limit number of time a tile is used ?

Hi,
your tool seems very useful, I have one simple question:

Is it possible to limit the number of time a tile is used ?
If yes, how can it be achieved ?
If no, is it easily implementable, any idea how to implement it ?

Thank you .

Error

FileNotFoundError: [Errno 2] No such file or directory: 'FILE.jpg'

python3 mosaic.py FILE.jpg /data/temp/Photo2021/

This happens when trying to run via python3. If I use python2 it says syntax error.

Improvement: Use more unique images as tiles

Currently, the same image is used for many tiles. Even in the GameOfThrones example, there are several duplicates. Provide a way to increase number of unique tiles.
How can we tweak get_best_fit_tile() to get more unique images?
We could add a tile_usage_score for each tile in tiles directory. Then factor this score into diff, so that a tile that has been overused will get trumped by another tile(which might not be the best fit, but close enough).

Memory error

Hey, I wanted to use it on windows from console and this is what I got:

Reading tiles from '.\belly'...
Processed 34 tiles.
Processing main image...
Traceback (most recent call last):
File ".\mosaic.py", line 221, in
mosaic(sys.argv[1], sys.argv[2])
File ".\mosaic.py", line 214, in mosaic
image_data = TargetImage(img_path).get_data()
File ".\mosaic.py", line 66, in get_data
large_img = img.resize((w, h), Image.ANTIALIAS)
File "C:\Python27\lib\site-packages\PIL\Image.py", line 1645, in resize
return self._new(self.im.resize(size, resample))
MemoryError

I don't know what is the reason, just saying.

Best,
Dawid

invalid path issue

Hey, I'm trying to run this, and can't seem to get the tiles to be recognized in the command. I have a subdirectory called 'screens' from where the .py file is

python mosaic.py imgSrc screens
python mosaic.py imgSrc /screens
python mosaic.py imgSrc fullPath

all give me

invalid syntax

Am I missing something obvious here?

Multiprocessing pickle challenges

Since pillow "Added support for encoding and decoding iTXt chunks"
python-pillow/Pillow#818

Certain images with those chunks are now unpicklable.
python-pillow/Pillow#1434

Causing this failure:

Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\spawn.py", line 105, in spawn_main
    exitcode = _main(fd)
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\spawn.py", line 115, in _main
    self = reduction.pickle.load(from_parent)
TypeError: __new__() missing 2 required positional arguments: 'lang' and 'tkey'
Traceback (most recent call last):
  File "C:/Users/myuser/Downloads/mosaic/mosaic.py", line 236, in <module>
    mosaic(sys.argv[1], sys.argv[2])
  File "C:/Users/myuser/Downloads/mosaic/mosaic.py", line 230, in mosaic
    compose(image_data, tiles_data)
  File "C:/Users/myuser/Downloads/mosaic/mosaic.py", line 204, in compose
    Process(target=build_mosaic, args=(result_queue, all_tile_data_large, original_img_large)).start()
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\process.py", line 105, in start
    self._popen = self._Popen(self)
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\context.py", line 223, in _Popen
    return _default_context.get_context().Process._Popen(process_obj)
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\context.py", line 322, in _Popen
    return Popen(process_obj)
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\popen_spawn_win32.py", line 65, in __init__
    reduction.dump(process_obj, to_child)
  File "C:\Users\myuser\AppData\Local\Continuum\Anaconda3\lib\multiprocessing\reduction.py", line 60, in dump
    ForkingPickler(file, protocol).dump(obj)
BrokenPipeError: [Errno 32] Broken pipe

Weird Error trying to run Mosaic

I've got this weird problem when trying to run it. Here is the copy from the terminal. I've had it running for a couple of hours and it doesn't change no matter the time running

python mosaic.py GLAM.jpg /home/rogue/Videos/'LTT Glam'
Reading tiles from '/home/rogue/Videos/LTT Glam'...
Processed 0 tiles.
Processing main image...
Main image processed.
Building mosaic, press Ctrl-C to abort...
Progress: Process Process-1:
Traceback (most recent call last):
File "/usr/lib/python2.7/multiprocessing/process.py", line 267, in _bootstrap
self.run()
File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
self._target(*self._args, **self._kwargs)
File "mosaic.py", line 166, in build_mosaic
tile_data = all_tile_data_large[best_fit_tile_index]
TypeError: list indices must be integers, not NoneType
Progress: 99%

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.