1

I'm trying to write the simplest possible image comparison function. The idea is to have a target image and a collection of n number of different images.

The goal is to pick the image which is most similar to the target image.

So far my method consists of defining the euclidean HSB distance from pixel to pixel in a resized image and have been trying to do a PImage function that returns the winner image. I already wrote a float function that ranks the image list from winner to loser but I would like to skip this step to make the process way more concise.

The issue is at the PImage difference(PImage){ function, the program outputs an error on the line:

        float x1 = brightness(imageKey.pixels[i]);

The error is ArrayIndexOutOfBoundsException

Here's the entire code:

//CLICK ON S TO SAVE FRAMES TO FOLDER


int series = 50; //
PImage[] collection = new PImage[series];

PImage imageKey,imageKey2, imageKeyHUE, imageKeySUM, imageKeySAT; //target image alias with ready operations
int imageWidth = 800;
int leftAlign = 850  ;

void setup()
{
size(1200,600);
background(255);
frameRate(random(1,10.0));

for ( int i = 0; i< collection.length; i++ )
{
collection[i] = loadImage( "Image_"+ i + ".jpg" );  
}

//_____________________________________________TARGET IMAGE AND NAME TEXT

textSize(10);
fill(0);
text("target image", leftAlign, 220);
textSize(15);
text("central london", leftAlign, 240);
text("comparison methods", leftAlign, 290);
//_____________________________________________________________________BUTTONS
imageKey = loadImage("Image_0.jpg");
imageKey.resize(240, 180);
image(imageKey, leftAlign,25);

   imageKeySAT= loadImage("Image_0.jpg");
    imageKeySAT.resize(60,60);
    imageKeySAT = saturation(imageKeySAT);
    image(imageKeySAT, leftAlign+140,300);

      imageKeySUM = loadImage("Image_0.jpg");
      imageKeySUM.resize(60,60);
      imageKeySUM = sum(imageKeySUM);
      image(imageKeySUM, leftAlign+70,300);

          imageKeyHUE = loadImage("Image_0.jpg");
          imageKeyHUE.resize(60,60);
          imageKeyHUE = hue(imageKeyHUE);
          image(imageKeyHUE, leftAlign,300);

          textSize(20);
          text("CLICK HERE TO", leftAlign, 430);
          text("STOP AT WINNER", leftAlign, 450);



}


void draw()
{

//______________________________________________SHOW IMAGES ARRAY
image(collection[int(random(0,series))],0,0);

//______________________________________________HISTOGRAMS
histogramhue();
histogramsat();
histogrambright();

//______________________________________________SUM METHOD
//float Vector_Approach(PImage sumSatin){


//}

}


void keyPressed()
{
  if(key=='s') saveFrame("images/image-######.jpg");
}


PImage difference(PImage satin)
{
  colorMode(HSB);

    satin.loadPixels();
    imageKey.loadPixels();

    PImage satout = createImage(satin.width, satin.height, RGB);

    satout.loadPixels();
    for(int i = imageWidth; i<satout.pixels.length-imageWidth; i++)
    {
        float x1 = brightness(imageKey.pixels[i]);
        float b0 = brightness(satin.pixels[i]);
       // float y1 = brightness(satin.pixels[i+1]);

        float value = x1-b0;

        satout.pixels[i] = color(0,0,value);

    }
    satout.updatePixels();

    return satout;    
}

void mouseReleased(){
//______________________________________________BUTTON OVER
for ( int i = 0; i< collection.length; i++ )
  if (mouseX > leftAlign && mouseX < (leftAlign + 60) && mouseY > 300 && mouseY < 360){

            collection[i] = loadImage( "Image_"+ i + ".jpg" );  
            collection[i] = hue(collection[i]); histogramhue();
                  noStroke(); fill(255); rect(leftAlign,360,200,40); fill(0);
                  textSize(10);text("comparison by hue", leftAlign, 380);

} else if (mouseX > (leftAlign + 70) && mouseX < (leftAlign + 130) && mouseY > 300 && mouseY < 360)
{

            collection[i] = loadImage( "Image_"+ i + ".jpg" );  
            collection[i] = sum(collection[i]);
                  noStroke(); fill(255); rect(leftAlign,360,200,40); fill(0);
                  textSize(10);text("comparison by sum", leftAlign, 380);

}else if (mouseX > (leftAlign + 140) && mouseX < (leftAlign + 200) && mouseY > 300 && mouseY < 360)
{

          collection[i] = loadImage( "Image_"+ i + ".jpg" );  
          collection[i] = saturation(collection[i]);
                  noStroke(); fill(255); rect(leftAlign,360,200,40); fill(0);
                  textSize(10);text("comparison by saturation", leftAlign, 380);


}else if (mouseX > leftAlign && mouseX < 1200 && mouseY > 340 && mouseY < 600)
{

          collection[i] = loadImage( "Image_"+ i + ".jpg" );  
          collection[i] = difference(collection[i]);
                  noStroke(); fill(255); rect(leftAlign,360,200,40); fill(0);
                  textSize(10);text("WINNER IMAGE!!!!", leftAlign, 380);
}else{

    collection[i] = loadImage( "Image_"+ i + ".jpg" );  
    noStroke(); fill(255); rect(leftAlign,360,200,40); fill(0);

}
}



/*          HSB PImage Methods             */

 //HUE              ------->    /** CHOSEN METHOD**/
 //SATURATION       ------->    /** CHOSEN METHOD**/
 //SUM              ------->    /** CHOSEN METHOD**/





PImage hue(PImage satin)
{
      colorMode(HSB);
      satin.loadPixels();

      PImage satout = createImage(satin.width, satin.height, HSB);

      satout.loadPixels();

      for (int j = 0; j < satout.pixels.length; j++)
      {
      satout.pixels[j] = color(hue(satin.pixels[j]),255,255);
      }

satout.updatePixels();

return satout;
} 

PImage saturation(PImage satin)
{
      colorMode(HSB);
      satin.loadPixels();
      PImage satout = createImage(satin.width, satin.height, RGB);
      satout.loadPixels();
      for (int j = 0; j < satout.pixels.length; j++)
      {
      satout.pixels[j] = color(saturation(satin.pixels[j]));
      }

satout.updatePixels();
//colorMode(RGB);
return satout;
} 



PImage sum(PImage satin)
{
  colorMode(HSB);

    satin.loadPixels();

    PImage satout = createImage(satin.width, satin.height, RGB);

    satout.loadPixels();
    for(int i = imageWidth; i<satout.pixels.length-imageWidth; i++)
    {
        float b0 = brightness(satin.pixels[i]);
        float x1 = brightness(satin.pixels[i-1]);
        float y1 = brightness(satin.pixels[i-imageWidth]);

        float xdiff = b0-x1;
        float ydiff = b0-y1;

        float value = (510 + xdiff + ydiff)/3;

        satout.pixels[i] = color(0,0,value);

    }
    satout.updatePixels();

    return satout;    
}

//REFERENCE HISTOGRAM TAKEN FROM A PROGRAMMING HANDBOOK FOR VISUAL DESIGNERS AND ARTISTS BY BEN FRY ET AL

void histogramhue(){
PImage img = loadImage("Image_0.jpg");
int[] hist = new int[600];

// Calculate the histogram
for (int i = 0; i < img.width; i++) {
  for (int j = 0; j < img.height; j++) {
    int hue = int(hue(get(i, j)));
    hist[hue]++; 
  }
}

int histMax = max(hist);

stroke(255,250); strokeWeight(5);
// Draw half of the histogram (skip every second value)
for (int i = 0; i < img.width; i += 20) {
  int which = int(map(i, 0, img.width, 0, 255));
  int y = int(map(hist[which], 0, histMax, img.height, 0));
  line(i, img.height, i, y);
}}


void histogramsat(){
PImage img = loadImage("Image_0.jpg");

int[] hist = new int[600];

for (int i = 0; i < img.width; i++) {
  for (int j = 0; j < img.height; j++) {
    int sat = int(saturation(get(i, j)));
    hist[sat]++; 
  }
}

int histMax = max(hist);

stroke(255,150);strokeWeight(10);
for (int i = 0; i < img.width; i += 20) {
  int which = int(map(i, 0, img.width, 0, 255));
  int y = int(map(hist[which], 0, histMax, img.height, 0));
  line(i, img.height, i, y);
}}

void histogrambright(){
PImage img = loadImage("Image_0.jpg");

int[] hist = new int[600];


for (int i = 0; i < img.width; i++) {
  for (int j = 0; j < img.height; j++) {
    int bright = int(brightness(get(i, j)));
    hist[bright]++; 
  }
}
int histMax = max(hist);

stroke(255, 150);strokeWeight(20);
for (int i = 0; i < img.width; i += 20) {
  int which = int(map(i, 0, img.width, 0, 255));
  int y = int(map(hist[which], 0, histMax, img.height, 0));
  line(i, img.height, i, y);
}}
Kevin Workman
  • 41,537
  • 9
  • 68
  • 107
  • Since you want to compare images you can use the technique called **correlation**. Correlation ranges between 0 - 1. As long as the images being compared are close to each other the correlation factor will be close to 1, else otherwise. – Jeru Luke Jan 11 '17 at 05:18
  • Just search for the winner, i.e. the image with the smallest distance, no need to sort. It doesn't get any more concise. – NoDataDumpNoContribution Jan 11 '17 at 09:06
  • @Trilarion I get the concept pretty well, I just can't get the PImage or float function to output the winner image. Any help on that? – Mercedes Landa Jan 12 '17 at 21:06
  • @MercedesLanda Depending on the time available for this task, you might want to also look into comparing the mean structural similarity index between the two images. It should be possible to use the `getMSSIM` function from the [OpenCV PSNR/SSIM example](http://docs.opencv.org/2.4/doc/tutorials/gpu/gpu-basics-similarity/gpu-basics-similarity.html). The example code is c++ and you won't need the GPU part, but you could use the [OpenCV Processing library](https://github.com/atduskgreg/opencv-processing) to convert your `PImages` to OpenCV's `Mat` format, compute SSIM then convert the result back – George Profenza Aug 22 '17 at 12:13

1 Answers1

0

In isolation your function does seem to work:

PImage imageKey,testImage;
int imageWidth = 800;
int imageHeight = 600;

void setup(){
  size(1600,600);
  //fake imageKey
  imageKey = getNoise(imageWidth,imageHeight);

  //fake test image
  testImage = getNoise(imageWidth,imageHeight);

  image(testImage,0,0);
  image(difference(testImage),800,0);
}

PImage getNoise(int width,int height){
  PImage out = createImage(width,height,RGB);

  for(int i = 0 ; i < out.pixels.length; i++) 
    out.pixels[i] = color(random(255),random(255),random(255));

  out.updatePixels();

  return out;
}

PImage difference(PImage satin)
{
  colorMode(HSB);

  satin.loadPixels();
  imageKey.loadPixels();

  PImage satout = createImage(satin.width, satin.height, RGB);

  satout.loadPixels();
  for (int i = imageWidth; i<satout.pixels.length-imageWidth; i++)
  {
    float x1 = brightness(imageKey.pixels[i]);
    float b0 = brightness(satin.pixels[i]);
    // float y1 = brightness(satin.pixels[i+1]);

    float value = x1-b0;
    //println(i,x1,b0,x1-b0,value);

    satout.pixels[i] = color(0, 0, value);
  }
  satout.updatePixels();

  return satout;
}

I can't test your actual setup as I don't have access to your images, but the ArrayIndexOutOfBoundsException is probably because your i counter goes beyond the number of pixels in imageKey. You can test this by putting checking if i < imageKey.pixels.length. My guess is the images aren't the same dimensions and therefore don't have the same number of pixels.

Other notes that are going slightly off-topic:

  1. Your difference() function is tightly coupled to the imageKey and imageWidth variables. You might want to make your functions loosely coupled so they can be reused easily in other contexts. You could start by making these two variables extra parameters/arguments of the function
  2. You might also want to look at euclidean distance between colours (in a perceptual colour space such as Lab*). Have a look at this answer.Even though it's an OpenFrameworks answer, it should be easy to adapt to Processing's color and PVector types.
Community
  • 1
  • 1
George Profenza
  • 50,687
  • 19
  • 144
  • 218