{"id":25087,"date":"2022-09-22T11:34:09","date_gmt":"2022-09-22T18:34:09","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=25087"},"modified":"2022-09-26T15:19:47","modified_gmt":"2022-09-26T22:19:47","slug":"image-collage-generation-how-we-pieced-it-all-together","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together","title":{"rendered":"Image Collage Generation: How We Pieced it All Together"},"content":{"rendered":"\n<p>Imagine being able to create a beautiful collage of digital images automatically, on a mass production scale, with unknown inputs, in any layout. For example, you could create:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>A product page gallery collage that you can share with a click<\/li><li>An album or gallery of real estate or travel properties to showcase your agency\u2019s offerings<\/li><li>A banner collage from images coming from a web page crawler, or technical flow instructions&nbsp;<\/li><\/ul>\n\n\n\n<p>You can do it easily using Cloudinary\u2019s Image Collage generation feature.<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-9d6595d7 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/c_pad,b_auto:predominant,fl_preserve_transparency\/v1663762005\/docs\/collage\/blog\/food-collage.jpg\" alt=\"\" \/><\/figure>\n<\/div>\n\n\n\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\">\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/c_pad,b_auto:predominant,fl_preserve_transparency\/v1663762004\/docs\/collage\/blog\/landscape-collage.jpg\" alt=\"\" \/><\/figure>\n<\/div>\n<\/div>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/c_pad,b_auto:predominant,fl_preserve_transparency\/v1663761994\/docs\/collage\/blog\/flow-collage.jpg\" alt=\"\" \/><\/figure>\n\n\n\n<p>I\u2019m proud to have developed this exciting Cloudinary feature. In this post, I\u2019ll introduce you to this feature and explain how my team and I:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Built a simple, developer-friendly interface for creating customizable collages<\/li><li>Resolved the templating challenge by creating a flexible array representation to fit all needs<\/li><li>Considered several tradeoffs to resolve the gap challenge while satisfying desired collage properties, without compromising the resolution<\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Developer-friendly Interface<\/strong><\/h2>\n\n\n\n<p><strong><br><\/strong>Our goal in developing the ImageCollage generation feature was to empower Cloudinary users to choose collage dimensions, assets, spacing, and layout. Most of these parameters are represented easily by a json. But how could we determine the collage layout? This was our first challenge.&nbsp;<\/p>\n\n\n\n<p>A grid layout was our first choice. We started with the following json representation:The first thing to tackle was how to represent collage generation easily. We would like to allow the user to choose the collage dimensions, assets, spacing and layout. Most of these parameters are represented easily by a json, but how to determine the collage layout?<\/p>\n\n\n\n<p>It was clear that `grid` should be the first one to develop. So we started with the following json representation:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">manifest_json={\n  <span class=\"hljs-string\">\"template\"<\/span>: <span class=\"hljs-string\">\"grid\"<\/span>,\n  <span class=\"hljs-string\">\"width\"<\/span>: <span class=\"hljs-number\">500<\/span>,\n  <span class=\"hljs-string\">\"height\"<\/span>: <span class=\"hljs-number\">500<\/span>,\n  <span class=\"hljs-string\">\"columns\"<\/span>: <span class=\"hljs-number\">3<\/span>,\n  <span class=\"hljs-string\">\"rows\"<\/span>: <span class=\"hljs-number\">3<\/span>,\n  <span class=\"hljs-string\">\"spacing\"<\/span>: <span class=\"hljs-number\">2<\/span>,\n  <span class=\"hljs-string\">\"color\"<\/span>: <span class=\"hljs-string\">\"white\"<\/span>,\n  <span class=\"hljs-string\">\"assetDefaults\"<\/span>: { <span class=\"hljs-string\">\"kind\"<\/span>: <span class=\"hljs-string\">\"upload\"<\/span>, <span class=\"hljs-string\">\"crop\"<\/span>: <span class=\"hljs-string\">\"fill\"<\/span>, <span class=\"hljs-string\">\"gravity\"<\/span>: <span class=\"hljs-string\">\"center\"<\/span> },\n  <span class=\"hljs-string\">\"assets\"<\/span>: &#91;\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/tasmaniandevil\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/kangaroo\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/cassawary\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/echidna\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/koala\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/emu\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/platypus\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/wombat\"<\/span> },\n    { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/australian-animals\/kokatoo\"<\/span>, <span class=\"hljs-string\">\"gravity\"<\/span>: <span class=\"hljs-string\">\"north\"<\/span> }\n  ]\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let\u2019s dive into the parameters.&nbsp;<\/p>\n\n\n\n<p>The template parameter defines the collage layout. A grid requires the following dimensions to be defined:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Width<\/li><li>Height<\/li><li>Spacing between images<\/li><\/ul>\n\n\n\n<p>In addition, row and column counts should be defined to determine the number of images in the collage. This is achieved by setting the following parameters:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Width<\/li><li>Height<\/li><li>Spacing<\/li><li>Rows<\/li><li>Columns<\/li><\/ul>\n\n\n\n<p>The images that compose the collage are specified by the assets parameter, which is an array that should match the template size (rows x columns).<br><br>Next, we explored collage customization.&nbsp;<\/p>\n\n\n\n<p>Setting the \u201ccolor\u201d parameter determines the color of the spacing between images. Options for personalizing assets include:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Setting defaults for all assets<\/strong> to control the resource delivery type (upload\/fetch\/restricted), crop, and gravity, defined by the \u201cassetDefaults\u201d parameter<\/li><li><strong>Setting specific asset properties<\/strong> by specifying the desired \u2018kind\u2019, \u2018crop\u2019, and \u2018gravity\u2019 and thus allowing maximum flexibility in asset personalization<\/li><\/ul>\n\n\n\n<p>The manifest json above produces the following collage:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/c_pad,b_auto:predominant,fl_preserve_transparency\/v1663746337\/docs\/collage\/blog\/australian-collage.jpg\" alt=\"\" \/><\/figure>\n\n\n\n<p>The challenge that remained was representing custom collage templates. Let\u2019s have a look at some template options:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"302\" height=\"594\" data-public-id=\"Screen_Shot_2022-09-20_at_2.53.38_PM\/Screen_Shot_2022-09-20_at_2.53.38_PM.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_302,h_594,c_scale\/f_auto,q_auto\/v1663712491\/Screen_Shot_2022-09-20_at_2.53.38_PM\/Screen_Shot_2022-09-20_at_2.53.38_PM.png?_i=AA\" alt=\"\" class=\"wp-post-25087 wp-image-25096\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1663712491\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712491\/Screen_Shot_2022-09-20_at_2.53.38_PM\/Screen_Shot_2022-09-20_at_2.53.38_PM.png?_i=AA 302w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712491\/Screen_Shot_2022-09-20_at_2.53.38_PM\/Screen_Shot_2022-09-20_at_2.53.38_PM.png?_i=AA 153w\" sizes=\"auto, (max-width: 302px) 100vw, 302px\" \/><\/figure>\n\n\n\n<p>We could use predefined templates using a string representation as we did for the grid layout, but we wanted to provide users with more template options.&nbsp;<\/p>\n\n\n\n<p>Assuming the whole collage is a 3&#215;3 matrix, we merged cells to get the desired template. Each image is mapped to a single cell or multiple cells and represented by a color and a number.<\/p>\n\n\n\n<p>Let\u2019s see how this looks:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img width=\"238\" height=\"580\" data-public-id=\"Screen_Shot_2022-09-20_at_2.54.05_PM\/Screen_Shot_2022-09-20_at_2.54.05_PM.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_238,h_580,c_scale\/f_auto,q_auto\/v1663712492\/Screen_Shot_2022-09-20_at_2.54.05_PM\/Screen_Shot_2022-09-20_at_2.54.05_PM.png?_i=AA\" alt=\"\" class=\"wp-post-25087 wp-image-25097\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1663712492\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712492\/Screen_Shot_2022-09-20_at_2.54.05_PM\/Screen_Shot_2022-09-20_at_2.54.05_PM.png?_i=AA 234w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712492\/Screen_Shot_2022-09-20_at_2.54.05_PM\/Screen_Shot_2022-09-20_at_2.54.05_PM.png?_i=AA 123w\" sizes=\"auto, (max-width: 238px) 100vw, 238px\" \/><\/figure>\n\n\n\n<p>Numbers are determined left to right, top to bottom.<\/p>\n\n\n\n<p>Removing the colors, we could now represent the collage template in a simple numeric array:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1663871482\/Screen_Shot_2022-09-22_at_11.30.18_AM.png\" alt=\"\" width=\"215\" height=\"289\" \/><\/figure>\n\n\n\n<p>Want a different template configuration? No problem! Simply add more rows and columns to create your desired template.<br><\/p>\n\n\n\n<p>Here\u2019s the finalized manifest json for a custom template:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">manifest_json={\n <span class=\"hljs-string\">\"template\"<\/span>: &#91;&#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">2<\/span>],\n              &#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">3<\/span>, <span class=\"hljs-number\">3<\/span>],\n              &#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">3<\/span>, <span class=\"hljs-number\">3<\/span>]],\n <span class=\"hljs-string\">\"width\"<\/span>: <span class=\"hljs-number\">600<\/span>,\n <span class=\"hljs-string\">\"height\"<\/span>: <span class=\"hljs-number\">400<\/span>,\n <span class=\"hljs-string\">\"columns\"<\/span>: <span class=\"hljs-number\">3<\/span>,\n <span class=\"hljs-string\">\"rows\"<\/span>: <span class=\"hljs-number\">3<\/span>,\n <span class=\"hljs-string\">\"spacing\"<\/span>: <span class=\"hljs-number\">2<\/span>,\n <span class=\"hljs-string\">\"color\"<\/span>: <span class=\"hljs-string\">\"black\"<\/span>,\n <span class=\"hljs-string\">\"assetDefaults\"<\/span>: { <span class=\"hljs-string\">\"kind\"<\/span>: <span class=\"hljs-string\">\"upload\"<\/span>, <span class=\"hljs-string\">\"crop\"<\/span>: <span class=\"hljs-string\">\"fill\"<\/span>, <span class=\"hljs-string\">\"gravity\"<\/span>: <span class=\"hljs-string\">\"auto\"<\/span>},\n <span class=\"hljs-string\">\"assets\"<\/span>: &#91;{ <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/dogs\/dog1\"<\/span> },\n            { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/dogs\/dog2\"<\/span> },\n            { <span class=\"hljs-string\">\"media\"<\/span>: <span class=\"hljs-string\">\"docs\/collage\/blog\/dogs\/dog3\"<\/span>}\n ]\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And here\u2019s the collage it produces:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/demo\/image\/upload\/c_pad,b_auto:predominant,fl_preserve_transparency\/v1663746336\/docs\/collage\/blog\/dogs-collage.jpg\" alt=\"\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Validating Input<\/strong><\/h2>\n\n\n\n<p>The next challenge we tackled was validating input.&nbsp;<\/p>\n\n\n\n<p>Looking at the examples above, it is clear that the only valid template options are a square or a rectangle. In addition, the image numbers should be consecutive, should not repeat themselves in different images, and should be ordered as I previously explained.<\/p>\n\n\n\n<p>Here are two examples of invalid inputs:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"266\" height=\"406\" data-public-id=\"Screen_Shot_2022-09-20_at_2.54.30_PM\/Screen_Shot_2022-09-20_at_2.54.30_PM.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_266,h_406,c_scale\/f_auto,q_auto\/v1663712490\/Screen_Shot_2022-09-20_at_2.54.30_PM\/Screen_Shot_2022-09-20_at_2.54.30_PM.png?_i=AA\" alt=\"\" class=\"wp-post-25087 wp-image-25099\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1663712490\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/Screen_Shot_2022-09-20_at_2.54.30_PM\/Screen_Shot_2022-09-20_at_2.54.30_PM.png?_i=AA 266w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/Screen_Shot_2022-09-20_at_2.54.30_PM\/Screen_Shot_2022-09-20_at_2.54.30_PM.png?_i=AA 197w\" sizes=\"auto, (max-width: 266px) 100vw, 266px\" \/><\/figure>\n\n\n\n<p>The first example is invalid because&nbsp;image 2 is not a rectangle or a square.<\/p>\n\n\n\n<p>The second example is invalid because there is a repeated number (2) and a jump in images numbering (5 instead of 4).<\/p>\n\n\n\n<p>Looking closely at the first example, the problem is that cells (0,1), (1,0), (1,1) don\u2019t create a square; they form an L-shaped structure. Any possible L structure is invalid even if it is rotated by 90, 180, or 270 degrees.<\/p>\n\n\n\n<p>Looking at the (2,1) cell in the second example,&nbsp;there is no adjacent horizontal or vertical cell with the same number (lonely cell), and the number already appears in a previous image in the template . Let\u2019s ignore the numbering consistency for now as this is easy to resolve.<br><br>After analyzing the input, validating the user input is easy: just search for \u201cL\u201d structures or lonely cells.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Mind the Gap<\/strong><\/h2>\n\n\n\n<p>Last but not least, we tackled the challenge of collage layout dimensions.&nbsp;<\/p>\n\n\n\n<p>Let\u2019s look at another example. Let\u2019s assume we\u2019re creating a 1&#215;3 grid collage of size 1000&#215;1000. Dividing the total width in the images count, to get each image width, results in 1000\/3=333.333<\/p>\n\n\n\n<p>We\u2019re working in pixels, which must be integers. We had several options here:<\/p>\n\n\n\n<ol class=\"wp-block-list\"><li>Leave a gap at the end, the start, or divide the missing pixels per image. How should we handle it in custom templates?<\/li><li>Stretch the last image to fill the gap (consider the edge cases, e.g., 1&#215;1000 grid)<\/li><li>Produce a collage with adjusted dimensions after ceiling\/flooring each image dimensions<\/li><\/ol>\n\n\n\n<p>Let\u2019s look at what Option 1 above might look like, where each blue section is a different image:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large is-resized\"><img width=\"1024\" height=\"226\" data-public-id=\"pasted_image_0_5\/pasted_image_0_5.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_226,c_scale\/f_auto,q_auto\/v1663712490\/pasted_image_0_5\/pasted_image_0_5.png?_i=AA\" alt=\"\" class=\"wp-post-25087 wp-image-25100\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1663712490\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/pasted_image_0_5\/pasted_image_0_5.png?_i=AA 1540w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/pasted_image_0_5\/pasted_image_0_5.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/pasted_image_0_5\/pasted_image_0_5.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/pasted_image_0_5\/pasted_image_0_5.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663712490\/pasted_image_0_5\/pasted_image_0_5.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Not looking very nice, is it?<\/p>\n\n\n\n<p>Fortunately, we used existing Cloudinary capabilities to enhance the collage layout.&nbsp;<\/p>\n\n\n\n<p>We started by rounding up any floating point value per image, such that every image had integer dimensions. After creating the whole grid using Cloudinary\u2019s <a href=\"https:\/\/cloudinary.com\/documentation\/resizing_and_cropping\">resize<\/a>, the collage is then downscaled to the desired dimensions. We thus filled the gap without losing resolution. (Note: Some resolution is, in fact, lost while ceiling each collage image, but this has a limited effect as 0 &lt; diff &lt; 1, while the alternative, upscaling the whole collage, may create a diff &gt; 1.)<br><\/p>\n\n\n\n<p>On a personal note, this was the first feature I developed in Cloudinary and I had a great time doing it while learning the system. Ready to start creating beautiful collages of images for your website with maximum flexibility using Cloudinary ImageCollage generation? Learn how to create your own collages <a href=\"https:\/\/cloudinary.com\/documentation\/image_collage_generation\">here<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Imagine being able to create a beautiful collage of digital images automatically, on a mass production scale, with unknown inputs, in any layout. For example, you could create:&nbsp; A product page gallery collage that you can share with a click An album or gallery of real estate or travel properties to showcase your agency\u2019s offerings [&hellip;]<\/p>\n","protected":false},"author":54,"featured_media":25088,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[165],"class_list":["post-25087","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-image-transformation"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Image Collage Generation: How We Pieced it All Together<\/title>\n<meta name=\"description\" content=\"A developer\u2019s look at building Cloudinary\u2019s automatic collage tool\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Image Collage Generation: How We Pieced it All Together\" \/>\n<meta property=\"og:description\" content=\"A developer\u2019s look at building Cloudinary\u2019s automatic collage tool\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-09-22T18:34:09+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2022-09-26T22:19:47+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation-png?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Image Collage Generation: How We Pieced it All Together\",\"datePublished\":\"2022-09-22T18:34:09+00:00\",\"dateModified\":\"2022-09-26T22:19:47+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\"},\"wordCount\":1067,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA\",\"keywords\":[\"Image Transformation\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\",\"url\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\",\"name\":\"Image Collage Generation: How We Pieced it All Together\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA\",\"datePublished\":\"2022-09-22T18:34:09+00:00\",\"dateModified\":\"2022-09-26T22:19:47+00:00\",\"description\":\"A developer\u2019s look at building Cloudinary\u2019s automatic collage tool\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Image Collage Generation: How We Pieced it All Together\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Image Collage Generation: How We Pieced it All Together","description":"A developer\u2019s look at building Cloudinary\u2019s automatic collage tool","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together","og_locale":"en_US","og_type":"article","og_title":"Image Collage Generation: How We Pieced it All Together","og_description":"A developer\u2019s look at building Cloudinary\u2019s automatic collage tool","og_url":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together","og_site_name":"Cloudinary Blog","article_published_time":"2022-09-22T18:34:09+00:00","article_modified_time":"2022-09-26T22:19:47+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation-png?_i=AA","type":"image\/png"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together"},"author":{"name":"","@id":""},"headline":"Image Collage Generation: How We Pieced it All Together","datePublished":"2022-09-22T18:34:09+00:00","dateModified":"2022-09-26T22:19:47+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together"},"wordCount":1067,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA","keywords":["Image Transformation"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together","url":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together","name":"Image Collage Generation: How We Pieced it All Together","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA","datePublished":"2022-09-22T18:34:09+00:00","dateModified":"2022-09-26T22:19:47+00:00","description":"A developer\u2019s look at building Cloudinary\u2019s automatic collage tool","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/image-collage-generation-how-we-pieced-it-all-together#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Image Collage Generation: How We Pieced it All Together"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1663366411\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation\/Cld_Blog_FeatImg_Sept2k22_Collage_Generation.png?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/25087","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/54"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=25087"}],"version-history":[{"count":13,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/25087\/revisions"}],"predecessor-version":[{"id":25202,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/25087\/revisions\/25202"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/25088"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=25087"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=25087"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=25087"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}