MATLAB Auto Crop - matlab

I am trying to automatically crop the image below to a bounding box. The background will always be the same colour. I have tried the answers at
Find the edges of image and crop it in MATLAB
and various applications and examples on Mathworks' file exchange but I get stuck at getting a proper boundingbox.
I was thinking to convert the image to black and white, converting it to binary and removing everything that's closer to white than black, but I'm not sure how to go about it.

Here's a nice way
img = im2double(imread('http://i.stack.imgur.com/ZuiEt.jpg')); % read image and convert it to double in range [0..1]
b = sum( (1-img).^2, 3 ); % check how far each pixel from "white"
% display
figure; imshow( b > .5 ); title('non background pixels');
% use regionprops to get the bounding box
st = regionprops( double( b > .5 ), 'BoundingBox' ); % convert to double to avoid bwlabel of logical input
rect = st.BoundingBox; % get the bounding box
% display
figure; imshow( img );
hold on; rectangle('Position', rect );
Following Jak's request, here's the second line explained
after converting img to double type (using im2double), the image is stored in memory as h-by-w-by-3 matrix of type double. Each pixel has 3 values between 0 and 1 (not 255!), representing its RGB values 0 being dark and 1 being bright.
Thus (1-img).^2 checks, for each pixel and each channel (RGB) how far it is from 1 - bright. The darker the pixel - the larger this distance.
Next, we sum the distance per channel to a single value per pixel using sum( . ,3 ) command leaving us with h-by-w 2D matrix of distances of each pixels from white.
Finally, assuming background is bright white we select all pixels that are significantly far from birght b > .5. This threshold is not perfect, but it captures well the boundary of the object.

Following the answer of Shai, I present a way to circumvent regionprops (image processing toolbox) just based on find on the black-white image.
% load
img = im2double(imread('http://i.stack.imgur.com/ZuiEt.jpg'));
% black-white image by threshold on check how far each pixel from "white"
bw = sum((1-img).^2, 3) > .5;
% show bw image
figure; imshow(bw); title('bw image');
% get bounding box (first row, first column, number rows, number columns)
[row, col] = find(bw);
bounding_box = [min(row), min(col), max(row) - min(row) + 1, max(col) - min(col) + 1];
% display with rectangle
rect = bounding_box([2,1,4,3]); % rectangle wants x,y,w,h we have rows, columns, ... need to convert
figure; imshow(img); hold on; rectangle('Position', rect);

to crop an image
first create boundry box where you want to crop.
crp = imcrop(original_image_name,boundry_box);
I have done this in my assignment. This really works!!!!!!

Related

Contrast Stretching for colore images with MATLAB

i'm working in matlab and i wanted to apply the Contrast Stretching for grey scale image and also RGB image ,
so for the grey scale i've tried this one and it worked
clear all;
clc;
itemp = imread('cameraman.tif'); %read the image
i = itemp(:,:,1);
rtemp = min(i); % find the min. value of pixels in all the
columns (row vector)
rmin = min(rtemp); % find the min. value of pixel in the image
rtemp = max(i); % find the max. value of pixels in all the
columns (row vector)
rmax = max(rtemp); % find the max. value of pixel in the image
m = 255/(rmax - rmin); % find the slope of line joining point
(0,255) to (rmin,rmax)
c = 255 - m*rmax; % find the intercept of the straight line
with the axis
i_new = m*i + c; % transform the image according to new slope
figure,imshow(i); % display original image
figure,imshow(i_new); % display transformed image
this is for greyscale image ,
the problem is that that i don't know how to do for the RGB image
any idea? how to implement that?
thank you :)
Could the function stretchlim (reference) be useful for your purpose?
Find limits to contrast stretch image.
Low_High = stretchlim(RGB,Tol) returns Low_High, a two-element vector
of pixel values that specify lower and upper limits that can be used
for contrast stretching truecolor image RGB.
img = imread('myimg.png');
lohi = stretchlim(img,[0.2 0.8]);
If you write
rmin = min(i(:));
Then it computes the minimum over all values in i. This will work for RGB images also, which simply are 3D matrices with 3 values along the 3rd dimension.
The rest of your code also applies directly to such images.

