Answers.Body: <p>In case anyone familiar with JavaScript is interested, I wrote this script (tested in Firefox 19) which lets the user specify the number of tiles (bricks) of each colour and browse for an image file with the same number of pixels.</p>
  
  <p>The luma (brightness) of each pixel is ranked and assigned one of the listed colours. Any ties are arbitrarily assigned to either possible colour (as JavaScript's sort is unstable).</p>
  
  <p><a href="http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Mona_Lisa_headcrop.jpg/20px-Mona_Lisa_headcrop.jpg" rel="noreferrer">http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Mona_Lisa_headcrop.jpg/20px-Mona_Lisa_headcrop.jpg</a> <img src="https://i.sstatic.net/AM6bf.jpg" alt="http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Mona_Lisa_headcrop.jpg/20px-Mona_Lisa_headcrop.jpg"> is an image which fits the example tile count.</p>
  
  <pre><code>&lt;html&gt;
   &lt;head&gt;&lt;title&gt;Make mosaic given tile colour count&lt;/title&gt;&lt;/head&gt;
   &lt;body&gt;&lt;form onSubmit="return false;"&gt;
    &lt;textarea id="counts" rows="3" cols="80"&gt;// Ordered from darkest to lightest
  colours = [ '#000000', '#993300', '#cc0000', '#ff9900', '#ffff00', '#ffffff' ];
  tiles   = [       110,        40,        80,        60,       120,        90 ];
  tileWidth = tileHeight = 16;&lt;/textarea&gt;&lt;br /&gt;
    &lt;input type="file" id="file" size="80" /&gt;
    &lt;img id="img" align="top" /&gt;&lt;br /&gt;
    &lt;input type="button" value="Go!" onClick="loadImage()" /&gt;&lt;br /&gt;&lt;br /&gt;
    &lt;canvas id="canvas" style="background:#808080;border:2px groove;"&gt;&lt;/canvas&gt;
    &lt;script&gt;
  var tileWidth = 16, tileHeight = 16, colours, tiles;
  // Based on http://jsfiddle.net/fWLJ9/
  var canvas  = document.getElementById('canvas');
  var context = canvas.getContext('2d');
  var img     = document.getElementById('img');
  var imgData;
  function loadImage() {
   var url = window.URL || window.webkitURL;
   var src = url.createObjectURL(document.getElementById('file').files[0]);
   img.src = src;
   img.onload = function() {
    resizeCanvas();
    context.drawImage(img, 0, 0);
    url.revokeObjectURL(src);
    imgData = context.getImageData(0, 0, img.width, img.height).data;
    makeMosaic();
   }
  }
  function resizeCanvas() {
   canvas.width  = img.width  * tileWidth  - 1;
   canvas.height = img.height * tileHeight - 1;
  }
  function makeMosaic() {
   // Check counts are correct
   eval(document.getElementById('counts').value);
   var count = 0;
   for (var i = 0; i &lt; colours.length; ++i) { count += tiles[i]; }
   if (count != img.width * img.height) {
    alert(img.width * img.height + ' pixels but ' + count + ' tiles!');
    return;
   }
   // Read pixels' luma
   var lumas = new Array();
   for (var y = 0; y &lt; img.height; ++y) {
    for (var x = 0; x &lt; img.width; ++x) {
     lumas.push({ x:x, y:y, luma:getPixelLuma(x, y) });
    }
   }
   // Sort in ascending order
   lumas.sort(function(a, b) { return a.luma - b.luma });
   // Assign pixels to colours
   resizeCanvas();
   count = 0;
   for (var i = 0; i &lt; colours.length; ++i) {
    context.fillStyle = colours[i];
    for (var j = 0; j &lt; tiles[i]; ++j) {
     context.fillRect(lumas[count].x * tileWidth, lumas[count].y * tileHeight,
                      tileWidth - 1, tileHeight - 1);
     ++count;
    }
   }
  }
  function getPixelLuma(x, y) {
   var pixelOffset = (x + y * img.width) * 4;
   return (0.21 * imgData[pixelOffset] + 0.72 * imgData[pixelOffset + 1] +
           0.07 * imgData[pixelOffset + 2]).toFixed(2);
  }
    &lt;/script&gt;
   &lt;/form&gt;&lt;/body&gt;
  &lt;/html&gt;
  </code></pre>
  
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2013-04-02T00:38:00.287Z
Answers.LastActivityDate: 2013-04-02T00:38:00.287Z
Answers.UserDisplayName: Gnubie
Body: <p>This question is similar to <a href="https://bricks.stackexchange.com/questions/1060/how-can-i-convert-an-images-pixels-to-actual-lego-colors/1491#1491">How can I convert an image&#39;s pixels to actual LEGO colors?</a> except that I need to fit a certain number of bricks of a certain colour.</p>
  
  <p>For example, I have 20 white, 28 yellow, 10 orange, 18 red and 24 black (colours listed in increasing darkness) 1x1 bricks and a greyscale 10x10 pixel photo. How can I manipulate the photo so that it shows the 20 brightest pixels in white, next 28 in yellow and so on until the darkest 24 in black? If there is a tie, for example, the 19th, 20th and 21st brightest pixel have the same brightness, it highlights the ties and tells me that two of them should be white and one should be yellow.</p>
  
  <p>Bonus: The above uses a 1-dimensional algorithm, but how can I have a 2-dimensional (or more one)? For example, if I have 10 white, 20 yellow, 30 blue and 40 black 1x1 bricks, and an image with shades of yellow, green and blue, the program should assign the most yellowish greens to yellow bricks (or white/black for brightest/darkest ones) and bluish greens to blue bricks (or white/black for brightest/darkest ones).</p>
  
Tags: <software><colour><mosaic>
Title: How can I make a mosaic from a given number of 1x1 bricks and a picture?
UserDisplayName: Gnubie
ThreadText: # How can I make a mosaic from a given number of 1x1 bricks and a picture?
  
  (Asked by: username_0 on 2013-03-31)
  
  This question is similar to [How can I convert an image's pixels to actual LEGO colors?](https://bricks.stackexchange.com/questions/1060/how-can-i-convert-an-images-pixels-to-actual-lego-colors/1491#1491) except that I need to fit a certain number of bricks of a certain colour.
  
  For example, I have 20 white, 28 yellow, 10 orange, 18 red and 24 black (colours listed in increasing darkness) 1x1 bricks and a greyscale 10x10 pixel photo. How can I manipulate the photo so that it shows the 20 brightest pixels in white, next 28 in yellow and so on until the darkest 24 in black? If there is a tie, for example, the 19th, 20th and 21st brightest pixel have the same brightness, it highlights the ties and tells me that two of them should be white and one should be yellow.
  
  Bonus: The above uses a 1-dimensional algorithm, but how can I have a 2-dimensional (or more one)? For example, if I have 10 white, 20 yellow, 30 blue and 40 black 1x1 bricks, and an image with shades of yellow, green and blue, the program should assign the most yellowish greens to yellow bricks (or white/black for brightest/darkest ones) and bluish greens to blue bricks (or white/black for brightest/darkest ones).
  
  **Tags:** software, colour, mosaic
  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_0 on 2013-04-02. Score: 6
  
  In case anyone familiar with JavaScript is interested, I wrote this script (tested in Firefox 19) which lets the user specify the number of tiles (bricks) of each colour and browse for an image file with the same number of pixels.
  
  The luma (brightness) of each pixel is ranked and assigned one of the listed colours. Any ties are arbitrarily assigned to either possible colour (as JavaScript's sort is unstable).
  
  <http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Mona_Lisa_headcrop.jpg/20px-Mona_Lisa_headcrop.jpg> ![http://upload.wikimedia.org/wikipedia/commons/thumb/5/55/Mona_Lisa_headcrop.jpg/20px-Mona_Lisa_headcrop.jpg](https://i.sstatic.net/AM6bf.jpg) is an image which fits the example tile count.
  
  ```
  <html>
   <head><title>Make mosaic given tile colour count</title></head>
   <body><form onSubmit="return false;">
    <textarea id="counts" rows="3" cols="80">// Ordered from darkest to lightest
  colours = [ '#000000', '#993300', '#cc0000', '#ff9900', '#ffff00', '#ffffff' ];
  tiles   = [       110,        40,        80,        60,       120,        90 ];
  tileWidth = tileHeight = 16;</textarea><br />
    <input type="file" id="file" size="80" />
    <img id="img" align="top" /><br />
    <input type="button" value="Go!" onClick="loadImage()" /><br /><br />
    <canvas id="canvas" style="background:#808080;border:2px groove;"></canvas>
    <script>
  var tileWidth = 16, tileHeight = 16, colours, tiles;
  // Based on http://jsfiddle.net/fWLJ9/
  var canvas  = document.getElementById('canvas');
  var context = canvas.getContext('2d');
  var img     = document.getElementById('img');
  var imgData;
  function loadImage() {
   var url = window.URL || window.webkitURL;
   var src = url.createObjectURL(document.getElementById('file').files[0]);
   img.src = src;
   img.onload = function() {
    resizeCanvas();
    context.drawImage(img, 0, 0);
    url.revokeObjectURL(src);
    imgData = context.getImageData(0, 0, img.width, img.height).data;
    makeMosaic();
   }
  }
  function resizeCanvas() {
   canvas.width  = img.width  * tileWidth  - 1;
   canvas.height = img.height * tileHeight - 1;
  }
  function makeMosaic() {
   // Check counts are correct
   eval(document.getElementById('counts').value);
   var count = 0;
   for (var i = 0; i < colours.length; ++i) { count += tiles[i]; }
   if (count != img.width * img.height) {
    alert(img.width * img.height + ' pixels but ' + count + ' tiles!');
    return;
   }
   // Read pixels' luma
   var lumas = new Array();
   for (var y = 0; y < img.height; ++y) {
    for (var x = 0; x < img.width; ++x) {
     lumas.push({ x:x, y:y, luma:getPixelLuma(x, y) });
    }
   }
   // Sort in ascending order
   lumas.sort(function(a, b) { return a.luma - b.luma });
   // Assign pixels to colours
   resizeCanvas();
   count = 0;
   for (var i = 0; i < colours.length; ++i) {
    context.fillStyle = colours[i];
    for (var j = 0; j < tiles[i]; ++j) {
     context.fillRect(lumas[count].x * tileWidth, lumas[count].y * tileHeight,
                      tileWidth - 1, tileHeight - 1);
     ++count;
    }
   }
  }
  function getPixelLuma(x, y) {
   var pixelOffset = (x + y * img.width) * 4;
   return (0.21 * imgData[pixelOffset] + 0.72 * imgData[pixelOffset + 1] +
           0.07 * imgData[pixelOffset + 2]).toFixed(2);
  }
    </script>
   </form></body>
  </html>
  
  ```
  
  
  ---
  
  