extracting coordinates from gray scales photos - matlab

Hi I am trying to extract x,y coordinates from gray scale photo (example.jpg).
I mark the points (with red dots) where I want the x, y coordinates (example1.jpg)
Then I extract the red dots area with below codes
A=imread('example.jpg');
B=imread('example1.jpg');
Size=size(A)
C=zeros(Size);
for j=1:Size(2)
for i=1:Size(1)
if A(i,j)==B(i,j);
C(i,j)=1;
else C(i,j)=255;
end
end
end
K=mat2gray(C);
imshow(K)
By doing this, I can extract the dots (I am interested) and got below image but the dot points bigger than my real marking in the photo (dots.png). And it did not capture all dots (8 out of 10 dots)
Then to get the x,y coordinates of these dot points, I use below codes:
X=imread('dots.png');
[I,J] = find(X(:,:,1) == 255); %// Change
scatter(I,J)
Then I got x and y coordinates in terms of I,J and the picture to counter check if the coordinates show the correct positions as in the photo.
But here are the problems I need solve:
1) How can I get the smaller dot points in figure 3 (as the dot I mark in gray photos are small but when it is generated in figure 3, it become bigger. As a consequence, when I extract x,y coordinates (in terms of I,J here), I got multiple x and y for one dot as each dot is so big. Instead, I want to get one x and one y for each dot.
2) How to make to capture all dots that I mark
3) And when you look at the figure 3 and 4, figure 3 shows the real orientation of pumpkin but in the figure 4, it is rotated. How this happen and how can I correct it?
4) And I thought there can be an easier way to do this extraction than my method. Can you please advice?
Thank you

Here's a more complete example, for the sake of an answer:
I = imread('cameraman.tif');
figure, imshow(I)
[y, x] = ginput(5); % This chooses the five points interactively.
close
color = I; % making a RGB color version of the image.
color(:,:,2) = I;
color(:,:,3) = I;
for idx = 1:numel(x)
% Change the color of the points to red one by one.
xpoint = x(idx);
ypoint = y(idx);
color(xpoint, ypoint, 1) = 255;
color(xpoint, ypoint, 2) = 0;
color(xpoint, ypoint, 3) = 0;
end
figure,
imshow(color) % display the colored dot image.
This should select five points in the image, interactively, and then color those points red.

In terms of the blobs I suggest that you have saved the image with the red dots using a compressed file format.
The blobs seem to be 24x24 macroblocks - not sure what compression algorithm uses that (not jpeg)...
Here it is one up close:
Regardless, an easy way to see why it is happening is to list the values of A(i,j) and B(i,j) when A(i,j) != B(i,j) - a basic bug finding process you can do yourself.

Related

Matlab - Ignoring the background of an image

