Scale image object to match another object's scale - image

I have two sets of images of different size for each set. The first set is images of 400x400 pixels with real picture objects.
The second set is 319x319, with image silhouettes of different scale than the real picture objects.
What I want to achieve, is basically to have the silhouettes replaced by the real picture objects (i.e. beaver) of the first set. So the end result will be 319x319 resolution images with real picture objects. Here is an example:
The first set images cannot simply be resized to 319x319, since the beaver will not match the silhouette. There are about 100 images with different "beaver size to beaver's silhouette size" relationships. Is there a way to automate this procedure?
So far, I've tried #cxw suggestion up to step 2. Here is the code of EllipseDirectFit I used. And here is my code to plot the images with the ellipse fits. I don't know how to proceed to steps 3-5.. I think from EllipseDirectFit function -> 2*abs(A(1)) should be the ellipsi's major axes. (NOTE: 'a1.bmp' is the real image and 'b1.bmp' is the silhouette).

In case anyone else has the same problem as me, I post the code that solved my problem. I actually followed cxw's suggestion and fitted an ellipse for both real and silhouette pictures, then resized the real picture based on the ratio of the silhouette-ellipse's major axis to the real-ellipse major axis. This made the image object match in size the silhouette image object (i.e. the beaver). Then I either cropped, or added border pixels to match the resolution I needed (i.e. 319x319).
% fetching the images
realList = getAllFiles('./real_images'); % getAllFiles => StackOverflow function
silhList = getAllFiles('./silhouettes');
for qq = 1:numel(realList)
% Name of the file to save
str = realList{qq}(15:end);
a = imread(realList{qq}); % assign real image
background_Ra = a(1,1,1); % getting the background colors
background_Ga = a(1,1,2);
background_Ba = a(1,1,3);
% finding the points (x,y) to pass to fit_ellipse
[x1,y1]=find(a(:,:,1)~=background_Ra | a(:,:,2)~=background_Ga | a(:,:,3)~=background_Ba);
% fitting an ellipse to these points
z1 = fit_ellipse(x1,y1); % Mathworks file exchange function
b = imread(silhList{qq}); % assign silhouette image
background_R2b = b(1,1,1); % getting the background colors
background_G2b = b(1,1,2);
background_B2b = b(1,1,3);
% finding the points (x,y) to pass to fit_ellipse
[x2,y2]=find(b(:,:,1)~=background_R2b & b(:,:,2)~=background_G2b & b(:,:,3)~=background_B2b);
% fitting an ellipse to these points
z2 = fit_ellipse(x2,y2);
% ratio of silhouette's ellipse major axis to real image's ellipse
% major axis
ellaxratio = z2.long_axis/z1.long_axis;
% resizing based on ellaxratio, so that the real image object size will
% now fit the silhouette's image object size
c = imresize(a,ellaxratio); c = rgb2gray(c);
bordercolor = c(end,end);
% if the resulting image is smaller, add pixels around it until they
% match with the silhouette image resolution
if size(c) < 319
while size(c) < 319
% 'addborder' is a Mathworks file exchange function
c = addborder(c(:,:,1),1, bordercolor ,'outer');
end
% if the resulting image is larger, crop pixels until they match
else size(c) > 319
while size(c) > 319
c = c(2:end-1,2:end-1);
end
end
% in a few cases, the resulting resolution is 318x318, instead of
% 319x319, so a small adjustment won't hurt.
if size(c) ~= 319
c = imresize(c,[319 319]);
end
% saving..
imwrite(c,['./good_fits/' str '.bmp'])
end