How can I convert an RGB histogram into a color spectrum?

How can I convert an RGB histogram of an image to create a histogram showing the combined colors along with correct color wavelength range?
Example code:
pkg load image
f=imread('/tmp/marbles.jpg');
f=uint8(f); %need to convert back to uint8 to show picture
%Split into RGB Channels
f_red = f(:,:,1);
f_green = f(:,:,2);
f_blue = f(:,:,3);
%Get histValues for each channel
[y_f_red, x] = imhist(f_red);
[y_f_green, x] = imhist(f_green);
[y_f_blue, x] = imhist(f_blue);
subplot (2,1,1); imshow(f);
subplot (2,1,2); plot(x, y_f_red, 'r', x, y_f_green, 'g', x, y_f_blue, 'b');
Example image along with separate RGB histogram the code produces:
I'm trying to get the histogram to look like the image below but have the colors go from red to blue:
Another image example:
PS: I'm using Octave 4.0 which is very similar to MATLAB.
There's a huge hurdle to converting between standard color representations (like RGB or HSV) and spectral wavelength: many colors can't be represented by a single wavelength of light. Colors such as magenta, pink, brown, or any grayscale color represent mixtures of different wavelengths. Generating an equivalent spectral wavelength is therefore a much more complicated endeavor (you may find some useful ideas and links here and here).
Creating histograms of the colors themselves may be a better way to go (illustrated in one of my other answers), but if you really want to relate color to wavelength in a simple fashion you can try the following...
A first step will be to convert RGB values to HSV values, then create a histogram of the hue channel. I'll adapt part of my answer from here to do that. The next step will be to map hues to wavelengths of light, using some rather gross approximations adapted from this answer:
rgbImage = imread('test_image.png'); % Load image
hsvImage = rgb2hsv(rgbImage); % Convert the image to HSV space
hPlane = 360.*hsvImage(:, :, 1); % Get the hue plane scaled from 0 to 360
binEdges = 0:270; % Edges of histogram bins
N = histc(hPlane(:), binEdges); % Bin the pixel hues from above
wavelength = 620-(170/270).*(0:269); % Approximate wavelength
hBar = bar(wavelength, N(1:end-1), 'histc'); % Plot the histogram
set(hBar, 'CData', 270:-1:1, ... % Change the color of the bars using
'CDataMapping', 'direct', ... % indexed color mapping (360 colors)
'EdgeColor', 'none'); % and remove edge coloring
colormap(hsv(360)); % Change to an HSV color map with 360 points
axis([450 620 0 max(N)]); % Change the axes limits
set(gca, 'Color', 'k'); % Change the axes background color
set(gcf, 'Pos', [50 400 560 200]); % Change the figure size
xlabel('Wavelength (nm)'); % Add an x label
ylabel('Bin counts'); % Add a y label
NOTE: For the above to work properly in Octave, it may be necessary to change the set(hBar, ... line to the following:
set(hBar, 'FaceColor', 'flat', 'EdgeColor', 'none');
set(get(hBar, 'Children'), 'CData', 270:-1:1, 'CDataMapping', 'direct');
And here's the histogram:
There is, however, one issue with this. If we instead use the code exactly as it is in my other answer to plot the histogram of all the hue values, we would get this:
Note that there is a big cluster of magenta, pink, and reddish pixels that gets excluded when we toss out part of the hue range to convert to wavelengths (they don't correspond to a single wavelength in the light spectrum). Incorporating these into the results would require a more complicated conversion from hue to wavelength.
you can not convert RGB to wavelength unless some physical properties of the image and light is met. Anyway you can fake this by inversing:
RGB values of visible spectrum
if you do not know how look at:
Reverse complex 2D lookup table
But the result will not be the same as physical wavelengths histogram ... For that you would need multi-band image acquisition either by rotating prism optics or by set of bandpass filters ...
PS. HSV is far from accurate ...
Btw. the easiest way to do this is create palette from the spectral colors and convert your input image to it (indexed colors) and then just create histogram sorted by wavelength (and or color index)...
Based on gnovices answer but with an image instead of bar (take 0.12s on my system):
rgbImage = imread ("17S9PUK.jpg");
hsvImage = rgb2hsv(rgbImage);
hPlane = 360 .* hsvImage(:, :, 1);
binEdges = 1:360;
N = histc (hPlane(:), binEdges);
cm = permute (hsv (360), [3 1 2]);
img = repmat (cm, max(N), 1);
row_index = max(N) - N';
sp = sparse (row_index(row_index>0), (1:360)(row_index>0), true);
mask = flipud (cumsum (sp));
img(repmat (logical(1 - full(mask)), [1 1 3])) = 0;
image (img)
set (gca, "ydir", "normal");
xlabel('hue');
ylabel('Bin counts');

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

remove the holes in an image by average values of surrounding pixels

can any one please help me in filling these black holes by values taken from neighboring non-zero pixels.
thanks
One nice way to do this is to is to solve the linear heat equation. What you do is fix the "temperature" (intensity) of the pixels in the good area and let the heat flow into the bad pixels. A passable, but somewhat slow, was to do this is repeatedly average the image then set the good pixels back to their original value with newImage(~badPixels) = myData(~badPixels);.
I do the following steps:
Find the bad pixels where the image is zero, then dilate to be sure we get everything
Apply a big blur to get us started faster
Average the image, then set the good pixels back to their original
Repeat step 3
Display
You could repeat averaging until the image stops changing, and you could use a smaller averaging kernel for higher precision---but this gives good results:
The code is as follows:
numIterations = 30;
avgPrecisionSize = 16; % smaller is better, but takes longer
% Read in the image grayscale:
originalImage = double(rgb2gray(imread('c:\temp\testimage.jpg')));
% 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
But you can get a similar solution using roifill from the image processing toolbox like so:
newImage2 = roifill(originalImage, badPixels);
figure(44);
clf;
imagesc(newImage2);
colormap gray
notice I'm using the same badPixels defined from before.
There is a file on Matlab file exchange, - inpaint_nans that does exactly what you want. The author explains why and in which cases it is better than Delaunay triangulation.
To fill one black area, do the following:
1) Identify a sub-region containing the black area, the smaller the better. The best case is just the boundary points of the black hole.
2) Create a Delaunay triangulation of the non-black points in inside the sub-region by:
tri = DelaunayTri(x,y); %# x, y (column vectors) are coordinates of the non-black points.
3) Determine the black points in which Delaunay triangle by:
[t, bc] = pointLocation(tri, [x_b, y_b]); %# x_b, y_b (column vectors) are coordinates of the black points
tri = tri(t,:);
4) Interpolate:
v_b = sum(v(tri).*bc,2); %# v contains the pixel values at the non-black points, and v_b are the interpolated values at the black points.

