How to smooth the perimeter output of bwperim? - matlab

I have a segmented image
When I apply bwperim function on this I get the output as below
I want to have a thin line of perimeter - just one pixel-thick. This is essential for further processing work. What is the best approach?
Please suggest.
======
BoundingBox
%%% ComputeBoundingBox
%%%
function [stats, statsAlreadyComputed] = ...
ComputeBoundingBox(imageSize,stats,statsAlreadyComputed)
% [minC minR width height]; minC and minR end in .5.
if ~statsAlreadyComputed.BoundingBox
statsAlreadyComputed.BoundingBox = 1;
[stats, statsAlreadyComputed] = ...
ComputePixelList(imageSize,stats,statsAlreadyComputed);
num_dims = numel(imageSize);
for k = 1:length(stats)
list = stats(k).PixelList;
if (isempty(list))
stats(k).BoundingBox = [0.5*ones(1,num_dims) zeros(1,num_dims)];
else
min_corner = min(list,[],1) - 0.5;
max_corner = max(list,[],1) + 0.5;
stats(k).BoundingBox = [min_corner (max_corner - min_corner)];
end
end
end

That is happening because your image had quantization error when you were saving the image. Did you save your image using a lossy compression algorithm, like JPEG? If you want to preserve the intensities so that they don't change when you save the image, use a lossless compression algorithm, like PNG.
To eliminate these "noisy" effects, threshold your image first to eliminate any quantization errors so that you can set these pixels to completely white, then try using bwperim again. In other words, do something like this:
im = im2bw(imread('http://i.stack.imgur.com/dagEc.png'));
im_noborder = imclearborder(im);
out = bwperim(im_noborder);
imshow(out);
The first line of code reads in your image directly from StackOverflow and we use im2bw to threshold your image. This image was originally grayscale, and so we want to convert this into black and white only. This will also remove any quantization artifacts as it thresholds anything higher than 128. The next line of code removes the white border with imclearborder that surrounds your shape because the image you uploaded has a white border surrounding it for some reason. Once we remove this border, we then apply bwperim and we show the image.
This is the image I get:

Related

Matlab - Removing image background using normalization

I have an image like this:
my goal is to get the output under background normalization at this link.
Following the above link, I did the following:
(1). I first dilate the image to get the background
(2). then try to remove it via normalization
I got the background:
However, when I try to do the normalized division, I get this :
(black borders added to make clear of the boundary of the image)
this is my code:
image = imread('image.png');
image = rgb2gray(image);
se = offsetstrel('ball',9,9);
dilatedI = imdilate(image,se);
output = imdivide(image,dilatedI);
imshow(output,[]);
using
imshow(output)
just gives a black image.
I thought it might be a type conversion issue, but based on the resources mentioned earlier, I am uncertain if it is the case...
Any advice would be appreciated
Just make sure you dont do integer division! your images are integer type, so 4/5 returns 0 and 5/4 returns 1, not a floating point number. Just convert to float before dividing:
image = imread('https://i.stack.imgur.com/bIVRT.png');
%image = rgb2gray(image); The image is not a RGB online
se = offsetstrel('ball',21,21);
dilatedI = imdilate(image,se);
output = imdivide(double(image),double(dilatedI));
figure
subplot(121)
imshow(image);
subplot(122)
imshow(output);

How to "disturb" an image with noise, but keep the orginal undisturbed pixel intact, and write to file in imagesc