I don't have code for this, but here's how I would proceed, just off-hand. There's almost certainly a better way :) .
For each of the real image and the silhouette image:
Get the X, Y coordinates of the pixels that aren't the background. Edit Example tested in Octave:
background_R = img(1,1,1)
background_G = img(1,1,2)
background_B = img(1,1,3)
[xs,ys]=find(img(:,:,1)~=background_R | img(:,:,2)~=background_G | img(:,:,3)~=background_B)
The logical OR is because the image can differ from the background in any color component.
Fit an ellipse to the X, Y coordinate pairs you found. E.g., use this routine from File Exchange. (Actually, I suppose you could use a circle fit or any other shape fit you wanted, as long as size and position are the only differences between the non-background portions of the images.)
Now you have ellipse parameters for the real image and the silhouette image. Assuming the aspect ratios are the same, those ellipses should differ only in center and scale.
Resize the real image (imresize) based on the ratio of silhouette ellipse major axis length to real image ellipse major axis length. Now they should be the same size.
Find the centers. Using the above fit routine,
A=EllipseDirectFit(...)
% switch to Mathworld notation from http://mathworld.wolfram.com/Ellipse.html
ma=A(1); mb=A(2)/2; mc=A(3); md=A(4)/2; mf=A(5)/2; mg=A(6);
center_x = (mc*md-mb*mf)/(mb**2-ma*mc)
center_y = (ma*mf-mb*md)/(mb**2-ma*mc)
Move the real image data in a 3-d matrix so that the ellipse centers
coincide. For example,
cx_silhouette = ... (as above, for the silhouette image)
cy_silhouette = ...
cx_real = ... (as above, for the *resized* real image)
cy_real = ...
shifted = zeros(size(silhouette_image)) % where we're going to put the real image
deltax = cx_silhouette - cx_real
deltay = cy_silhouette - cy_real
% if deltax==deltay==0, you're done with this step. If not:
portion = resized_real_image(max(deltay,0):319-abs(deltay), max(deltax,0):319-abs(deltax), :); % or something like that - grab the overlapping part of the resized real image
shifted(max(deltay,0):min(deltay+319,319), max(deltax,0):min(deltax+319,319), :) = portion; % or something like that - slide the portion of the resized real image in x and y. Now _shifted_ should line up with the silhouette image.
Using the background color (or the black silhouette — same difference) as a mask, copy pixels from the resized, moved real image into the silhouette image.
Hope this helps!

Related

Calculate 3D distance based on change in intensity

I have three sections (top, mid, bot) of grayscale images (3D). In each section, I have a point with coordinates (x,y) and intensity values [0-255]. The distance between each section is 20 pixels.
I created an illustration to show how those images were generated using a microscope:
Illustration
Illustration (side view): red line is the object of interest. Blue stars represents the dots which are visible in top, mid, bot section. The (x,y) coordinates of these dots are known. The length of the object remains the same but it can rotate in space - 'out of focus' (illustration shows a rotating line at time point 5). At time point 1, the red line is resting (in 2D image: 2 dots with a distance equal to the length of the object).
I want to estimate the x,y,z-coordinate of the end points (represents as stars) by using the changes in intensity, the knowledge about the length of the object and the information in the sections I have. Any help would be appreciated.
Here is an example of images:
Bot section
Mid section
Top section
My 3D PSF data:
https://drive.google.com/file/d/1qoyhWtLDD2fUy2zThYUgkYM3vMXxNh64/view?usp=sharing
Attempt so far:
enter image description here
I guess the correct approach would be to record three images with slightly different z-coordinates for your bot and your top frame, then do a 3D-deconvolution (using Richardson-Lucy or whatever algorithm).
However, a more simple approach would be as I have outlined in my comment. If you use the data for a publication, I strongly recommend to emphasize that this is just an estimation and to include the steps how you have done it.
I'd suggest the following procedure:
Since I do not have your PSF-data, I fake some by estimating the PSF as a 3D-Gaussiamn. Of course, this is a strong simplification, but you should be able to get the idea behind it.
First, fit a Gaussian to the PSF along z:
[xg, yg, zg] = meshgrid(-32:32, -32:32, -32:32);
rg = sqrt(xg.^2+yg.^2);
psf = exp(-(rg/8).^2) .* exp(-(zg/16).^2);
% add some noise to make it a bit more realistic
psf = psf + randn(size(psf)) * 0.05;
% view psf:
%
subplot(1,3,1);
s = slice(xg,yg,zg, psf, 0,0,[]);
title('faked PSF');
for i=1:2
s(i).EdgeColor = 'none';
end
% data along z through PSF's center
z = reshape(psf(33,33,:),[65,1]);
subplot(1,3,2);
plot(-32:32, z);
title('PSF along z');
% Fit the data
% Generate a function for a gaussian distibution plus some background
gauss_d = #(x0, sigma, bg, x)exp(-1*((x-x0)/(sigma)).^2)+bg;
ft = fit ((-32:32)', z, gauss_d, ...
'Start', [0 16 0] ... % You may find proper start points by looking at your data
);
subplot(1,3,3);
plot(-32:32, z, '.');
hold on;
plot(-32:.1:32, feval(ft, -32:.1:32), 'r-');
title('fit to z-profile');
The function that relates the intensity I to the z-coordinate is
gauss_d = #(x0, sigma, bg, x)exp(-1*((x-x0)/(sigma)).^2)+bg;
You can re-arrange this formula for x. Due to the square root, there are two possibilities:
% now make a function that returns the z-coordinate from the intensity
% value:
zfromI = #(I)ft.sigma * sqrt(-1*log(I-ft.bg))+ft.x0;
zfromI2= #(I)ft.sigma * -sqrt(-1*log(I-ft.bg))+ft.x0;
Note that the PSF I have faked is normalized to have one as its maximum value. If your PSF data is not normalized, you can divide the data by its maximum.
Now, you can use zfromI or zfromI2 to get the z-coordinate for your intensity. Again, I should be normalized, that is the fraction of the intensity to the intensity of your reference spot:
zfromI(.7)
ans =
9.5469
>> zfromI2(.7)
ans =
-9.4644
Note that due to the random noise I have added, your results might look slightly different.

