Sort PImage array by brightness

Hi there,

I have an array of say 10 images and I’m trying to re-order their indexing based on the overall brightness of each image.

Darkest image is 0 & brightest is 10.

I then want to save out each image with its new index:
image01 to image10.

I feel like I’m really close.
I can manage to sort the array of brightness values, but I need to apply this sorting to the PImage array.

I’ve been looking at this for far too long today. Hopefully, someone can point me in the right direction.

thanks internet friends!



PImage [] source; // images to re-order
float  [] bScore; //array of brightness "scores" for the images 

void setup () {
  size(20,20);
  source = new PImage[10];
  bScore = new float [10];
  
  for(int i = 0; i < 10; i ++) {
    source[i] = loadImage(sketchPath("data/test_"+i+".jpg") );
    source[i].loadPixels();
    
    bScore[i] = score(source[i]); 
  }
  bScore = sort(bScore);
  printArray(bScore);
}

//function to calculate brightness for each image
float score(PImage source) {
  float avg = 0.0;
  source.loadPixels();
  
  for(int i = 0; i < source.pixels.length; i ++) {
    float b = brightness(source.pixels[i]);    
    avg += b;
  }
    avg /= source.pixels.length;  
    return avg;
  
}

the source images are just random b&w squares:

Do something like:

for (int i = 0; i < bScore.length; i++) {
  bScore[i].save("image"+i);
}

PS: this is untested, expect errors.

Hmm thanks for the idea. That didn’t work though.

I’ve not used that sort of ‘dot syntax’ with save before.

this is terrible but it works and might at least point you in the correct direction.

PImage [] source; // images to re-order
float  [] bScore; //array of brightness "scores" for the images 

void setup () {
  size(20,20);
  source = new PImage[9];
  bScore = new float [9];
  
  for(int i = 0; i < 9; i ++) {
    source[i] = loadImage(sketchPath("data/img"+i+".png") );
    
    bScore[i] = score(source[i]);
  }
  sortImagesByBrightness();
  for (int i = 0; i < bScore.length; i++) {
    source[i].save("data/simg" + i + ".png");
  }
}

void sortImagesByBrightness() {
  int numUnsorted = source.length;
  int max = 0;
  while(numUnsorted > 0) {
    max = 0;
    for(int i = 1; i < numUnsorted; i++) {
      if(bScore[max] < bScore[i]) max = i;
    }
    PImage temp1 = source[max];
    source[max] = source[numUnsorted - 1];
    source[numUnsorted - 1] = temp1;
    
    float temp2 = bScore[max];
    bScore[max] = bScore[numUnsorted - 1];
    bScore[numUnsorted - 1] = temp2;
    
    numUnsorted--;
  }
}

//function to calculate brightness for each image
float score(PImage source) {
  float avg = 0.0;
  source.loadPixels();
  
  for(int i = 0; i < source.pixels.length; i ++) {
    float b = brightness(source.pixels[i]);    
    avg += b;
  }
    avg /= source.pixels.length;  
    return avg; 
}

these are the images i used

img0 img1 img2 img3 img4 img5 img6 img7 img8

3 Likes

Thank you so much for your help :slight_smile:

Here is an additional solution for the same problem.

It loads the three Processing logos 1 2 3, then sorts them by brightness, 3, 1, 2.

The code creates a Comparator – a Java object for comparing two objects and determining which is bigger / first / brighter / more – and then passes the comparator to Arrays.sort(), which uses it to sort a PImage[] array.

import java.util.Arrays;
/**
 * Sort PImage array by brightness
 * 
 * https://discourse.processing.org/t/sort-pimage-array-by-brightness/19423
 */
import java.util.Comparator;

void setup() {
  size(720,240);
  PImage[] imgs = new PImage[3];
  imgs[0] = loadImage("Processing_1_logo.png");
  imgs[1] = loadImage("Processing_2_logo.png");
  imgs[2] = loadImage("Processing_3_logo.png");
  Arrays.sort(imgs, CMP_PIMG_BRIGHT_AVG);
  for(int i=0; i<imgs.length; i++) {
    image(imgs[i],0,0);
    text((int)brightAvg(imgs[i]), 20, 20);
    translate(240,0);
  }
  save("PImageBrightSort--screenshot.png");
}

long brightSum(PImage img) {
  img.loadPixels();
  long bright = 0;
  for(int i=0; i<img.pixels.length; i++) bright+=brightness(img.pixels[i]);
  return bright;
}

double brightAvg(PImage img) {
  long bright = brightSum(img);
  return bright/(double)img.pixels.length;
}

Comparator<PImage> CMP_PIMG_BRIGHT_SUM = new Comparator<PImage>() {
  @ Override public final int compare(final PImage a, final PImage b) {
    long asum = brightSum(a);
    long bsum = brightSum(b);
    if(asum>bsum) return 1;
    if(bsum>asum) return -1;
    return 0;
  }
};

Comparator<PImage> CMP_PIMG_BRIGHT_AVG = new Comparator<PImage>() {
  @ Override public final int compare(final PImage a, final PImage b) {
    double aAvg = brightAvg(a);
    double bAvg = brightAvg(b);
    if(aAvg>bAvg) return 1;
    if(bAvg>aAvg) return -1;
    return 0;
  }
};

There are some subtleties to sorting PImage by brightness –

  1. what if the images are two different sizes?
  2. what if either image has transparent pixels?

This example provides two Comparators – a sum (raw pixels) and an average – and uses the average.

See previous discussion with PVector: Sort PVector array by distance

2 Likes

Nice one! thanks very much for your help.

1 Like