Plot of mean square error [EDIT] - matlab

If this be the formula for MSE for RGB images A,B of same size 256*200, then how to obtain a line plot for every pixel with x axis representing pixels and y axis representing the MSE values
MSE = reshape(mean(mean((double(A) - double(B)).^2,2),1),[1,3])
There are only two images A ,B. The plot should illustrate change between each pixel of A and B which is meant by MSE.

If you want to display the changes "between each pixel" then what you're showing is not mean squared errors any more -- there's no averaging going on. (Unless you intend to average across the three colour planes, but I don't recommend that: changes in R,G,B are not equally salient to the human visual system. If you really must do this, you might want to weight them, say, 2:4:1 for something a bit more representative, but this is still ad hoc and not likely to give a very accurate idea of what differences will look biggest.)
Of course it's perfectly reasonable to want to see the per-pixel errors, but I wouldn't recommend using a line plot to display them; it's likely to be confusing rather than informative. Rather, display them as an image:
errs = (double(A)-double(B)).^2;
image(errs / max(errs(:)));
axis image;
which you can then compare by eye with A and B to see what image regions/features/... correspond to worse errors. The brightness and colour of each pixel indicate the amount of error and how it's distributed across the R, G, and B planes.
On the other hand, perhaps what you actually need is mean squared error over individual rows, or columns, of the image. In that case, after creating errs as above, use mean to compute the row or column means; that will give you a 256-by-1-by-3 image or a 1-by-200-by-3 image; now I would suggest plotting R,G,B curves separately unless you (probably foolishly in my opinion, as mentioned above) insist on averaging the planes.
row_errs = mean(errs,2); % this is now of size [n,1,3]
now row_errs(:,:,1) is a vector of MS-across-rows red errors, row_errs(:,:,2) is a vector of MS-across-rows green errors, etc. You can feed these to plot.

Related

Automatic detection of B/W image against colored background. What should I do when local thresholding doesn't work?

I have an image which consists of a black and white document against a heterogeneous colored background. I need to automatically detect the document in my image.
I have tried Otsu's method and a Local Thresholding method, but neither were successful. Also, edge detectors like Canny and Sobel didn't work.
Can anyone suggest some method to automatically detect the document?
Here's an example starting image:
After using various threshold methods, I was able to get the following output:
What follows is an automated global method for isolating an area of low color saturation (e.g. a b/w page) against a colored background. This may work well as an alternative approach when other approaches based on adaptive thresholding of grayscale-converted images fail.
First, we load an RGB image I, covert from RGB to HSV, and isolate the saturation channel:
I = imread('/path/to/image.jpg');
Ihsv = rgb2hsv(I); % convert image to HSV
Isat = Ihsv(:,:,2); % keep only saturation channel
In general, a good first step when deciding how to proceed with any object detection task is to examine the distribution of pixel values. In this case, these values represent the color saturation levels at each point in our image:
% Visualize saturation value distribution
imhist(Isat); box off
From this histogram, we can see that there appear to be at least 3 distinct peaks. Given that our target is a black and white sheet of paper, we’re looking to isolate saturation values at the lower end of the spectrum. This means we want to find a threshold that separates the lower 1-2 peaks from the higher values.
One way to do this in an automated way is through Gaussian Mixture Modeling (GMM). GMM can be slow, but since you’re processing images offline I assume this is not an issue. We’ll use Matlab’s fitgmdist function here and attempt to fit 3 Gaussians to the saturation image:
% Find threshold for calling ROI using GMM
n_gauss = 3; % number of Gaussians to fit
gmm_opt = statset('MaxIter', 1e3); % max iterations to converge
gmmf = fitgmdist(Isat(:), n_gauss, 'Options', gmm_opt);
Next, we use the GMM fit to classify each pixel and visualize the results of our GMM classification:
% Classify pixels using GMM
gmm_class = cluster(gmmf, Isat(:));
% Plot histogram, colored by class
hold on
bin_edges = linspace(0,1,256);
for j=1:n_gauss, histogram(Isat(gmm_class==j), bin_edges); end
In this example, we can see that the GMM ended up grouping the 2 far left peaks together (blue class) and split the higher values into two classes (yellow and red). Note: your colors might be different, since GMM is sensitive to random initial conditions. For our use here, this is probably fine, but we can check that the blue class does in fact capture the object we’d like to isolate by visualizing the image, with pixels colored by class:
% Visualize classes as image
im_class = reshape(gmm_class ,size(Isat));
imagesc(im_class); axis image off
So it seems like our GMM segmentation on saturation values gets us in the right ballpark - grouping the document pixels (blue) together. But notice that we still have two problems to fix. First, the big bar across the bottom is also included in the same class with the document. Second, the text printed on the page is not being included in the document class. But don't worry, we can fix these problems by applying some filters on the GMM-grouped image.
First, we’ll isolate the class we want, then do some morphological operations to low-pass filter and fill gaps in the objects.
Isat_bw = im_class == find(gmmf.mu == min(gmmf.mu)); %isolate desired class
opened = imopen(Isat_bw, strel('disk',3)); % morph open
closed = imclose(Isat_bw, strel('disk',50)); % morph close
imshow(closed)
Next, we’ll use a size filter to isolate the document ROI from the big object at the bottom. I’ll assume that your document will never fill the entire width of the image and that any solid objects bigger than the sheet of paper are not wanted. We can use the regionprops function to give us statistics about the objects we detect and, in this case, we’ll just return the objects’ major axis length and corresponding pixels:
% Size filtering
props = regionprops(closed,'PixelIdxList','MajorAxisLength');
[~,ridx] = min([props.MajorAxisLength]);
output_im = zeros(numel(closed),1);
output_im(props(ridx).PixelIdxList) = 1;
output_im = reshape(output_im, size(closed));
% Display final mask
imshow(output_im)
Finally, we are left with output_im - a binary mask for a single solid object corresponding to the document. If this particular size filtering rule doesn’t work well on your other images, it should be possible to find a set of values for other features reported by regionprops (e.g. total area, minor axis length, etc.) that give reliable results.
A side-by-side comparison of the original and the final masked image shows that this approach produces pretty good results for your sample image, but some of the parameters (like the size exclusion rules) may need to be tuned if results for other images aren't quite as nice.
% Display final image
image([I I.*uint8(output_im)]); axis image; axis off
One final note: be aware that the GMM algorithm is sensitive to random initial conditions, and therefore might randomly fail, or produce undesirable results. Because of this, it's important to have some kind of quality control measures in place to ensure that these random failures are detected. One possibility is to use the posterior probabilities of the GMM model to form some kind of criteria for rejecting a certain fit, but that’s beyond the scope of this answer.

How to make a heat map on top of worldmap using hist3 in MATLAB?

My x-axis is latitudes, y-axis is longitudes, and z-axis is the hist3 of the two. It is given by: z=hist3(location(:,1:2),[180,360]), where location(:,1) is the latitude column, and location(:,2) is the longitude column.
What I now want is, instead of plotting on a self-created XY plane, I want to plot the same on a worldmap. And instead of representing the frequency of each latitude-longitude pair with the height of the bars of hist3, I want to represent the frequency of each location by a heat map on top of the world map, corresponding to each latitude-longitude pair's frequency on the dataset. I have been searching a lot for this, but have not found much help. How to do this? I could only plot the skeleton of the worldmap like this:
worldmap world
load geoid
geoshow(geoid, geoidrefvec, 'DisplayType', 'texturemap');
load coast
geoshow(lat, long)
I don't know what the colour is being produced based on.
Additionally, if possible, I would also like to know how to plot the hist3 on a 3D map of the world (or globe), where each bar of the hist3 would correspond to the frequency of each location (i.e., each latitude-longitude pair). Thank you.
The hist3 documentation, which you can find here hist3, says:
Color the bars based on the frequency of the observations, i.e. according to the height of the bars. set(get(gca,'child'),'FaceColor','interp','CDataMode','auto');
If that's not what you need, you might wanna try it with colormap. More info about it here colormap. I haven't tried using colormap on histograms directly, so If colormap doesn't help, then you can try creating a new matrix manually which will have values in colors instead of the Z values the histogram originally had.
To do that, you need to first calculate the maximum Z value with:
maxZ=max(Z);
Then, you need to calculate how much of the colors should overlap. For example, if you use RGB system and you assign Blue for the lowest values of the histogram, then Green for the middle and Red for the High, and the green starts after the Blue with no overlap, than it will look artificial. So, if you decide that you will have, for example overlapping of 10 values, than, having in mind that every R, G and B component of the RGB color images have 255 values (8 bits) and 10 of each overlap with the former, that means that you will have 255 values (from the Blue) + 245 values (From the Green, which is 255 - 10 since 10 of the Green overlap with those of the Blue) + 245 (From the Red, with the same comment as for the Green), which is total amount of 745 values that you can assign to the new colored Histogram.
If 745 > maxZ there is no logic for you to map the new Z with more than maxZ values. Then you can calculate the number of overlaping values in this manner:
if 745 > maxZ
overlap=floor(255- (maxZ-255)/2)
end
At this point you have 10 overlapping values (or more if you still think that it doesn't looks good) if the maximum value of the Z is bigger than the total amount of values you are trying to assign to the new Z, or overlap overlapping values, if the maximum of Z is smaller.
When you have this two numbers (i.e. 745 and maxZ), you can write the following code so you can create the newZ.
First you need to specify that newZ is of the same size as Z. You can achieve that by creating a zero matrix with the same size as Z, but having in mind that in order to be in color, it has to have an additional dimension, which will specify the three color components (if you are working with RGB).
This can be achieved in the following manner:
newZ=zeros(size(Z),3)
The number 3 is here, as I said, so you would be able to give color to the new histogram.
Now you need to calculate the step (this is needed only if maxZ > The number of colors you wish to assign). The step can be calculated as:
stepZ=maxZ/Total_Number_of_Colors
If maxZ is, for example 2000 and Total_Number_of_Colors is (With 10 overlaping colours) 745, then stepZ=2.6845637583892617449664429530201. You will also need a counter so you would know what color you would assign to the new matrix. You can initialize it here:
count=0;
Now, finally the assignment is as follows:
For i=1:stepZ:maxZ
count=count+1;
If count>245
NewZ(Z==stepz,3)=count;
elseif count>245 && count<256
NewZ(Z==stepz,3)=count;
NewZ(Z==stepz,2)=count-245;
elseif count>255
NewZ(Z==stepz,2)=count-245;
elseif count>500 && count<511
NewZ(Z==stepz,2)=count-245;
NewZ(Z==stepz,1)=count-500;
else
NewZ(Z==stepz,1)=count-500;
end
end
At this point you have colored your histogram. Note that you can manually color it in different colors than red, green and blue (even if you are working in RGB), but it would be a bit harder, so if you don't like the colors you can experiment with the last bit of code (the one with the for loops), or check the internet of some other automatic way to color your newZ matrix.
Now, how do you think to superimpose this matrix (histogram) over your map? Do you want only the black lines to be shown over the colored histogram? If that's the case, than it can be achieved by resampling the NewZ matrix (the colored histogram) with the same precision as the map. For example, if the map is of size MxN, then the histogram needs to be adjusted to that size. If, on the other hand, their sizes are the same, then you can directly continue to the next part.
Your job is to find all pixels that have black in the map. Since the map is not binary (blacks and whites), it will be a bit more harder, but still achievable. You need to find a satisfactory threshold for the three components. All the lines under this threshold should be the black lines that are shown on the map. You can test these values with imshow(worldmap) and checking the values of the black lines you wish to preserve (borders and land edges, for example) by pointing the cross tool on the top of the figure, in the tools bar on every pixel which is of interest.
You don't need to test all black lines that you wish to preserve. You just need to have some basic info about what values the threshold should have. Then you continue with the rest of the code and if you don't like the result so much, you just adjust the threshold in some trial and error manner. When you have figured that this threshold is, for example, (40, 30, 60) for all of the RGB values of the map that you wish to preserve (have in mind that only values that are between (0,0,0) and (40,30,60) will be kept this way, all others will be erased), then you can add the black lines with the following few commands:
for i = 1:size(worldmap,1)
for j = 1:size(worldmap,2)
if worldmap(i,j,1)<40 && worldmap(i,j,2)<30 && worldmap(i,j,3)<60
newZ(i,j,:)=worldmap(i,j,:)
end
end
I want to note that I haven't tested this code, since I don't have Matlab near me atm, so It can have few errors, but those should be easily debugable.
Hopes this is what you need,
Cheers!

abs function for fft2 is not working in MATLAB

i am trying to plot the figure of FFT magnitude of an image using the following code in the command window:
a= imread('lena','png')
figure,imshow(a)
ffta=fft2(a)
fftshift1=fftshift(ffta)
magnitude=abs(fftshift1)
figure,imshow(magnitude),title('magnitude')
However, the figure with the title magnitude shows nothing, even though MATLAB shows that it has computed abs() on fftshift. The figure is still empty, and there is no error. Also, why do we need to compute the phase shift before magnitude?
The reason why this is probably happening is because of the following things:
When you take the 2D fft of your image, it will produce a double valued result, even though your image is mostly unsigned 8-bit integer. MATLAB assumes that double formatted images have their intensities / colours between [0,1]. By doing imshow on just the magnitude itself, you will most likely get an entirely white image because I suspect a good majority of the FFT coefficients are bigger than 1. This is probably the blank figure that you're referring to.
Even if you rescale the magnitude so that it is between [0,1], the DC coefficient will be so large that if you try to display the image, you'll only see a white dot in the middle while every other component will be black.
As a side note, the reason why you are doing fftshift is because by default, MATLAB assumes that the origin of the FFT for 2D is located at the top left corner. Doing fftshift will allow the origin to be in the middle, which is what we would intuitively expect of the 2D FFT.
In order to remedy this situation, I would suggest doing a log transformation on the FFT coefficients so you can visually see the results. I would also normalize the coefficients once you log transform it so that they go between [0,1]. Do not actually modify the FFT coefficients as this would be improper. You need to leave them the same way that it is because if you intend to do any processing on the spectrum, you would start by working on the raw image. Doing filter design or anything of that sort will require the raw spectrum, as the final filter will depend on these coefficients untouched. Unless you actually want to do a log operation as part of your pipeline, then leave these coefficients as is. As such, this can be done through the following MATLAB code:
imshow(log(1 + magnitude), []);
I'm going to show an example, using your code that you have provided but using another image as you haven't provided one here. I'm going to use the cameraman.tif image that's part of the MATLAB system path. As such:
a= imread('cameraman.tif');
figure,imshow(a);
ffta=fft2(a);
fftshift1=fftshift(ffta);
magnitude=abs(fftshift1);
figure;
imshow(log(1 + magnitude), []); %// NEW
title('magnitude')
This is what I get:
As you can see, the magnitude is displayed more nicely. Also, the DC coefficient is in the middle of the spectrum thanks to fftshift.
If you want to apply this for colour images, fft2 should still work. It will apply the 2D fft to each colour plane by itself. However, if you want this to work, you'll not only need to take the log transform, but you'll also need to normalize each plane separately. You have to do this because if we tried doing the imshow command we did earlier, it would normalize it so that the greatest value in the spectrum of the colour image gets normalized to 1. This will inevitably produce that same small dot effect that we talked about earlier.
Let's try a colour image that's built-in to MATLAB: onion.png. We will use the same code that you used above, but we need an additional step of normalizing each colour plane by itself. As such:
a = imread('onion.png');
figure,imshow(a);
ffta=fft2(a);
fftshift1=fftshift(ffta);
magnitude=abs(fftshift1);
logMag = log(1 + magnitude); %// New
for c = 1 : size(a,3); %// New - normalize each plane
logMag(:,:,c) = mat2gray(logMag(:,:,c));
end
figure; imshow(logMag); title('magnitude');
Note that I had to loop through each colour plane and use mat2gray to normalize each plane to [0,1]. Also, I had to create a new variable called logMag because I have to modify each colour plane individually, and you can't do this with a single imshow call.
With this, these are the results I get:
What's different with this spectrum is that we are applying the FFT to each colour plane separately, and so you'll see a whole bunch of colour spatters because for each location in this image, we are visualizing a linear combination of components from the red, green and blue channels. For each location, we have a value in between [0,1] for each colour plane, and the combination of these give you a colour at this location. You could say that darker colours are for locations that have a relatively low magnitude for at least one of the colour channels, while locations that are brighter have a relatively high magnitude for at least one of the colour channels.
Hope this helps!
Can't be sure about your version of "lena.png", but if it's a color RGB picture, you need to convert it first to grayscale, or at least select which RGB plane you want to examine.
I.e., the following works for http://optipng.sourceforge.net/pngtech/img/lena.png (color png):
clear; close all;
a = imread('lena','png');
ag = rgb2gray(a);
ag = im2double(ag);
figure(1);
imshow(ag);
F = fftshift( fft2(ag) ); % also try fft2(ag, N, N) where N < image size. Say N=128.
magnitude=abs(F);
figure(2);
imshow(magnitude);

MATLAB Quiver - Tiny arrows

I am trying to plot x and y velocities using quiver function in MATLAB.
I have x,y,u and v arrays(with their usual meanings) with dimension 100x100
So, the result is my quiver plot is dense and I cannot see the arrows unless I zoom in.
Somewhat like this: quiver not drawing arrows just lots of blue, matlab
Take a look at my plot:
Is there any way to make quiver plot less dense(and with bigger arrows)? I am planning to clip x-axis range to 0-4. But anything apart from that?
I cannot make my mesh less dense for accuracy concerns. I am, however willing to ignore some fine data points if that's required to make the plot look better.
You can plot a reduced number of arrows by plotting, for example, (assuming your data are in arrays)
quiver(x(1:2:end,1:2:end),y(1:2:end,1:2:end),u(1:2:end,1:2:end),v(1:2:end,1:2:end))
where the 2 in this example means we plot only a quarter as many arrows. You can of course change it, as long as you change all of the 2's so that the arrays are all appropriately sized.
If you want to change the length of the arrows there are two options. Firstly, you can use the scale option scale=2 to scale the arrows by the amount specified, or you can normalise the velocities if you want to have all the arrows the same length. You do lose information doing that, because you can't compare the magnitude of the velocity by looking at the arrows, but it may be useful in some situations. You can do this by dividing u and v both by sqrt(u.^2+v.^2) (at the points you wish to plot arrows at.
Hope that helps and sets everything out nicely.
You need to make your interval value a bit larger in order to make your matrix more sparse.
This is very dense:
1:0.0001:100
This is very sparse:
1:1:100
EDIT:
If you have the Image Processing Toolkit you can use the imresize function to reduce the matrix resolution:
newMat = imresize(oldMat, newSize);
And if you don't have the Toolbox then you can resize in a similar manner to this example using interp2 Interpolation:
orgY = 1:size(oldMat,1);
orgX = 1:size(oldMat,2);
[orgX,orgY] = meshgrid(orgX ,orgY);
newY = linspace(1,size(mat,1),newHeight);
newX = linspace(1,size(mat,2),newWidth);
[newX,newY] = meshgrid(newX,newY);
newMat = interp2(orgX,orgY,mat,newX,newY);
And thanks to #David, if you want to just strip out some individual points you can simply do:
xPlot=x(1:2:end)

Variance and Mean of Image

I am calculating mean and variance of my original and stego image to compare them
I am using grayscale BMP image for comaprison
image=imread("image name")
M = mean(image(:))
V = var((image(:)))
Is this is correct way fo calculating mean/var in MATLAB? My Variance is getting more than mean..
Any help appreciated..
These are indeed the correct way to calculate the mean and variance over all the pixels of your image.
It is not impossible that your variance is larger than the mean as both are defined in the following way:
mean = sum(x)/length(x)
variance = sum((x - mean(x)).^2)/(length(x) - 1);
For example, if you generate noise from a standard normal distribution with randn(N,1), you will get N samples, and if you calculate the mean and variance, you will get approximately 0 and 1. So there as well, your variance may well be larger than the mean.
Both have a totally different meaning: the mean gives you an idea where your pixels are (i.e. are they white, black, 50% gray, ...). The mean will give you an idea of what pixel color to choose to summarize the color of the complete image. The variance gives you an idea how the pixel values are spread: e.g. if your mean pixel value is 50% gray, are most of the other pixels also 50% gray (small variance) or do you have 50 black pixels and 50 white pixels (large variance)? So you could also view it as a way to get an idea how well the mean summarizes the image (i.e. with zero variance, most of the information is captured by the mean).
edit: For the RMS value (Root Mean Square) of a signal, just do what the definition says. In most cases you want to remove the DC component (i.e. the mean) before calculating the RMS value.
edit 2: What I forgot to mention was: it also makes little sense to compare the numerical value of the variance with the mean from a physical point of view. The mean has the same dimension as your data (in case of pixels, think of intensity), while the variance has the dimension of your data squared (so intensity^2). The standard deviation (std in MATLAB), which is the square root of the variance on the other hand has the same dimension as the data, so there you could make some comparisons (it is another question whether you should do such comparison).
If you are workign with RGB image (H x W x 3), you have to calculate mean and variance separately for each channel. In this case the mean pixel will also be 3-values vector.
for ch = 1:3
M(ch) = mean(reshape(img(:,:,ch),[],1));
V(ch) = var(reshape(img(:,:,ch),[],1));
end
MATLAB has function image. Avoid using it as a variable.