Introduction
In the 21st century with great advancement in technology, photo editing has become a form of art itself. Mosaic Image tool is a great way to create unique and expressive art. Image Mosaic is a simple application that will allow you to simply upload an image and create a mosaic out of a large database of pictures. The aim of my project was to implement image-processing based image mosaic tool within the Wolfram Language. The project consists of three parts:
- Equal Size Image Mosaic
- Unequal Size Image Mosaic
- Interactive web pages
Algorithm & Code
The project consists of two options. The first one is creating mosaic images which will be constructed from equal sized images. Whereas the second one considers the color distribution of the original image and based on the result divides it into different sized parts.
Datasets
Datasets are the set of images that are being used for image colorization. You can set/provide your preferable dataset for creating beautiful images.
flagList =
ConformImages[
Image /@
Cases[DeleteDuplicates[
EntityValue[EntityList["Country"], "Flag"]], _Graphics]];
flagListMean = ImageMeasurements[#, "Mean"] & /@ flagList;
Equal Size Mosaic
For creating equal size mosaic, first of we will use the function ImagePartition to divide the original image into equal sized parts. Afterwards, we will consider the mean value of each element and compare it with the mean values of the images of the database. Then the program creates a list which contains the images from the database which are the nearest to each part of the original image. Finally, I used the function ImageAssemble to assemble the images of the newly created list mentioned above.
The function imagePartition takes two parameters and partitions an image into the list of partitionSize x partitionSize subimages.
imagePartition[image_?ImageQ, partitionSize_?IntegerQ]:=
Flatten[ImagePartition[image, partitionSize]];
PiecewiseMatching is finding and resizing the element of the dataset which is the nearest to the current image part considering their mean values.
piecewiseMatching[imgPart_?ImageQ, dataSet_?ListQ, dataSetMean_?ListQ]:=
Module[
{curImgMean =ImageMeasurements[imgPart, "Mean"],
nearestMean, nearestMeanPos},
nearestMean =First[ Nearest[dataSetMean, curImgMean]];
nearestMeanPos = First[Flatten@Position[dataSetMean,nearestMean]];
ImageResize[dataSet[[nearestMeanPos]], ImageDimensions[imgPart]]
]
imageAssemble assembles an image from the list of the already matched images which are the nearest to the original image parts.
imageAssemble[imgPieces_?ListQ, inputImageDims_?ListQ, partitionSize_?IntegerQ]:=
Module[
{nestedImagePieces,
conformImg= ConformImages[imgPieces]},
nestedImagePieces =
Partition[conformImg, IntegerPart[First[inputImageDims]/partitionSize]];
ImageAssemble[nestedImagePieces]
]
imageMosaic uses all the functions mentioned above to create the mosaicImage.
imageMosaic[image_?ImageQ, partSmallestSize_?IntegerQ, dataSet_?ListQ] :=
Module[
{orgImgDim = ImageDimensions[image],
orgImgPieces = imagePartition[image, partSmallestSize],
dataSetMean = ImageMeasurements[#,"Mean"]&/@dataSet,
imgPieces, finalImg},
imgPieces = piecewiseMatching[#, dataSet, dataSetMean]&/@orgImgPieces;
finalImg = imageAssemble[imgPieces, orgImgDim, partSmallestSize]
]
Unequal Size Mosaic
For creating a mosaic image with unequal parts first we divide the image into already defined equal parts. At the same time, another function computes the exact coordinates of the generated pieces on the main image. Afterwards, the function creates an association from coordinates and corresponding image pieces. The next function compares the mean values of the images from the database to the mean values of the generated pieces. If the difference between those two number is higher than a constant the function divides that piece into other four equal sized parts in other case it transfers the association into new list. This function is repeated over and over again until in the main list is fully transformed. At the end using the function ImageCompose the program places each piece on the blank image considering its coordinates.
The function fourCords computes the exact coordinates of newly generated pieces and fourImgs divides the given image into four equal parts. After all this, the getAssoc function creates an association from the coordinates and the corresponding images. imageDivide function breaks the image into equal parts as long as they are bigger than the given maximum size of the part.
fourImgs[img_?ImageQ]:= Flatten[ImagePartition[img, ImageDimensions[img]/2]];
getAssoc[list_,image_]:=AssociationThread[fourCords[list] ,fourImgs[image]];
fourCords[cord_List]:=Module[
{res=Flatten@cord, midX, midY},
midX = res[[1]] + (res[[3]]-res[[1]])/2;
midY = res[[2]] + (res[[4]]-res[[2]])/2;
N@{
{{res[[1]],midY},{midX,res[[4]]}},
{{midX,midY},{res[[3]],res[[4]]}},
{{res[[1]],res[[2]]},{midX,midY}},
{{midX,res[[2]]},{res[[3]],midY}}
}
];
imageDivide[image_?ImageQ, max_?IntegerQ] :=Module[
{imgAssoc},
imgAssoc = Association[{{1,1},ImageDimensions@image}->image];
While[First[ImageDimensions[First[Flatten[Values[imgAssoc]]]]]*Last[ImageDimensions[First[Flatten[Values[imgAssoc]]]]]>max,
imgAssoc=Association@MapThread[getAssoc[#1,#2]&,{Keys[imgAssoc],Values[imgAssoc]}]
];
imgAssoc
];
The function imageMatching compares the generated pieces to the ones from the database and after finding a match replaces it with the image from the database.
imageMatching[imgAssoc_?AssociationQ, smallImages_?ListQ, min_?IntegerQ, allowedDistance_] := Module[
{inList = imgAssoc, dist =(First@#)&/@smallImages, nearestMean,curImg,nearestMeanPos, curItem,curImgMean, finalAssoc = <||>},
While
[
Length[inList]>0,
curItem = First@Normal[inList];
curImg = Last@curItem;
curImgMean = ImageMeasurements[curImg,"Mean"];
inList =DeleteCases[inList, curImg];
nearestMean = First@Nearest[dist,curImgMean ];
nearestMeanPos = First[Flatten@Position[dist,nearestMean]];
If[
Norm[curImgMean -nearestMean]<=allowedDistance || First[ImageDimensions[curImg]]* Last[ImageDimensions[curImg]]<=min,
curItem = Rule[First[curItem],ImageResize[Last[smallImages[[nearestMeanPos]]], ImageDimensions[curImg]]];
AppendTo[finalAssoc,curItem];,
inList = Join[inList,getAssoc[First[curItem],Last[curItem]]];
]
];
finalAssoc
]
mosiacCompose mainly uses the function ImageCompose to overlay each newly generated piece onto a blank image for creating the mosaic.
mosaicCompose[associmage_?AssociationQ, blankImage_?ImageQ] := Module[
{coords,imgs, midCoords, mosaic,i},
imgs = Flatten[Values[associmage]];
coords = Keys[associmage];
midCoords = {#[[1,1]]+(#[[2,1]]-#[[1,1]])/2,#[[1,2]]+ (#[[2,2]] - #[[1,2]])/2}&/@coords;
mosaic = blankImage;
For[i=1,i<=Length@associmage,i++,
mosaic=ImageCompose[mosaic,imgs[[i]],midCoords[[i]]]
];
mosaic
]
imageMosaic combines all the functions mentioned above to create the mosaic image.
imageMosaic[image_?ImageQ, max_?IntegerQ, dataSet_?ListQ, min_?IntegerQ, allowedDistance_]:=Module[
{imgPart= imageDivide[img,max],
orgImgDim = ImageDimensions[image],
assoc, blankImage, list},
list = Rule[ImageMeasurements[#,"Mean"],#]&/@dataSet;
assoc =imageMatching[imgPart,list, min, allowedDistance];
blankImage = ConstantImage[0, orgImgDim];
mosaicCompose[assoc, blankImage]
]
Cloud Deploy
Equal Size Mosaic Webpage
To make a mosaic image of your image out of flags you can upload your picture to the web form, which can be found here.
Unequal Size Mosaic Webpage
To make an unequal mosaic image of your image out of flags you can upload your picture to the web form, which can be found here
In case the picture turns out the same as in the case of equal size try changing the specifications/input.
Attachments: