0

In this picture, you can see that each brick has chosen a colour based on the most prominent colour value of the picture. I'm interested in painting a picture on a brick wall, but having the bricks only be one colour each.

I think the desired effect would be better if the bricks chose not the most prominent colour of the picture, but the average colour.

enter image description here

1

I'm sure there could be different approaches, here are couple ideas.

  1. Scripting approach. A script would create selections based on blocks and fill them. I made two versions: one fills with average color of the selection, the other selects a color in the center of a block. Results:

    enter image description here

    // using average blur
    function main() {
      var img = [activeDocument.width.as("px"),activeDocument.height.as("px")];
      var block = [40,20];
      var x = Math.ceil(img[0]/block[0]);
      var y = Math.ceil(img[1]/block[1]);
      var delta;
    
      for (var i = 0; i < y; i++) {
        for (var k = 0; k < x; k++) {
          delta = i % 2 == 0 ? 0 : -block[0]/2;
          rectSelection([
            block[0] * k + delta,
            block[1] * i,
            block[0] * k + block[0] + delta,
            block[1] * i + block[1],
            ])
          executeAction(cTID('Avrg'), undefined, DialogModes.NO);
        }
      }
      activeDocument.selection.deselect();
    
      function rectSelection(_data)
      {
        var desc = new ActionDescriptor();
        var ref1 = new ActionReference();
        ref1.putProperty(charIDToTypeID('Chnl'), charIDToTypeID('fsel'));
        desc.putReference(charIDToTypeID('null'), ref1);
        var desc26 = new ActionDescriptor();
        desc26.putUnitDouble(charIDToTypeID('Top '), charIDToTypeID('#Pxl'), _data[1]);
        desc26.putUnitDouble(charIDToTypeID('Left'), charIDToTypeID('#Pxl'), _data[0]);
        desc26.putUnitDouble(charIDToTypeID('Btom'), charIDToTypeID('#Pxl'), _data[3]);
        desc26.putUnitDouble(charIDToTypeID('Rght'), charIDToTypeID('#Pxl'), _data[2]);
        desc.putObject(charIDToTypeID('T   '), charIDToTypeID('Rctn'), desc26);
        executeAction(charIDToTypeID('setd'), desc, DialogModes.NO);
      }
    }
    app.activeDocument.suspendHistory("temp", "main()");
    

     

    // using color sampler
    function main() {
      var img = [activeDocument.width.as("px"),activeDocument.height.as("px")];
      var block = [40,20];
      var x = Math.ceil(img[0]/block[0]);
      var y = Math.ceil(img[1]/block[1]);
      var delta;
    
      var colorSamplerRef = app.activeDocument.colorSamplers.add([0,0])
    
      for (var i = 0; i < y; i++) {
        for (var k = 0; k < x; k++) {
          delta = i % 2 == 0 ? 0 : -block[0]/2;
    
          rectSelection([
            block[0] * k + delta,
            block[1] * i,
            block[0] * k + block[0] + delta,
            block[1] * i + block[1],
            ])
          samplerx = block[0] * k + block[0]/2 + delta;
          while (samplerx >= img[0]) {
            samplerx -= 5
          }
          samplery = block[1] * i + block[1]/2
          while (samplery >= img[1]) {
            samplery -= 5
          }
          colorSamplerRef.move([samplerx,samplery])
          fill({
            color: [colorSamplerRef.color.hsb.hue,colorSamplerRef.color.hsb.saturation,colorSamplerRef.color.hsb.brightness]
          })
        }
      }
      activeDocument.selection.deselect();
    
      app.activeDocument.colorSamplers.removeAll()
    
      function rectSelection(_data)
      {
        var desc = new ActionDescriptor();
        var ref1 = new ActionReference();
        ref1.putProperty(charIDToTypeID('Chnl'), charIDToTypeID('fsel'));
        desc.putReference(charIDToTypeID('null'), ref1);
        var desc26 = new ActionDescriptor();
        desc26.putUnitDouble(charIDToTypeID('Top '), charIDToTypeID('#Pxl'), _data[1]);
        desc26.putUnitDouble(charIDToTypeID('Left'), charIDToTypeID('#Pxl'), _data[0]);
        desc26.putUnitDouble(charIDToTypeID('Btom'), charIDToTypeID('#Pxl'), _data[3]);
        desc26.putUnitDouble(charIDToTypeID('Rght'), charIDToTypeID('#Pxl'), _data[2]);
        desc.putObject(charIDToTypeID('T   '), charIDToTypeID('Rctn'), desc26);
        executeAction(charIDToTypeID('setd'), desc, DialogModes.NO);
      }
    
      function fill(_data)
      {
        var desc = new ActionDescriptor();
        desc.putEnumerated(charIDToTypeID('Usng'), charIDToTypeID('FlCn'), charIDToTypeID('Clr '));
        var desc79 = new ActionDescriptor();
        desc79.putUnitDouble(charIDToTypeID('H   '), charIDToTypeID('#Ang'), _data.color[0]);
        desc79.putDouble(charIDToTypeID('Strt'), _data.color[1]);
        desc79.putDouble(charIDToTypeID('Brgh'), _data.color[2]);
        desc.putObject(charIDToTypeID('Clr '), charIDToTypeID('HSBC'), desc79);
        executeAction(charIDToTypeID('Fl  '), desc, DialogModes.NO);
      };
    }
    app.activeDocument.suspendHistory("temp", "main()");
    

     

  2. A completely different approach, also very fast. I layed blocks in Blender and generate UVs based on viewport projection, then scaled each UV island individually. This way I could've easily changed blocks that were caught on black lines:

    enter image description here

2
  • Wow that is amazing. Which programming language is the first one? Can it be done in Photoshop? Feb 25 at 12:43
  • 1
    @VictorNielsen it's javascript in Photoshop Feb 25 at 13:31

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.