Remove optical disk image of the retina, setting the color of the optical disk as background color

I've been working with the retina image, currently I am submitting to the wavelet, but I have noticed that I have two problems are:
The optical disk which causes me image noise
And the circle delimiting the retina
The original image is the next
My plan is to establish the bottom of the tone of the optical disk in order not to lose any detail of the blood vessels of the retina (I post a code with which I played but still do not understand much as I know the tone of the optical disc and how to set it to the image without altering the blood vessels)
And with respect to the outer circle of the retina, I don´t know that you recommend me (I do not know about masks, I would appreciate if they have to consult my literature can provide)
c = [242 134 72];% Background to change
thresh = 50;
A = imread('E:\Prueba.jpg');
B = zeros(size(A));
Ar = A(:,:,1);
Ag = A(:,:,2);
Ab = A(:,:,3);
Br = B(:,:,1);
Bg = B(:,:,2);
Bb = B(:,:,3);
logmap = (Ar > (c(1) - thresh)).*(Ar < (c(1) + thresh)).*...
(Ag > (c(2) - thresh)).*(Ag < (c(2) + thresh)).*...
(Ab > (c(3) - thresh)).*(Ab < (c(3) + thresh));
Ar(logmap == 1) = Br(logmap == 1);
Ag(logmap == 1) = Bg(logmap == 1);
Ab(logmap == 1) = Bb(logmap == 1);
A = cat(3 ,Ar,Ag,Ab);
imshow(A);
courtesy of the question How can I change the background color of the image?
The image I get is the following
I need a picture like this where the optical disc does not cause me noise when segmenting the blood vessels of the retina.
I want to be uniform background ... and only the veins are perceived
I continued to work and have obtained the following image As you can realize the optical disk removes some parts of the blood vessels (veins) that are above him, so I require eliminating or make uniform the entire bottom of the image.
As Wouter said, you should first correct the inhomogeneity of the image. I would do it in my own way:
First, the parameters you can adjust to optimize the output:
gfilt = 3;
thresh = 0.4;
erode = 3;
brighten = 20;
You will see how they are used in the code.
This is the main step: to apply a Gaussian filter to the image to make it smooth and then subtract the result from the original image. This way you end up with the sharp changes in your data, which happens to be the vessels:
A = imread('Prueba.jpg');
B = imgaussfilt(A, gfilt) - A; % Gaussian filter and subtraction
% figure; imshow(B)
Then I create a binary mask to remove the unwanted area of the image:
% the 'imadjust' makes sure that you get the same result even if you ...
% change the intensity of illumination. "thresh" is the threshold of ...
% conversion to black and white:
circ = im2bw(imadjust(A(:,:,1)), thresh);
% here I am shrinking the "circ" for "erode" pixels:
circ = imerode(circ, strel('disk', erode));
circ3 = repmat(circ, 1, 1, 3); % and here I extended it to 3D.
% figure; imshow(circ)
And finally, I remove everything on the surrounding dark area and show the result:
B(~circ3) = 0; % ignore the surrounding area
figure; imshow(B * brighten) % brighten and show the output
Notes:
I do not see the last image as a final result, but probably you could apply some thresholds to it and separate the vessels from the rest.
The quality of the image you provided is quite low. I expect good results with a better data.
Although the intensity of blue channel is less than the rest, the vessels are expressed there better than the other channels, because blood is red!
If you are acquiring this data or you have access to the person, I suggest you to use blue light for illumination, since it provides you with higher contrast of the vessels.
Morphological operations are good for working with sphagetti images.
Original image:
Convert to grayscale:
original = rgb2gray(gavrF);
Estimate the background via morphological closing:
se = strel('disk', 3);
background = imclose(original, se);
Estimate of the background:
You could then for example subtract this background from the original grayscale image. You can do this straight by doing a bottom hat transform on the grayscale image:
flatImage = imbothat(original, strel('disk', 4));
With a output:
Noisy, but now you got access to global thresholding methods. Remember to change the datatypes to double if you wish to do some subtraction or division manually.

Image Repetition from Binary to Cartesian

I'd like to take in an RGB image, find the points in the image that are white, and get the cartesian coordinates of those points in the image. I've gotten most of the way there, but when I try to plot the cartesian coordinates, I get a vertically tiled image (i.e. 5 overlapped copies of what I should see). Anyone know what could be causing this?
,
Code: (JPG comes in as 2448 x x3264 x 3 uint8)
I = imread('IMG_0245.JPG');
imshow(I); % display unaltered image
% Convert image to grayscale
I = rgb2gray(I);
% Convert image to binary (black/white)
I = im2bw(I, 0.9);
% Generate cartesian coordinates of image
imageSize = size(I);
[x, y] = meshgrid( 1:imageSize(1), 1:imageSize(2) );
PerspectiveImage = [x(:), y(:), I(:)];
% Get indices of white points only
whiteIndices = find(PerspectiveImage(:,3));
figure; plot( PerspectiveImage(whiteIndices, 1), PerspectiveImage(whiteIndices, 2),'.');
% Flip vertically to correct indexing vs. plotting issue
axis ij
Very simple. You're declaring your meshgrid wrong. It should be:
[x, y] = meshgrid( 1:imageSize(2), 1:imageSize(1) );
The first parameter denotes the horizontal extents of the 2D grid, and so you want to make this vary for as many columns as you have. Similarly, the second parameter denotes the vertical extents of the 2D grid, and so you want to make this for as many rows as you have.
I had to pre-process some of your image to get some good results because your original image had a large white border surrounding the image. I had to remove this border by removing all pure white pixels. I also read in the image directly from StackOverflow:
I = imread('http://s7.postimg.org/ovb53w4ff/Track_example.jpg');
mask = all(I == 255, 3);
I = bsxfun(#times, I, uint8(~mask));
This was the image I get after doing my pre-processing:
Once I do this and change your meshgrid call, I get this:

How to process a "Very Large image file" in MATLAB and find out the pixel with largest value

I have to process a very large image ( say 10 MB image file or even more).I have to remove artifacts and dead pixels in MATLAB
I have read about Block Processing of Large Images, but have no idea how to apply it to a 16 bit image.
I am referring to removal of pixels which have highest value into the average value of surrounding pixel .my code is not working on my image which is 80 MB of size
numIterations = 30;
avgPrecisionSize = 16; % smaller is better, but takes longer
% Read in the image grayscale:
originalImage = double(rgb2gray(imread('C:\Documents and Settings\admin\Desktop\TM\image5.tif')));
% get the bad pixels where = 0 and dilate to make sure they get everything:
badPixels = (originalImage == 0);
badPixels = imdilate(badPixels, ones(12));
%# Create a big gaussian and an averaging kernel to use:
G = fspecial('gaussian',[1 1]*100,50);
H = fspecial('average', [1,1]*avgPrecisionSize);
%# User a big filter to get started:
newImage = imfilter(originalImage,G,'same');
newImage(~badPixels) = originalImage(~badPixels);
% Now average to
for count = 1:numIterations
newImage = imfilter(newImage, H, 'same');
newImage(~badPixels) = originalImage(~badPixels);
end
%% Plot the results
figure(123);
clf;
% Display the mask:
subplot(1,2,1);
imagesc(badPixels);
axis image
title('Region Of the Bad Pixels');
% Display the result:
subplot(1,2,2);
imagesc(newImage);
axis image
set(gca,'clim', [0 255])
title('Infilled Image');
colormap gray
newImage2 = roifill(originalImage, badPixels);
figure(44);
clf;
imagesc(newImage2);
colormap gray
You are doing a few things which are obvious problems (but it might depend specifically on how far you can get into the code before you run out of memory)
1) You are immediately converting the whole image to double
2) You are identifying certain pixels which you want to replace, but passing the whole image to imfilter and then throwing away (presumably) most of the output:
newImage = imfilter(originalImage,G,'same'); % filter across the entire image
newImage(~badPixels) = originalImage(~badPixels); % replace all the good pixels!
Without converting to double, why not first check where the bad pixels are, do your processing on subregions of the appropriate size around those pixels (the subregions can be converted to double and back), and then reassemble the image at the end?
blockproc may work if you can write your filtering option as a function which takes in an image area and returns the correct area - you'll have to use the border_size option appropriately and make sure your function just returns the original image without bothering to do any filtering if it hits a block with no bad pixels in. You can even have it write out to file as well.