For simple explanation of the title, imagine you're using a Photoshop layers, where noise is on the top layer.
% load a test image
I = rgb2gray(imread('peppers.png'));
% recreate image
cmap = colormap(); % grab current colormap
ncolors = size(cmap,1);
% do what imagesc does
Iind = double(I) - double(min(I(:)));
Iind = Iind / max(Iind(:));
% quantize image
Iind = round(Iind * ncolors + 0.5);
Iind(Iind > ncolors) = ncolors;
Iind(Iind < 1) = 1;
% convert to RGB from indexed image using cmap as palette
Irgb = ind2rgb(Iind,cmap);
imwrite(Irgb, 'filename.bmp');
These code will scale the colormap and write to file. However, by adding a artificial noise at a certain pixel location after loaded the image:
I(50,50) = rand(1);
This will generate a completely different colormap visually, and the one with the noise will look a little washed out. Top image is the original and bottom is with noise.
EDIT: The below image is the cropped image on the top left corner. Left is original and right is with noise. On the right you can see the color washed out a little bit, and if you look closely you can see a noise (dark blue color, position (50,50)).
Any idea how to still maintain the original after noise was added? Thanks in advance!
The issue is that you're adding noise (between 0 and 1) to the original image which has min = 8 and max = 255. This is reducing the new min to either 0 or 1 (the input image is of type uint8, and it seems MATLAB rounds the random number) which stretches out the colourmap slightly. There are two options to avoid this.
Add random noise in the range of the original image
I(50,50) = randi([min(I(:)), max(I(:))]);
Add random noise after scaling the original image
Irgb(50,50) = rand(1);

Extract foreground from background in matlab

I have 2 images. One is background image and other image has same background but with some foreground object. I want to extract foreground object from background. Simple subtraction operation in matlab will not suffice as it subtracts RGB value of background image from that of foreground image (as in below code).
im1 = imread('output/frame-1.jpg')
im2 = imread('output/frame-7.jpg')
%# subtract
deltaImage = im1 - im2;
imshow(deltaImage)
So if background color is white and foreground object is blue, then output (i.e. deltaImage) comes foreground object with orange color with black background. However the output I want is foreground object with blue color (i.e. original color) with black background. How can I get this ? I tried to do it using below code, but output image is incorrect.
im1 = imread('foreground.jpg')
im2 = imread('background.jpg')
[m n k]=size(im2);
deltaImage = zeros(m,n,3);
fprintf('%d %d %d.\n',m,n,k);
for l=1:k
for i=1:m-1
for j=1:n-1
if im1(i:j:l)~=im2(i:j:l)
deltaImage(i,j,l) = im1(i,j,l);
end
end
end
end
imshow(deltaImage)
Background IMAGE
Foreground Image
Output Image (Here I want color of man to be blue)
You can use deltaImage to create a mask (zeros and ones image) that multiplies the foreground. However, note that you will have artifacts associated with lossy image compression (.jpeg). These can be reduced, to some extent, if you use a threshold, like the average difference or a specific value you want. Try this:
im1 = double(imread('~/Downloads/foreground.jpg'));
im2 = double(imread('~/Downloads/background.jpg'));
compute the difference of the averages of the 3 channels
deltaImage = mean(im2,3) - mean(im1,3);
then use the product of the mean by a standard deviation (~3), or uncomment the line below to use a specific threshold, like 128
mask = deltaImage>3*mean(deltaImage(:));
% mask = deltaImage>128;
then assuming all original images are in 8-bit format produce a result also in 8-bit format:
result = uint8(cat(3, im1(:,:,1).*mask, im1(:,:,2).*mask, im1(:,:,3).*mask));
imshow(result)
And this is the result you should get:
Again the weird looking pixels around the main object are artifacts of lossy image compression (.jpeg), you should try working with lossless like .png formats.

How do I convert the whole image to grayscale except for a sub image which should be in color?

I have an image and a subimage which is cropped out of the original image.
Here's the code I have written so far:
val1 = imread(img);
val2 = imread(img_w);
gray1 = rgb2gray(val1);%grayscaling both images
gray2 = rgb2gray(val2);
matchingval = normxcorr2(gray1,gray2);%normalized cross correlation
[max_c,imax]=max(abs(matchingval(:)));
After this I am stuck. I have no idea how to change the whole image grayscale except for the sub image which should be in color.
How do I do this?
Thank you.
If you know what the coordinates are for your image, you can always just use the rgb2gray on just the section of interest.
For instance, I tried this on an image just now:
im(500:1045,500:1200,1)=rgb2gray(im(500:1045,500:1200,1:3));
im(500:1045,500:1200,2)=rgb2gray(im(500:1045,500:1200,1:3));
im(500:1045,500:1200,3)=rgb2gray(im(500:1045,500:1200,1:3));
Where I took the rows (500 to 1045), columns (500 to 1200), and the rgb depth (1 to 3) of the image and applied the rgb2gray function to just that. I did it three times as the output of rgb2gray is a 2d matrix and a color image is a 3d matrix, so I needed to change it layer by layer.
This worked for me, making only part of the image gray but leaving the rest in color.
The issue you might have though is this, a color image is 3 dimensions while a gray scale need only be 2 dimensions. Combining them means that the gray scale must be in a 3d matrix.
Depending on what you want to do, this technique may or may not help.
Judging from your code, you are reading the image and the subimage in MATLAB. What you need to know are the coordinates of where you extracted the subimage. Once you do that, simply take your original colour image, convert that to grayscale, then duplicate this image in the third dimension three times. You need to do this so that you can place colour pixels in this image.
For RGB images, grayscale images have the RGB components to all be the same. Duplicating this image in the third dimension three times creates the RGB version of the grayscale image. Once you do that, simply use the row and column coordinates of where you extracted the subimage and place that into the equivalent RGB grayscale image.
As such, given your colour image that is stored in img and your subimage stored in imgsub, and specifying the rows and columns of where you extracted the subimage in row1,col1 and row2,col2 - with row1,col1 being the top left corner of the subimage and row2,col2 is the bottom right corner, do this:
img_gray = rgb2gray(img);
img_gray = cat(3, img_gray, img_gray, img_gray);
img_gray(row1:row2, col1:col2,:) = imgsub;
To demonstrate this, let's try this with an image in MATLAB. We'll use the onion.png image that's part of the image processing toolbox in MATLAB. Therefore:
img = imread('onion.png');
Let's also define our row1,col1,row2,col2:
row1 = 50;
row2 = 90;
col1 = 80;
col2 = 150;
Let's get the subimage:
imgsub = img(row1:row2,col1:col2,:);
Running the above code, this is the image we get:
I took the same example as rayryeng's answer and tried to solve by HSV conversion.
The basic idea is to set the second layer i.e saturation layer to 0 (so that they are grayscale). then rewrite the layer with the original saturation layer only for the sub image area, so that, they alone have the saturation values.
Code:
img = imread('onion.png');
img = rgb2hsv(img);
sPlane = zeros(size(img(:,:,1)));
sPlane(50:90,80:150) = img(50:90,80:150,2);
img(:,:,2) = sPlane;
img = hsv2rgb(img);
imshow(img);
Output: (Same as rayryeng's output)
Related Answer with more details here

Changing image aspect ratio of interpolated RGB image. Square to rectangular

I have some code which takes a fish eye images and converts it to a rectangular image in each RGB channels. I am having trouble with the fact the the output image is square instead of rectangular. (this means that the image is distorted, compressed horizontally.) I have tried changing the output matrix to a more suitable format, without success. Besides this i have also discovered that for the code to work the input image must be square like 500x500. Any idea how to solve this issue? This is the code:
The code is inspired by Prakash Manandhar "Polar To/From Rectangular Transform of Images" file exchange on mathworks.
EDIT. Code now works.
function imP = FISHCOLOR2(imR)
rMin=0.1;
rMax=1;
[Mr, Nr, Dr] = size(imR); % size of rectangular image
xRc = (Mr+1)/2; % co-ordinates of the center of the image
yRc = (Nr+1)/2;
sx = (Mr-1)/2; % scale factors
sy = (Nr-1)/2;
reduced_dim = min(size(imR,1),size(imR,2));
imR = imresize(imR,[reduced_dim reduced_dim]);
M=size(imR,1);N=size(imR,2);
dr = (rMax - rMin)/(M-1);
dth = 2*pi/N;
r=rMin:dr:rMin+(M-1)*dr;
th=(0:dth:(N-1)*dth)';
[r,th]=meshgrid(r,th);
x=r.*cos(th);
y=r.*sin(th);
xR = x*sx + xRc;
yR = y*sy + yRc;
for k=1:Dr % colors
imP(:,:,k) = interp2(imR(:,:,k), xR, yR); % add k channel
end
imP = imresize(imP,[size(imP,1), size(imP,2)/3]);
imP = imrotate(imP,270);
SOLVED
Input image <- Image link
Output image <- Image link
PART A
To remove the requirement of a square input image, you may resize the input image into a square one with this -
%%// Resize the input image to make it square
reduced_dim = min(size(imR,1),size(imR,2));
imR = imresize(imR,[reduced_dim reduced_dim]);
Few points I would like to raise here though about this image-resizing to make it a square image. This was a quick and dirty approach and distorts the image for a non-square image, which you may not want if the image is not too "squarish". In many of those non-squarish images, you would find blackish borders across the boundaries of the image. If you can remove that using some sort of image processing algorithm or just manual photoshoping, then it would be ideal. After that even if the image is not square, imresize could be considered a safe option.
PART B
Now, after doing the main processing of flattening out the fisheye image,
at the end of your code, it seemed like the image has to be rotated
90 degrees clockwise or counter-clockwise depending on if the fisheye
image have objects inwardly or outwardly respectively.
%%// Rotating image
imP = imrotate(imP,-90); %%// When projected inwardly
imP = imrotate(imP,-90); %%// When projected outwardly
Note that the flattened image must have the height equal to the half of the
size of the input square image, that is the radius of the image.
Thus, the final output image must have number of rows as - size(imP,2)/2
Since you are flattening out a fisheye image, I assumed that the width
of the flattened image must be 2*PI times the height of it. So, I tried this -
imP = imresize(imP,[size(imP,2)/2 pi*size(imP,2)]);
But the results looked too flattened out. So, the next logical experimental
value looked like PI times the height, i.e. -
imP = imresize(imP,[size(imP,2)/2 pi*size(imP,2)/2]);
Results in this case looked good.
I'm not very experienced in the finer points of image processing in MATLAB, but depending on the exact operation of the imP fill mechanism, you may get what you're looking for by doing the following. Change:
M = size(imR, 1);
N = size(imR, 2);
To:
verticalScaleFactor = 0.5;
M = size(imR, 1) * verticalScaleFactor;
N = size(imR, 2);
If my hunch is right, you should be able to tune that scale factor to get the image just right. It may, however, break your code. Let me know if it doesn't work, and edit your post to flesh out exactly what each section of code does. Then we should be able to give it another shot. Good luck!
This is the code which works.
function imP = FISHCOLOR2(imR)
rMin=0.1;
rMax=1;
[Mr, Nr, Dr] = size(imR); % size of rectangular image
xRc = (Mr+1)/2; % co-ordinates of the center of the image
yRc = (Nr+1)/2;
sx = (Mr-1)/2; % scale factors
sy = (Nr-1)/2;
reduced_dim = min(size(imR,1),size(imR,2));
imR = imresize(imR,[reduced_dim reduced_dim]);
M=size(imR,1);N=size(imR,2);
dr = (rMax - rMin)/(M-1);
dth = 2*pi/N;
r=rMin:dr:rMin+(M-1)*dr;
th=(0:dth:(N-1)*dth)';
[r,th]=meshgrid(r,th);
x=r.*cos(th);
y=r.*sin(th);
xR = x*sx + xRc;
yR = y*sy + yRc;
for k=1:Dr % colors
imP(:,:,k) = interp2(imR(:,:,k), xR, yR); % add k channel
end
imP = imresize(imP,[size(imP,1), size(imP,2)/3]);
imP1 = imrotate(imP1,270);