0

I need to create composite portrait mosaics (i.e. portraits made out of other portraits). See reference below.

Another good reference would be AndreaMosaic. http://www.andreaplanet.com/andreamosaic/samples/

Or this youtube tutorial (skip to the 5min mark) https://www.youtube.com/watch?v=9cy2gVm_ztQ

Looking for the best way to do this then generate a jpeg file that's ready to download. Ideally would like to do this with Node/Javascript but open to using PHP or whatever.

Any suggestions as to where to start? There are a few libraries here and there but nothing quite suited to what I'm trying to do.

enter image description here

jcbmb
  • 115
  • 10
  • see [Image to ASCII art conversion](https://stackoverflow.com/q/32987103/2521214) it is very similar problem. You just have images instead of letters and also you need to compare the similarity including color and intensity instead monochrome letters but most of the stuff is the same. – Spektre Sep 19 '17 at 11:39
  • Thank you but I think the problem is slightly different here. It's more of an image processing problem I think. Looking for a way to perform similar processing to what's at the end of the video I supplied as a reference. – jcbmb Sep 20 '17 at 11:10
  • The linked answer is image processing. Even the letters from Font are handled as sprite images. The only difference is they do not use intensity or colors just B/W but that is only the way of computing the coefficients. so you change just that sumation for loop (or just the data type of variable) and that is it... Do you have a sample texture atlas to work with ? preferably in single image – Spektre Sep 20 '17 at 11:16
  • Not sure I fully understand your comment but the tiles will be different for each computed image. And it should work with any image for that matter. – jcbmb Sep 20 '17 at 14:58
  • I see they are faking a mosaic (they do not use original images coloring) so it should be relativelly easy you just set color and intensity for each tile from the original image and may be add some blending with original image – Spektre Sep 21 '17 at 07:41

1 Answers1

1

The faked mosaic is simple. Well I tried a simple multiplication and looks like ti works.

  1. create a photo texture pattern covering the size of input image

  2. modulate the grayscale photo pattern and original image

    simple multiplication will do.

Both steps can be combined into single one... here simple C++ code for this:

// globals
const int txrs=41; // number of textures for mosaic
picture txr[txrs]; // mosaic textures
picture pic0,pic1; // input and output images

// init
pic0.load("MonaLisa.jpg");
int sz=32; // mosaic grid size
for (int i=0;i<txrs;i++) // load/resize/grayscale textures
    {
    txr[i].load(AnsiString().sprintf("textures\\%03i.jpg",i));  // load image
    txr[i].resize_fit(sz,sz,0x00000000);                        // resize to tile size
    txr[i].enhance_range();
    txr[i].pixel_format(_pf_u);                                 // convert to grayscale <0,765>
    txr[i].pixel_format(_pf_rgba);                              // convert to grayscale RGBA
    }
pic0.resize_fit((pic0.xs/sz)*sz,(pic0.ys/sz)*sz,0x00000000);    // cut not full tile size part of pic1

// mosaic
int xx,yy,x,y,i,j,sz=txr[0].xs,a,b;
color c0,c1;
pic1=pic0;  // copy source image to destination
// process all regions
for (y=0;y<pic1.ys;y+=sz)
 for (x=0;x<pic1.xs;x+=sz)
    {
    // select random texture
    i=Random(txrs);
    // proces region
    for (yy=0;yy<sz;yy++)
     for (xx=0;xx<sz;xx++)
        {
        // grayscale texture and original color image pixels
        c0=txr[i].p[yy][xx];
        c1=pic1.p[y+yy][x+xx];
        // mutiply them
        for (j=0;j<3;j++)
            {
            a=BYTE(c0.db[j]);
            b=BYTE(c1.db[j]);
            a=(a*b)>>8;
            c0.db[j]=a;
            }
        // store to destinatio image
        pic1.p[y+yy][x+xx]=c0;
        }
    }
pic1.save("out.png");

I use my own picture class for images so some members are:


xs,ys is size of image in pixels
p[y][x].dd is pixel at (x,y) position as 32 bit integer type
clear(color) clears entire image with color
resize(xs,ys) resizes image to new resolution
bmp is VCL encapsulated GDI Bitmap with Canvas access
pf holds actual pixel format of the image:

enum _pixel_format_enum
    {
    _pf_none=0, // undefined
    _pf_rgba,   // 32 bit RGBA
    _pf_s,      // 32 bit signed int
    _pf_u,      // 32 bit unsigned int
    _pf_ss,     // 2x16 bit signed int
    _pf_uu,     // 2x16 bit unsigned int
    _pixel_format_enum_end
    };


color and pixels are encoded like this:

union color
    {
    DWORD dd; WORD dw[2]; byte db[4];
    int i; short int ii[2];
    color(){}; color(color& a){ *this=a; }; ~color(){}; color* operator = (const color *a) { dd=a->dd; return this; }; /*color* operator = (const color &a) { ...copy... return this; };*/
    };


The bands are:

enum{
    _x=0,   // dw
    _y=1,

    _b=0,   // db
    _g=1,
    _r=2,
    _a=3,

    _v=0,   // db
    _s=1,
    _h=2,
    };

The input image I used was this:

input

And here the result:

output

It might need some brightness tweaking to match original input image properties.

Community
  • 1
  • 1
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • Ah thanks for this. Will have a look and see if I can get it to work in JS. – jcbmb Sep 21 '17 at 09:52
  • @jcbmb I di dit on per pixel basis ... I think in JS there should be also methods for images to combine them ... In such case you would ignore the `xx,yy` for loops and use the combining/blend methods instead. But I do not code in JS nor know which api for gfx you use ... Also to convert color `(r,g,b)` to grayscale you just do this `i=r+g+b/3; color(i,i,i)` you can also use weights for the bands to make it better ... – Spektre Sep 21 '17 at 09:54