views:

975

answers:

4

I'm plotting on top of an image in Matlab. Sometimes, I can't see what's being plotted because the color of the image underneath is too close to the color of the image at the same location. I could just always change the color of the plot (e.g. from 'rx' to 'bx'), but that's cumbersome.

Is it possible to plot the inverse color of what's underneath so that the overlay is always visible?

+2  A: 

I believe it's not possible to automatically invert color of the plot based on background image. You probably can rasterize the plot and somehow combine it with the image (xor?).

Here is another solution. If you can use closed markers, like circle, square, triangle, you can set different MarkerEdgeColor and MarkerFaceColor, so the marker will be visible against different colors.

h = plot(1:5,'o');
set(h,'MarkerEdgeColor','b')
set(h,'MarkerFaceColor','r')
yuk
+1  A: 

There's no automated way I know of to have your plotted points change color based on the color of the pixel behind them. Keep in mind that you don't have to use just the eight predefined color specifications (i.e. 'r' for red or 'b' for blue). You could pick an RGB color specification for your plotted points that isn't common in your underlying image. For example:

h = plot(0,0,'Marker','x','Color',[1 0.4 0.6]);  %# Plot a pink x

You could programmatically find the least common color with some simple code that picks the least frequently used color values in an image. Here's one example:

rawData = imread('peppers.png');  %# Read a sample RGB image
imData = reshape(rawData,[],3);   %# Reshape the image data
N = hist(double(imData),0:255);   %# Create a histogram for the RGB values
[minValue,minIndex] = min(N);     %# Find the least used RGB values
plotColor = (minIndex-1)./255;    %# The color for the plotted points
image(rawData);                   %# Plot the image
hold on;
hp = plot(500.*rand(1,20),350.*rand(1,20),...  %# Plot some random points
          'Marker','o','LineStyle','none',...
          'MarkerFaceColor',plotColor,'MarkerEdgeColor',plotColor);

The above code first reshapes the image data into an M-by-3 matrix, where M is the number of image pixels and the three columns contain the red, green, and blue values, respectively. A binning of values is done for each column using HIST, then the value with the smallest bin (i.e. lowest frequency) is found for each column. These three values become the RGB triple for the plot color. When the image is overlaid with random points of this color, it gives the following plot:

alt text

Notice in this case that the above code picks a bright blue color for the plot points, which happens to be a color not appearing in the image and thus giving good contrast.

gnovice
Just imagine how this will fail on a GIF of almost any natural scene :-)
AVB
@AB: Admittedly, it's a pretty simple algorithm I used as an example, so there may be some images that would be tricky to apply this sort of approach to. But in general it should end up picking a color that isn't used much, minimizing the likelihood that the points end up over a matching pixel.
gnovice
I like the idea. However, I would use only, say, 16 bins to find the 'color region' that is least used. You can then either take the center of the bin as your plot color, or do another search within the region.
Jonas
@Jonas: Interesting idea. There are probably a lot of variations of the code that could be made. I was just throwing a simple algorithm idea out there as food for thought.
gnovice
+2  A: 

This is possible.

Assuming you know what your image is like, you can do the following:

  1. Read the color at the coordinates over which you plot

  2. Invert the color

  3. Use scatter

    %# load rgb color image - this is maybe not the best example, since it's all so dark. X = double(imread('ngc6543a.jpg'))/255; %# since it's quite a dark image, invert half of it X(:,1:floor(size(X,2)/2),:) = 1-X(:,1:floor(size(X,2)/2),:);

    %# create some plot data plotX = rand(50,1) * size(X,1); plotY = rand(50,1) * size(X,2);

    %# read RGB components (it must be possible to do this more efficently, but I don't see %# it right now plotColors = zeros(length(plotX),3); for c = 1:3 plotColors(:,c) = interp2(X(:,:,c),plotY,plotX); end

    %# invert plotColors = 1-plotColors; %# If you want highly different colors, and avoid the problem that grey is the inverse %# to grey, you could use %# plotColors = round(1-plotColors); %# This gives you the choice of wrgbcmyk, whichever is farthest from the image color

    %# plot figure,imshow(X) hold on scatter(plotY,plotX,[],plotColors)

Edit: this has now been tested and it should work.

Edit2: inverting half the original image makes it clearer how this works

Edit3: incorporated a modified form of gnovice's suggestion

Edit4: fixed the bug as pointed out by AB

Jonas
One small drawback... The inversion won't give very distinct colors when they are close to [0.5 0.5 0.5]. Perhaps it would be better to choose either white or black based on which one is furthest from the pixel color (i.e. biggest total difference in RGB values).
gnovice
Good point. I guess instead of using the inverse (which was what the OP asked for), one could use the rounded inverse.
Jonas
A: 

This is very simple and looks fairly nice, if you need to plot scattered points:

%# load rgb color image
X = double(imread('ngc6543a.jpg'))/255;
%# since it's quite a dark image, invert half of it
X(:,1:floor(size(X,2)/2),:) = 1-X(:,1:floor(size(X,2)/2),:);

%# create some plot data
plotX = rand(50,1) * size(X,1);
plotY = rand(50,1) * size(X,2);

%# plot
figure,imshow(X)
hold on
scatter(plotY,plotX,'xw');
scatter(plotY,plotX,'ok');

If you need something more complicated, leave me a comment.

AVB
This does not answer the question, so I would write that clearly to avoid being down voted.
kigurai
@kigurai: Did you try to read the code, or even maybe run it, before you threaten people?
AVB
Yes. And I can see that it plots the data both as a white cross and a black ring, which means it will probably be quite visible on most images. However, the question is specifically about plotting the inverse color of the background on the image, which is not in anyway close to what you are suggesting. Not saying your answer is a bad idea (although two scatter plots will obviously mean longer execution time), only that it does not answer the direct question.
kigurai
Also, if you copy-paste code from other solutions, you may want to mention what you're basing your solution on.
Jonas
@kigurai: if you read the other answers and comments, you'll see that "inverse" (1) is ill-defined and (2) probably does not correspond to the poster's intent. I'm trying to solve the real problem. You are trying to solve the question as stated. Both are equally valid.
AVB
@Jonas: thanks! The copy-paste is too obvious to mention, I thought. And by the way, there is a bug in your code - you are only inverting the red channel; I've corrected it.
AVB
Thanks for pointing out the bug.
Jonas