I'm working on part of an application where I now need to compare two images together and check how similar they are with one another. This is currently done by converting the two images into binary, counting the number of black and white pixels, before finally dividing the number of white pixels with the total number of pixels present inside the image.
The problem I am facing now though is the background for the images I am using since it is counting towards the 'similarity score' and making it an inaccurate result. I have an idea on how to resolve this issue, I just don't know how to put into practice.
For example, say this was the image file and it's pixel values.
0000000000
0000110000
0001001000
0011001100
0001001000
0000110000
0000000000
My idea is to search each row and column from left to right, right to left, up to down and down to up, to detect the number of the black pixels contained in the background of the image. Then hopefully once it detects a white pixel, it stops searching that row/column and moves onto the next. Then finally, when the search is completed, I will know how many black pixels the background takes up and I can then subtract that value away from my grand total, which will the allow me to have a more accurate reading.
I just don't know how to search in this way and I don't want to eliminate every black pixel on screen as they are important because they are also contained inside these objects (example below). Does anybody know how I'd go about doing it this way, or knows an even simpler way?
Current Code:
for i = 1:length(jpgFiles)
baseFileName = jpgFiles(i).name;
fullFileName = fullfile(referenceFolder, baseFileName);
%Reading in the images & converting the images to Black & White
a = im2bw(imread(firstImage));
b = im2bw(imread(fullFileName));
compiledImage = a==b;
fprintf(1, 'Reading %s\n', fullFileName);
axes(handles.axes4);
disp(compiledImage); %Displays the values if the pixels match
[X, Y] = find(a ~= b); %Find coordinates for which the two images are equal
imshow(a); %Show first image
hold on %Retains the current plot and certain axes properties
plot(Y, X, 'y.'); %Overlay those coordinates
hold off %Resets axes properties to their default before drawing new plots
%Getting the values of white and black pixels
blackCount = sum(sum(a==b ==0));
whiteCount = sum(sum(a==b));
disp('Black Count:');
disp(blackCount);
disp('White Count:');
disp(whiteCount);
%Calculating the percentage
maxTotal = blackCount + whiteCount;
decimalValue = whiteCount / maxTotal;
percentageValue = sprintf('%.0f%%',100*decimalValue);
disp(decimalValue);
disp(percentageValue);
%Title settings and delay, if needed
title('Strongest Features (Yellow Hides Any Discrepancies)', 'fontweight', 'bold'); %Sets the title
drawnow;
%pause(5);
%Adding into the image array
imageArray{j} = baseFileName;
j = j + 1;
%Adding into the popular features array
popFeaturesArray{i} = 100*decimalValue;
i = i + 1;
end
Image Examples:
you can do that with some morphological operations:
c1 = rgb2gray(imread('c1.png'));
bw = imfill(imclose(c1 ~= 0,ones(3)),'holes');
bw = bwareafilt(bw,2);
% plotting
A = imoverlay(bw,(c1 > 0).*bw,'b');
imshow(A)
bw is the foreground here.
Edit - if you don't have bwareafilt you can do instead:
n = 2; % keep the two largest areas
cc = bwconncomp(bw);
pixels = cc.PixelIdxList;
sizes = cellfun(#numel,pixels);
[~,idxs] = sort(sizes,'descend');
pixels = cell2mat(pixels(idxs(1:min(n,end)))');
bw(:) = false;
bw(pixels) = true;

Matlab - Plot areas of interest onto an image

I'm working on an application and I'm at a stage where I'm comparing two images to see if they have any resemblance, with one another. I have managed to do this, an example you can find here.
From the image, it will display white spaces for pixels that are near the same for both images given. What I want to do next is get the coordinates of the white spaces and plot them onto the original image to highlight the strongest features about the coin. However, I'm unsure how to do this as I'm rather new to Matlab.
firstImage = sprintf('M:/Project/MatLab/Coin Image Processing/Image Processing/test-1.jpg');
secondImage = sprintf('M:/Project/MatLab/Coin Image Processing/Image Processing/test-99.jpg');
a = rgb2gray(imread(firstImage));
b = rgb2gray(imread(secondImage));
axes(handles.axes4);
imshow(a==b);
title('Scanning For Strongest Features', 'fontweight', 'bold')
From using disp(a==b), I can see which points of both pictures are the same. So my guess is that I need to do something where I get the coordinates of all the zeroes and then plot them onto the original image in a way that highlights it, similar to using a yellow highlighter, but I just don't know how.
If I got your question, I think you should use find to collect all the coordinates for which a==b:
[X, Y] = find(a == b); % Find coordinates for which the two images are equal
imshow(a), axis image; % Show first image
hold on
plot(Y, X, 'y.'); % Overlay those coordinates
hold off
You can use a transparent overlay to plot the region of interest.
figure
imshow(originalImage); % plot the original image
hold on
% generate a red overlay
overlay(:, :, 1) = ones(size(a)); % red channel
overlay(:, :, 2) = zeros(size(a)); % green channel
overlay(:, :, 3) = zeros(size(a)); % blue channel
h = imshow(overlay); % plot the overlay
set(h, 'AlphaData', (a == b) * 0.5); % set the transparency to 50% if a == b and 0% otherwise

Matlab - Image processing 3D image by shifting red component of 2D

I have been assigned recently a task related to image processing. The thing is that I have no prior experience to this subject so I face some difficulties.
The concept is to create a 3D image from a 2D image by initially extracting the R,G,B components and combine them after shifting the red one according to some sort of a formula (i.e. calculate the pixel which has to be shifted by applying the formula a = x(i,j,k)*40/max(Rcomponent(:)) to each element of the red image and replace element x(i,j-a,k) by the value of x(i,j,k)).
Here is my current progress:
original = imread(nameofimag); % read file
imaginfos = imfinfo(nameofimag); % identify colortype
coltype = imaginfos(1).ColorType;
truecol = 'truecolor';
if strcmp(coltype,truecol) == 1 % convert to grayscale if needed
originalgray = rgb2gray(original);
else
originalgray = original;
end
Z = zeros(size(originalgray,1),size(originalgray,2));
% create red, blue, green parts
imagR = cat(3,originalgray,Z,Z);
imagG = cat(3,Z,originalgray,Z);
imagB = cat(3,Z,Z,originalgray);
imagRshifted = zeros(size(originalgray,1),size(originalgray,2));
h = waitbar(0,'Please wait...');
% shift red image
for i = 1:size(imagR,1)
for j = 1:size(imagR,2)
for k = 1:size(imagR,3)
imagRshifted(i,j,k) = imagR(i,j,k);
pixeltochange = round((imagRshifted(i,j,k)*40)...
/max(original(:)));
if pixeltochange < j
imagRshifted(i,j-pixeltochange,k) =...
imagRshifted(i,j,k);
end
end
end
waitbar(i/size(imagR,1));
end
imagRshifted = uint8(imagRshifted);
% combine R,G,B to create 3D image
imag3D = imfuse(imagRshifted,imfuse(imagG,imagB));
% plot the desired images
if depictrgb == 0;
figure;imshow(imag3D);
else
figure;imshow(imag3D);
figure;imshow(imagR);
figure;imshow(imagB);
figure;imshow(imagG);
end
close(h);
end
As you can see the result is quite confusing (at least to me). At first there is a line splitting the final image in two parts (it seems like the formula has been applied only on the left part of the line leaving the right part untouched). Additionally the produced image is more like a reconstruction of random pixels rather than a combination of the R,G,B components.
Any ideas would be more than appreciated.
Thank you in advance.
My friend, you have 2 main problems: You don't know exactly what you are doing and you are using Matlab extremely inefficiently.
So lets go to what you need, and how to do it.
You described your problem relatively OK. Lets re-describe it.
Starting from a grayscale image, create a color image with the red channel shifted by an amount given by the following equation: shift = x_ij*40/max(x(:)) .
There are missing things here, like: what to do if the shift goes out of the image? In this case we will assume that if the shift is too big, then make it zero.
lets start: First we are going to load an image, and if it is not grayscale, convert it to grayscale. I am using the following image:
% // same as you had
nameofimag='https://2.bp.blogspot.com/-db3zQg7geYA/UYqnvBriBeI/AAAAAAAAAHo/cv3Dy40mUFo/s1600/Meteosat_grid.jpg'
original = imread(nameofimag); % read file
imaginfos = imfinfo(nameofimag); % identify colortype
coltype = imaginfos(1).ColorType;
truecol = 'truecolor';
if strcmp(coltype,truecol) == 1 % convert to grayscale if needed
originalgray = rgb2gray(original);
else
originalgray = original;
end
Now we want to compute the shift per pixel we can do that in one line:
% compute shifts
shift=-round( double(originalgray) *40./double(max(originalgray(:))));
And we want to make sure that we don't go over the iamge, so we will compute the actual index (indY+shift) and check if that's inside the image
% get indexes
[indY,~]=meshgrid(1:size(originalgray,2),1:size(originalgray,1));
% Make sure we dont go out of the image
shift(indY+shift<=0)=0;
Now we have a proper shift, that we know its not going out. Realize, shift is a matrix size(image), thus we have all we need.
Now, lets take the R channel and shift it. Realize, that the image is grayscale, thus R G and B are exactyl equal. we will then shift the original image.
% allocate memory
redshifted = zeros(size(originalgray,1),size(originalgray,2));
% actually shift red
for ii=1:size(originalgray,1)
redshifted(ii,:)=originalgray(ii,indY(ii,:)+shift(ii,:));
end
Now that we have the red shifted, lets create a new image, that has RGB values, where GB are the original image and R the shifted image
colorImg=cat(3,redshifted,originalgray,originalgray);
imshow(colorImg)
This gives:
Conclusion: This code applies the shift to a grayscale image (or truecolor converted to grayscale) and generates a color image with the red shifted. Modifications need to be done if the original image is actually truecolor without the grayscale conversion, but these modifications will be minor, and mainly relate to changing originalgray in different places to proper RGB chanel access (e.g. original(:,:,2) is G).
As a final note: Realise that this will only look good with 3D glasses if the image is a depthmap. Else, you will get weird visual effect, as the color of an image has nothing to do with the depth of it (thus displacement of red)

matlab: how to plot multidimensional array

Let's say I have 9 MxN black and white images that are in some way related to one another (i.e. time lapse of some event). What is a way that I can display all of these images on one surface plot?
Assume the MxN matrices only contain 0's and 1's. Assume the images simply contain white lines on a black background (i.e. pixel value == 1 if that pixel is part of a line, 0 otherwise). Assume images are ordered in such a way as to suggest movement progression of line(s) in subsequent images. I want to be able to see a "side-view" (or volumetric representation) of these images which will show the surface that a particular line "carves out" in its movement across the images.
Coding is done in MATLAB. I have looked at plot (but it only does 2D plots) and surf, which does 3D plots but doesn't work for my MxNx9 matrix of images. I have also tried to experiment with contourslice, but not sure what parameters to pass it.
Thanks!
Mariya
Are these images black and white with simple features on a "blank" field, or greyscale, with more dense information?
I can see a couple of approaches.
You can use movie() to display a sequence of images as an animation.
For a static view of sparse, simple data, you could plot each image as a separate layer in a single figure, giving each layer a different color for the foreground, and using AlphaData to make the background transparent so all the steps in the sequenc show through. The gradient of colors corresponds to position in the image sequence. Here's an example.
function plotImageSequence
% Made-up test data
nLayers = 9;
x = zeros(100,100,nLayers);
for i = 1:nLayers
x(20+(3*i),:,i) = 1;
end
% Plot each image as a "layer", indicated by color
figure;
hold on;
for i = 1:nLayers
layerData = x(:,:,i);
alphaMask = layerData == 1;
layerData(logical(layerData)) = i; % So each layer gets its own color
image('CData',layerData,...
'AlphaData',alphaMask,...
'CDataMapping','scaled');
end
hold off
Directly showing the path of movement a "line" carves out is hard with raster data, because Matlab won't know which "moved" pixels in two subsequent images are associated with each other. Don't suppose you have underlying vector data for the geometric features in the images? Plot3() might allow you to show their movement, with time as the z axis. Or you could use the regular plot() and some manual fiddling to plot the paths of all the control points or vertexes in the geometric features.
EDIT: Here's a variation that uses patch() to draw each pixel as a little polygon floating in space at the Z level of its index in the image sequence. I think this will look more like the "surface" style plots you are asking for. You could fiddle with the FaceAlpha property to make dense plots more legible.
function plotImageSequencePatch
% Made-up test data
nLayers = 6;
sz = [50 50];
img = zeros(sz(1),sz(2),nLayers);
for i = 1:nLayers
img(20+(3*i),:,i) = 1;
end
% Plot each image as a "layer", indicated by color
% With each "pixel" as a separate patch
figure;
set(gca, 'XLim', [0 sz(1)]);
set(gca, 'YLim', [0 sz(2)]);
hold on;
for i = 1:nLayers
layerData = img(:,:,i);
[x,y] = find(layerData); % X,Y of all pixels
% Reshape in to patch outline
x = x';
y = y';
patch_x = [x; x+1; x+1; x];
patch_y = [y; y; y+1; y+1];
patch_z = repmat(i, size(patch_x));
patch(patch_x, patch_y, patch_z, i);
end
hold off

How do I calculate the area under a curve in an image with MATLAB?

alt text http://internationalpropertiesregistry.com/Server/showFile.php?file=%2FUpload%2Fstatistics.gifc49ca28823a561a41d09ef9adbb5e0c5.gif
The unit of x-axis is hours (h), and there are 24 hours in total.
The unit of y-axis is millions (m).
How do I calculate the area under the red curve in the image in units of m*h?
Important UPDATE
Only the image is readily available (not the data), and I want to calculate the area programmatically.
Here's an interesting solution :). Btw, it uses bwfill (similar to imfill) which needs some user interaction.
Code
%# Constants
gray_value_curve = 2;
gray_value_box = 3;
area_box_in_units = 10;
%# Read the image
I = imread('C:\p23\graph.gif');
%# Find the area of a unit block
figure(1);
imshow(I,[]);
[BS sq_elem] = bwfill;
imshow(BS,[]);
%# Get the dimensions to make the estimate more accurate
X = zeros(size(BS));
X(sq_elem) = 1;
s = regionprops(X,'Area','BoundingBox');
block_area = s.Area + 2*(s.BoundingBox(3)-1) + 2*(s.BoundingBox(4)-1) + 4;
%#Find the area under the curve
I( ~(I == gray_value_curve | I == gray_value_box) ) = 0;
figure(2);
imshow(I,[]);
[BA area_curve_elem] = bwfill;
imshow(BA,[]);
%# Area under the curve
curve_area = numel(area_curve_elem);
%# Display the area in the required units
area = area_box_in_units*curve_area/block_area;
disp(area);
Output
113.5259
Figure 1
Figure 2
The difficulty with creating a fully-automated solution is that it would require you to hardcode into your solution certain assumptions about the input images you are going to process. If these assumptions don't hold for all the potential images you may come across, the fully-automated solution won't give trustworthy results, and trying to extend the fully-automated solution to handle all possible inputs will likely cause it to bloat into an incomprehensible and complicated mess of code.
When in doubt about the variability in features of your input images, a solution like Jacob's with some user interaction is generally best. If you can be certain that the features of your input images follow a strict set of rules, then an automated solution can be considered.
As an example, below is some automated code I wrote to approximate the area under the red curve in your graph. Since I used the above graph as a guide, there are a number of conditions that must be met for it to work:
The red pixels of the plotted line must be uniquely described in the image as containing green and blue color components equal to 0 and red color components equal to 1.
The green pixels of the grid lines must be uniquely described in the image as containing red and blue color components less than 1 and green color components equal to 1.
The blue pixels of the axes lines must be uniquely described in the image as containing red and green color components equal to 0 and blue color components equal to 1.
The grid and axis lines must always be exactly aligned in a horizontal or vertical direction.
The length of the grid lines must span well over half the width and height of the image.
The x axis must be the longest horizontal blue line in the image.
The grid lines must always be 1 pixel thick.
Subject to the above conditions on the input image, the following code can be used to approximate the area under the red curve without user input:
[img,map] = imread('original_chart.gif'); %# Read the indexed image
[r,c] = size(img); %# Get the image size
redIndex = find((map(:,1) == 1) & ... %# Find the red index value
(map(:,2) == 0) & ...
(map(:,3) == 0))-1;
greenIndex = find((map(:,1) < 1) & ... %# Find the green index value
(map(:,2) == 1) & ...
(map(:,3) < 1))-1;
blueIndex = find((map(:,1) == 0) & ... %# Find the blue index value
(map(:,2) == 0) & ...
(map(:,3) == 1))-1;
redLine = (img == redIndex); %# A binary image to locate the red line
greenLine = (img == greenIndex); %# A binary image to locate the grid lines
blueLine = (img == blueIndex); %# A binary image to locate the axes lines
w = mean(diff(find(sum(greenLine,1) > r/2))); %# Compute unit square width
h = mean(diff(find(sum(greenLine,2) > c/2))); %# Compute unit square height
squareArea = w*h; %# Compute unit square area
[maxValue,maxIndex] = max(redLine); %# Find top edge of red line
x = find(maxValue > 0); %# Find x coordinates of red line
y = maxIndex(maxValue > 0); %# Find y coordinates of red line
[maxValue,maxIndex] = max(sum(blueLine,2)); %# Find row index of x axis
y = maxIndex-y; %# Zero the y coordinate
totalArea = trapz(x,y)/squareArea; %# Compute the area under the curve
Which gives the following results:
squareArea = 460.6 square pixels
totalArea = 169.35 m*h
EXPLANATION:
I'll elaborate more about the steps involved in computing w:
The binary image greenLine is summed along each column using the function SUM, giving a 1-by-c vector where each element is a count of how many grid line pixels are in each column of the image.
The elements of this vector that are greater than r/2 (half the number of rows in the image) indicate columns of the image that contain a vertical grid line. The indices of these columns are found using the function FIND.
The pairwise differences between these column indices are found using the function DIFF. This gives a vector containing the widths (in pixels) of the spaces between grid lines.
Finally, the function MEAN is used to compute the mean width of the spaces between all the grid lines in the image.
When computing h, the only difference is that the sum is performed along each row and r/2 is replaced with c/2 (half the number of columns in the image).
Since you only have the image available, I suggest you integrate by eye: count the number of whole squares underneath the red line.
For each square which the red line intersects, decide whether or not to include it in the count depending on how much lies below the line. Don't try to estimate how much of the square lies beneath the red line, at best that will give you an illusion of greater accuracy.
EDIT: I counted the green squares for you, the answer is 168 m.h
Since that doesn't seem like a "function" that you could integrate I would using a numerical integration technique. I'm always partial to trapz which use the "trapezoidal rule" for numerical integration.
Something like:
area = trapz(data);
should be sufficient.
Hope that helps,
Will