How do I plot Precision-Recall graphs for Content-Based Image Retrieval in MATLAB?
Asked Answered
B

1

7

I am accessing 10 images from a folder "c1" and I have query image. I have implemented code for loading images in cell array and then I'm calculating histogram intersection between query image and each image from folder "c1" one-by-one. Now i want to draw precision-recall curve but i am not sure how to write code for getting "precision-recall curve" using the data obtained from histogram intersection. My code:

Inp1=rgb2gray(imread('D:\visionImages\c1\1.ppm'));
figure, imshow(Inp1), title('Input image 1');

srcFiles = dir('D:\visionImages\c1\*.ppm');  % the folder in which images exists
for i = 1 : length(srcFiles)
    filename = strcat('D:\visionImages\c1\',srcFiles(i).name);
    I = imread(filename);
    I=rgb2gray(I);
    Seq{i}=I;


end
for i = 1 : length(srcFiles)  % loop for calculating histogram intersections
    A=Seq{i};
    B=Inp1;
    a = size(A,2); b = size(B,2); 
    K = zeros(a, b);
    for j = 1:a
    Va = repmat(A(:,j),1,b);
    K(j,:) = 0.5*sum(Va + B - abs(Va - B));
    end  
end
Blubber answered 12/9, 2014 at 0:51 Comment(4)
You can see this answer for details. I don't yet see any information your question about a classification problem.Douma
So how do you define precision and recall in your context?Gonsalve
Ah CBIR... how I've missed you. I'll write an answer for you. Hold onTellurion
@ParagS.Chandakkar - This isn't a classification problem. This is a Content-Based Image Retrieval problem. It's about measuring how similar one image is with images in a database, and then ranks them based on this similarity. I would say this is more of a fuzzy classification more than anything.Tellurion
T
13

Precision-Recall graphs measure the accuracy of your image retrieval system. They're also used in the performance of any search engine really, like text or documents. They're also used in machine learning evaluation and performance, though ROC Curves are what are more commonly used.

Precision-Recall graphs are more suitable for document and data retrieval. For the case of images here, given your query image, you are measuring how similar this image is with the rest of the images in your database. You then have similarity measures for each of the database images in relation to your query image and then you sort these similarities in descending order. For a good retrieval system, you would want the images that are the most relevant (i.e. what you are searching for) to all appear at the beginning, while the irrelevant images would appear after.

Precision

The definition of Precision is the ratio of the number of relevant images you have retrieved to the total number of irrelevant and relevant images retrieved. In other words, supposing that A was the number of relevant images retrieved and B was the total number of irrelevant images retrieved. When calculating precision, you take a look at the first several images, and this amount is A + B, as the total number of relevant and irrelevant images is how many images you are considering at this point. As such, another definition Precision is defined as the ratio of how many relevant images you have retrieved so far out of the bunch that you have grabbed:

Precision = A / (A + B)

Recall

The definition of Recall is slightly different. This evaluates how many of the relevant images you have retrieved so far out of a known total, which is the the total number of relevant images that exist. As such, let's say you again take a look at the first several images. You then determine how many relevant images there are, then you calculate how many relevant images that have been retrieved so far out of all of the relevant images in the database. This is defined as the ratio of how many relevant images you have retrieved overall. Supposing that A was again the total number of relevant images you have retrieved out of a bunch you have grabbed from the database, and C represents the total number of relevant images in your database. Recall is thus defined as:

Recall = A / C

How you calculate this in MATLAB is actually quite easy. You first need to know how many relevant images are in your database. After, you need to know the similarity measures assigned to each database image with respect to the query image. Once you compute these, you need to know which similarity measures map to which relevant images in your database. I don't see that in your code, so I will leave that to you. Once you do this, you then sort on the similarity values then you go through where in the sorted similarity values these relevant images occur. You then use these to calculate your precision and recall.

I'll provide a toy example so I can show you what the graph looks like as it isn't quite clear on how you're calculating your similarities here. Let's say I have 5 images in a database of 20, and I have a bunch of similarity values between them and a query image:

rng(123); %// Set seed for reproducibility
num_images = 20;
sims = rand(1,num_images);

sims =

Columns 1 through 13

0.6965    0.2861    0.2269    0.5513    0.7195    0.4231    0.9808    0.6848    0.4809    0.3921    0.3432    0.7290    0.4386

Columns 14 through 20

0.0597    0.3980    0.7380    0.1825    0.1755    0.5316    0.5318

Also, I know that images [1 5 7 9 12] are my relevant images.

relevant_IDs = [1 5 7 9 12];
num_relevant_images = numel(relevant_IDs);

Now let's sort the similarity values in descending order, as higher values mean higher similarity. You'd reverse this if you were calculating a dissimilarity measure:

[sorted_sims, locs] = sort(sims, 'descend');

locs will now contain the image ranks that each image ranked as. Specifically, these tell you which position in similarity the image belongs to. sorted_sims will have the similarities sorted in descending order:

sorted_sims =

Columns 1 through 13

0.9808    0.7380    0.7290    0.7195    0.6965    0.6848    0.5513    0.5318    0.5316    0.4809    0.4386    0.4231    0.3980

Columns 14 through 20

0.3921    0.3432    0.2861    0.2269    0.1825    0.1755    0.0597

locs =

 7    16    12     5     1     8     4    20    19     9    13     6    15    10    11     2     3    17    18    14

Therefore, the 7th image is the highest ranked image, followed by the 16th image being the second highest image and so on. What you need to do now is for each of the images that you know are relevant, you need to figure out where these are located after sorting. We will go through each of the image IDs that we know are relevant, and figure out where these are located in the above locations array:

locations_final = arrayfun(@(x) find(locs == x, 1), relevant_IDs)

locations_final =

  5     4     1    10     3

Let's sort these to get a better understand of what this is saying:

locations_sorted = sort(locations_final)

locations_sorted = 

 1     3     4     5    10

These locations above now tell you the order in which the relevant images will appear. As such, the first relevant image will appear first, the second relevant image will appear in the third position, the third relevant image appears in the fourth position and so on. These precisely correspond to part of the definition of Precision. For example, in the last position of locations_sorted, it would take ten images to retrieve all of the relevant images (5) in our database. Similarly, it would take five images to retrieve four relevant images in the database. As such, you would compute precision like this:

precision = (1:num_relevant_images) ./ locations_sorted;

Similarly for recall, it's simply the ratio of how many images were retrieved so far from the total, and so it would just be:

recall = (1:num_relevant_images) / num_relevant_images;

Your Precision-Recall graph would now look like the following, with Recall on the x-axis and Precision on the y-axis:

plot(recall, precision, 'b.-');
xlabel('Recall');
ylabel('Precision');
title('Precision-Recall Graph - Toy Example');
axis([0 1 0 1.05]); %// Adjust axes for better viewing
grid;

This is the graph I get:

enter image description here

You'll notice that between a recall ratio of 0.4 to 0.8 the precision is increasing a bit. This is because you have managed to retrieve a successive chain of images without touching any of the irrelevant ones, and so your precision will naturally increase. It goes way down after the last image, as you've had to retrieve so many irrelevant images before finally hitting a relevant image.

You'll also notice that precision and recall are inversely related. As such, if precision increases, then recall decreases. Similarly, if precision decreases, then recall will increase.

  • The first part makes sense because if you don't retrieve that many images in the beginning, you have a greater chance of not including irrelevant images in your results but at the same time, the amount of relevant images is rather small. This is why recall would decrease when precision would increase
  • The second part also makes sense because as you keep trying to retrieve more images in your database, you'll inevitably be able to retrieve all of the relevant ones, but you'll most likely start to include more irrelevant images, which would thus drive your precision down.

In an ideal world, if you had N relevant images in your database, you would want to see all of these images in the top N most similar spots. As such, this would make your precision-recall graph a flat horizontal line hovering at y = 1, which means that you've managed to retrieve all of your images in all of the top spots without accessing any irrelevant images. Unfortunately, that's never going to happen (or at least not for now...) as trying to figure out the best features for CBIR is still an on-going investigation, and no image search engine that I have seen has managed to get this perfect. This is still one of the most broadest and unsolved computer vision problems that exist today!


Edit

You retrieved this code to compute histogram intersection from this post. They have a neat way of computing histogram intersection as:

enter image description here

n is the total number of bins in your histogram. You'll have to play around with this to get good results, but we can leave that as a parameter in your code. The code above assumes that you have two matrices A and B where each column is a histogram. You'll generate a matrix that is of a x b, where a is the number of columns in A and b is the number of columns in b. The row and column of this matrix (i,j) tells you the similarity between the ith column in A with the b jth column in B. In your case, A would be a single column which denotes the histogram of your query image. B would be a 10 column matrix that denotes the histograms for each of the database images. Therefore, we will get a 1 x 10 array of similarity measures through histogram intersection.

As such, we need to modify your code so that you're using imhist for each of the images. We can also specify an additional parameter that gives you how many bins each histogram will have. Therefore, your code will look like this. Each new line that I have placed will have a %// NEW comment beside each line.

Inp1=rgb2gray(imread('D:\visionImages\c1\1.ppm'));
figure, imshow(Inp1), title('Input image 1');
num_bins = 32; %// NEW - I'm specifying 32 bins here.  Play around with this yourself

A = imhist(Inp1, num_bins); %// NEW - Calculate histogram

srcFiles = dir('D:\visionImages\c1\*.ppm');  % the folder in which images exists
B = zeros(num_bins, length(srcFiles)); %// NEW - Store histograms of database images
for i = 1 : length(srcFiles)
    filename = strcat('D:\visionImages\c1\',srcFiles(i).name);
    I = imread(filename);
    I=rgb2gray(I);
    B(:,i) = imhist(I, num_bins); %// NEW - Put each histogram in a separate
                                  %// column              
end
%// NEW - Taken directly from the website
%// but modified for only one histogram in `A`
b = size(B,2); 
Va = repmat(A, 1, b);
K = 0.5*sum(Va + B - abs(Va - B));

Take note that I have copied the code from the website, but I have modified it because there is only one image in A and so there is some code that isn't necessary.

K should now be a 1 x 10 array of histogram intersection similarities. You would then use K and assign sims to this variable (i.e. sims = K;) in the code I have written above, then run through your images. You also need to know which images are relevant images, and you'd have to change the code I've written to reflect that.

Hope this helps!

Tellurion answered 12/9, 2014 at 14:49 Comment(23)
Thanks for the explanation. I'm finding trying to find similarity between query image and db images using "histogram intersection" as coded above. But i'm not sure that it gives me similarities. Could you please tell me, how do i find similarities using histogram intersection?Blubber
@nikhilk Histogram intersection by definition is a similarity measure. It tells you how much overlap there is between two histograms and an image can be characterized by its Histogram. As such I don't quite know what you want to ask. Do you want to know how to compute it?Tellurion
yes, i want to know how to compute histogram intersection. In the above code. I have coded for it, could you tell me whether it is correct?Blubber
@nikhilk - No it doesn't look like you are. Where did you get that definition of histogram intersection? Also, is B your query image and A your database image? You didn't give much explanation on your code you provided above.Tellurion
Yes, B is my query image and A is db image. I have used this jccaicedo.blogspot.com/2012/01/histogram-intersection.html for finding histogram intersection.Blubber
@nikhilk - Cool, that's a neat trick, but you're not using the code correctly. The code's input requires histograms where you are using the images directly. If you want to do this properly, you would need to modify the code so that you are computing histograms for each image, and then you can use the above for histogram intersection. I'll modify my post.Tellurion
@nikhilk - Done. Good luck!Tellurion
I've got a few doubts. Hope you don't mind. I have 30 images in total. 10 are dinosaurs of different types. Next 10 are roses and next 10 are buses and my query image is first image of dinosaur. Then relevant images would be 10? and relevant_IDs will be 1, 2, .., 10?Blubber
If the first 10 images you read in are dinosaurs, then yes, they'll be 1, 2, ... 10, and the number of total relevant images is 10. You must make sure you read them in this order (i.e. first 10 images you read in are dinosaurs, next 10 roses, next 10 buses)Tellurion
@nikhilk do you need any more help?Tellurion
Yes, The code works for grayscale images but how do we do for rgb images? As well as, I have P-R curves for 3 input images but need to know whether the curves are proper or not. Would you like to see those?Blubber
@nikhilk - (1) RGB images are a whole different story. You would need to quantize each colour plane, then translate the quantization to a single 1D bin for indexing. That can't be answered in a comment, and I encourage you to make another post. (2) I don't need to see the P-R curves as I'm pretty sure the above code works. As such, please consider accepting my answer and make another post requesting help with colour images.Tellurion
@Tellurion Where did the 5 retrieved images come from, wouldn't they just be the images with the highest similarity to the query image? Or are you assuming there is some other retrieval model at work that's not mentioned?Borisborja
@Borisborja There are no images here. I generated random similarity measures for the sake of calculating the precision and recall. The similarity measures and matches are all hypothetical. The point of this answer is to show how the measures are calculated, not the actual mechanism for comparing similarity.Tellurion
@Tellurion I understand that, but then why wasn't relevant_IDs = [7 16 12 5 1 ] (the indices of the largest similarity scores)Borisborja
@Borisborja You're reading the wrong variable. Those are the first five entries of locs.Tellurion
@Tellurion Right, before sorting, those are the positions of the highest scores (which I thought should be the relevant ids) so why are the relevant_IDs [1 5 7 9 12] instead of [7 16 12 5 1 ]? Are they not ranked?Borisborja
@Borisborja The relevant IDs are the images that belong to the specific category we're interested in. No these aren't ranked.Tellurion
Let us continue this discussion in chat.Borisborja
Thanks, @Tellurion for the detailed explanation. However, I have a simple question: Do we plot the PR graph based on only one query image? What if that specific query is not performing great but other queries might.Gnaw
@AbhishekBoorugu The PR graphs are done over all of your query images, regardless of however they're performing. It's one key disadvantage to this metric because you actually don't know which ones are performing badly unless you dig into the images.Tellurion
Got it! So we take an average of all the queries and then plot a graph.Gnaw
@AbhishekBoorugu Yup! That's what is known as the average precision and recall in the case where you have multiple images. Typically if you want to see how well an image is performing, you plot the PR curve with respect to that query alone but if you want a consolidated metric on performance for all of your query images, the average is a typical one. It's not the most perfect way to represent a consolidated performance across all query images, but it's typically done.Tellurion

© 2022 - 2024 — McMap. All rights reserved.