How can I draw a circle on an image in MATLAB?

I have an image in MATLAB:
im = rgb2gray(imread('some_image.jpg');
% normalize the image to be between 0 and 1
im = im/max(max(im));
And I've done some processing that resulted in a number of points that I want to highlight:
points = some_processing(im);
Where points is a matrix the same size as im with ones in the interesting points.
Now I want to draw a circle on the image in all the places where points is 1.
Is there any function in MATLAB that does this? The best I can come up with is:
[x_p, y_p] = find (points);
[x, y] = meshgrid(1:size(im,1), 1:size(im,2))
r = 5;
circles = zeros(size(im));
for k = 1:length(x_p)
circles = circles + (floor((x - x_p(k)).^2 + (y - y_p(k)).^2) == r);
end
% normalize circles
circles = circles/max(max(circles));
output = im + circles;
imshow(output)
This seems more than somewhat inelegant. Is there a way to draw circles similar to the line function?
You could use the normal PLOT command with a circular marker point:
[x_p,y_p] = find(points);
imshow(im); %# Display your image
hold on; %# Add subsequent plots to the image
plot(y_p,x_p,'o'); %# NOTE: x_p and y_p are switched (see note below)!
hold off; %# Any subsequent plotting will overwrite the image!
You can also adjust these other properties of the plot marker: MarkerEdgeColor, MarkerFaceColor, MarkerSize.
If you then want to save the new image with the markers plotted on it, you can look at this answer I gave to a question about maintaining image dimensions when saving images from figures.
NOTE: When plotting image data with IMSHOW (or IMAGE, etc.), the normal interpretation of rows and columns essentially becomes flipped. Normally the first dimension of data (i.e. rows) is thought of as the data that would lie on the x-axis, and is probably why you use x_p as the first set of values returned by the FIND function. However, IMSHOW displays the first dimension of the image data along the y-axis, so the first value returned by FIND ends up being the y-coordinate value in this case.
This file by Zhenhai Wang from Matlab Central's File Exchange does the trick.
%----------------------------------------------------------------
% H=CIRCLE(CENTER,RADIUS,NOP,STYLE)
% This routine draws a circle with center defined as
% a vector CENTER, radius as a scaler RADIS. NOP is
% the number of points on the circle. As to STYLE,
% use it the same way as you use the rountine PLOT.
% Since the handle of the object is returned, you
% use routine SET to get the best result.
%
% Usage Examples,
%
% circle([1,3],3,1000,':');
% circle([2,4],2,1000,'--');
%
% Zhenhai Wang <zhenhai#ieee.org>
% Version 1.00
% December, 2002
%----------------------------------------------------------------
Funny! There are 6 answers here, none give the obvious solution: the rectangle function.
From the documentation:
Draw a circle by setting the Curvature property to [1 1]. Draw the circle so that it fills the rectangular area between the points (2,4) and (4,6). The Position property defines the smallest rectangle that contains the circle.
pos = [2 4 2 2];
rectangle('Position',pos,'Curvature',[1 1])
axis equal
So in your case:
imshow(im)
hold on
[y, x] = find(points);
for ii=1:length(x)
pos = [x(ii),y(ii)];
pos = [pos-0.5,1,1];
rectangle('position',pos,'curvature',[1 1])
end
As opposed to the accepted answer, these circles will scale with the image, you can zoom in an they will always mark the whole pixel.
Hmm I had to re-switch them in this call:
k = convhull(x,y);
figure;
imshow(image); %# Display your image
hold on; %# Add subsequent plots to the image
plot(x,y,'o'); %# NOTE: x_p and y_p are switched (see note below)!
hold off; %# Any subsequent plotting will overwrite the image!
In reply to the comments:
x and y are created using the following code:
temp_hull = stats_single_object(k).ConvexHull;
for k2 = 1:length(temp_hull)
i = i+1;
[x(i,1)] = temp_hull(k2,1);
[y(i,1)] = temp_hull(k2,2);
end;
it might be that the ConvexHull is the other way around and therefore the plot is different. Or that I made a mistake and it should be
[x(i,1)] = temp_hull(k2,2);
[y(i,1)] = temp_hull(k2,1);
However the documentation is not clear about which colum = x OR y:
Quote: "Each row of the matrix contains the x- and y-coordinates of one vertex of the polygon. "
I read this as x is the first column and y is the second colum.
In newer versions of MATLAB (I have 2013b) the Computer Vision System Toolbox contains the vision.ShapeInserter System object which can be used to draw shapes on images. Here is an example of drawing yellow circles from the documentation:
yellow = uint8([255 255 0]); %// [R G B]; class of yellow must match class of I
shapeInserter = vision.ShapeInserter('Shape','Circles','BorderColor','Custom','CustomBorderColor',yellow);
I = imread('cameraman.tif');
circles = int32([30 30 20; 80 80 25]); %// [x1 y1 radius1;x2 y2 radius2]
RGB = repmat(I,[1,1,3]); %// convert I to an RGB image
J = step(shapeInserter, RGB, circles);
imshow(J);
With MATLAB and Image Processing Toolbox R2012a or newer, you can use the viscircles function to easily overlay circles over an image. Here is an example:
% Plot 5 circles at random locations
X = rand(5,1);
Y = rand(5,1);
% Keep the radius 0.1 for all of them
R = 0.1*ones(5,1);
% Make them blue
viscircles([X,Y],R,'EdgeColor','b');
Also, check out the imfindcircles function which implements the Hough circular transform. The online documentation for both functions (links above) have examples that show how to find circles in an image and how to display the detected circles over the image.
For example:
% Read the image into the workspace and display it.
A = imread('coins.png');
imshow(A)
% Find all the circles with radius r such that 15 ≤ r ≤ 30.
[centers, radii, metric] = imfindcircles(A,[15 30]);
% Retain the five strongest circles according to the metric values.
centersStrong5 = centers(1:5,:);
radiiStrong5 = radii(1:5);
metricStrong5 = metric(1:5);
% Draw the five strongest circle perimeters.
viscircles(centersStrong5, radiiStrong5,'EdgeColor','b');
Here's the method I think you need:
[x_p, y_p] = find (points);
% convert the subscripts to indicies, but transposed into a row vector
a = sub2ind(size(im), x_p, y_p)';
% assign all the values in the image that correspond to the points to a value of zero
im([a]) = 0;
% show the new image
imshow(im)

Resources