How to get image matrix from axes content

Is there a way to get the content of a contourf plot as an image matrix? I want rasterize only the content, not the axes, labels and the empty space of the entire figure.
My goal is to overlay a transparent, colored contour plot over a grayscale image and I don't see another way, since MATLAB has only one colormap per figure.
Try getframe and frame2im
Example from the frame2im documentation:
Create and capture an image using getframe and frame2im:
peaks %Make figure
f = getframe; %Capture screen shot
[im,map] = frame2im(f); %Return associated image data
if isempty(map) %Truecolor system
rgb = im;
else %Indexed system
rgb = ind2rgb(im,map); %Convert image data
end
Not a direct answer to the question, but this is how I think you could achieve your goal:
%# load in grayscale image
gray_im = rgb2gray(imread('peppers.png'));
%# converting n x m grey image to n x m x 3 rgb gray image
rgb_gray_im = cat( 3, gray_im, gray_im, gray_im );
%# displaying this image
imshow( rgb_gray_im );
%# plotting contourf on top with arbitrary colourmap
hold on
h = axes('position', [0.5, 0.5, 0.2, 0.2]);
z = peaks;
contourf(h, z, [min(z(:)), -6 : 8]);
Which gives the result:
The figure's colourmap is being used for the contourf plot. The background image is not relying on a colourmap, and is instead being displayed in truecolour - i.e. each pixel is being displayed as an RGB value defined in rgb_gray_im.
There are also other ways of getting around the MATLAB colourmap restrictions: see for example this blog post or these answers.