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

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)

Related

How can I merge multiple images into one and save it on matlab?

I need to merge multiple bitmap of same sizes into one image.That image is basically rotated in different angles and needs to be merged into one whole image. I have tried multiple methods but I come with many issues as I am not able to save that image.
I have tried multiple codes but I actually cannot make sense out of it. What I want to achieve is transparent overlay (not sure) that superimposes two images and you can actually see both one image
figure1 = figure;
ax1 = axes('Parent',figure1);
ax2 = axes('Parent',figure1);
set(ax1,'Visible','off');
set(ax2,'Visible','off');
[a,map,alpha] = imread('E:\training data\0.bmp');
I = imshow(a,'Parent',ax2);
set(I,'AlphaData',alpha);
F = imshow('E:\training data\200.bmp','Parent',ax1);
I just want to superimpose multiple images.
This is my data set:
This is what I want to achieve, i want to add all of the rotated images and achieved into one
This is what I get sadly, I have tried everything
The following does kind of what you want. First load the image, then divide it into 6 equal blocks, and add these. To add the pixel values, I first converted the image to doubles, since uint8 only can go up to pixel values of 255. This would mean that you will just see a large bright spot in the image because you are clipping.
Then add all the blocks. You will see in the output, that the car is not always perfect in the center of the block, so depending on what you are trying to achieve you may want to align the blocks using something like xcorr2.
% load image
A = imread('S82CW.jpg');
fig = figure(1); clf
image(A);
% convert A to double and divide in blocks.
A = double(A);
[img_h, img_w, ~] = size(A);
block_h = img_h/2;
block_w = img_w/3;
% split image in blocks
Asplit = mat2cell(A, repelem(block_h,2), repelem(block_w,3), 3);
% check if splitting makes sense
figure(2); clf
for k = 1:numel(Asplit)
subplot(3,2,k)
image(uint8(Asplit{k}))
end
% superimpose all blocks,
A_super = zeros(size(Asplit{1,1}),'like',Asplit{1,1} ); % init array, make sure same datatype
for k = 1:numel(Asplit)
A_super = A_super + Asplit{k};
end
% divide by max value in A and multiply by 255 to make pixel
% values fit in uint8 (0-255)
A_super_unit8 = uint8(A_super/max(A_super,[],'all')*255);
figure(3); clf;
image(A_super_unit8)

Wrong output for 2d convolution function

As the title says, the output I’m getting out of this function is incorrect. By incorrect I mean that the data are overflowing. How do I normalise the matrix correctly? Currently almost all of the pictures I get are white.
I called the function from another MATLAB file like this:
mask = [3,10,3;0,0,0;-3,-10,-3];
A = imread(“football.jpg”);
B = ConvFun(A,mask);
function [ image ] = ConvFun( img,matrix )
[rows,cols] = size(img); %// Change
%// New - Create a padded matrix that is the same class as the input
new_img = zeros(rows+2,cols+2);
new_img = cast(new_img, class(img));
%// New - Place original image in padded result
new_img(2:end-1,2:end-1) = img;
%// Also create new output image the same size as the padded result
image = zeros(size(new_img));
image = cast(image, class(img));
for i=2:1:rows+1 %// Change
for j=2:1:cols+1 %// Change
value=0;
for g=-1:1:1
for l=-1:1:1
value=value+new_img(i+g,j+l)*matrix(g+2,l+2); %// Change
end
end
image(i,j)=value;
end
end
%// Change
%// Crop the image and remove the extra border pixels
image = image(2:end-1,2:end-1);
imshow(image)
end
In a convolution, if you want the pixel value to stay in the same range
, you need to make the mask add up to 1. Just divide the mask by sum(mask(:)) after defining it. This is however, not the case you are dealing with.
Sometimes that is not the needed. For example if you are doing edge detection (like the kernel you show), you don't really care about maintaining the pixel values. In those cases, the plotting of unnormalized images is more the problem. You can always set the imshow function to auto select display range: imshow(image,[]).
Also, I hope this is homework, as this is the absolutely worst way to code convolution. FFT based convolution is about 100 times faster generally, and MATLAB has an inbuilt for it.

how to segment the colony When I can't use bw image

I tried to use image processing toolbox to count my colony. I used imfindcircles to find the colony and count. But I got some problems:
(1)Due to that my colony could be white and black, I tried to find the colony by using Bright or Dark in ObjectPolarity, and use a if loop to select which one I finally choose. But the first step of my if loop doesn't really work.
(2) For using imfindcircles to find the circle, I found it works for the colony in white, while the method is a disaster for the black colony. I'm kind of desperate now because I can't find other ways to segment the colony.
So finally i need to: label each colony in the plate, count the colony number, calculate each colony size, extract the mean gray value for each colony (the colour).
Thank you very much!!!
So here is my code:
im = imread(test.jpeg)
imshow(im)
% to find the colony
[centersBright, radiiBright] = imfindcircles(im,[30 60],'ObjectPolarity','bright','Sensitivity',0.925,'Method','twostage','EdgeThreshold',0.1)
[centersDark, radiiDark] = imfindcircles(im,[30 60],'ObjectPolarity','dark','Sensitivity',0.925,'Method','twostage','EdgeThreshold',0.15)
% to select which one is the correct count. if one of the length(centres) value is lower than 10, I consider it as an error. But if both length(centres) is low than 10, I consider it as a treatment effect and accept the value.
if length(centersDark)<10<length(centersBright)
centers=centersBright
radii=radiiBright
elseif length(centersBright)<10<length(centersDark)
centers=centersDark
radii=radiiDark
else
centers=[centersBright,centersDark]
radii=[radiiBright,radiiDark]
end
% view and label the colony
h = viscircles(centers,radii)
for k = 1:length(radii)
string = sprintf('%d',k)
text(centers(k,1),centers(k,2),string,'color','y','HorizontalAlignment', 'center','VerticalAlignment', 'middle')
area(k)=pi*radii(k)^2
end
Suggested Solution
While it's hard to distinguish between the colonies and their surrounding in the black and white version of your input, it is not hard to do so in the hue space by using thresholding.
The reason is that the colonies have a unique hue which is different from their background.
This will be more noticable after converting to HSV space:
Therefore, I suggest the folowing solution:
convert input image to HSV space
use thresholding on the hue component.
extract connected components
Use the connected components which are large enough for the mask
perform imclose opertation for cleaning the artifacts
Code
%reads the image
I = imread('colony2.png');
%convert to hsv
hsvIm = rgb2hsv(I);
%thresholding on the hue space
bwIm = hsvIm(:,:,1) < 0.15;
%find connected components
CC = bwconncomp(bwIm);
%choose only cc with sufficient amount of pixels
numPixels = cellfun(#numel,CC.PixelIdxList);
relevantCC = CC.PixelIdxList(numPixels > 10);
%generate a binary mask with these conencted componants
colonyMask = false(size(bwIm));
for ii=1:length(relevantCC)
colonyMask(relevantCC{ii}) = true;
end
%perform morpholopical operations for cleaning
colonyMask = imclose(colonyMask,strel('disk',1));
%display result
h = imshow(I); % Save the handle; we'll need it later
set(h, 'AlphaData', ~colonyMask);
Results
Selection of threshold
The threshold was chosen by choosing the first pick in the histogram of the hue component
histogram(hsvIm(:,:,1))

How can I traverse through pixels?

Suppose, I have the following image in my hand.
I have marked some pixels of the image as follows,
Now, I have obtained the pixel mask,
How can I traverse through only those pixels that are in that mask?
Given a binary mask, mask, where you want to iterate over all the true pixels in mask, you have at least two options that are both better than the double for loop example.
1) Logical indexing.
I(mask) = 255;
2) Use find.
linearIdx = find(mask);
I(linearIdx) = 255;
The original question:
How can I save only those pixels which I am interested in?
...
Question: Now, in the Step#2, I want to save those pixels in a data-structure (or, whatever) d so that I can apply another function f2(I, d, p,q,r) which does something on that image on the basis of those pixels d.
Create a binary mask
Try using a logical mask of the image to keep track of the pixels of interest.
I'll make up a random image for example here:
randImg = rand(64,64,3);
imgMask = false(size(randImg(:,:,1)));
imgMask(:,[1:4:end]) = true; % take every four columns This would be your d.
% Show what we are talking about
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
maskImg(imgMaskForRGB) = randImg(imgMaskForRGB);
figure('name','Psychadelic');
subplot(2,1,1);
imagesc(randImg);
title('Random image');
subplot(2,1,2);
imagesc(maskImg);
title('Masked pixels of interest');
Here's what it looks like:
It will be up to you to determine how to store and use the image mask (d in your case) as I am not sure how your functions are written. Hopefully this example will give you an understanding of how it can be done though.
EDIT
You added a second question since I posted:
But, now the problem is, how am I going to traverse through those pixels in K?
Vectrorization
To set all pixels to white:
randImg(imgMaskForRGB) = 255;
In my example, I accessed all of the pixels of interest at the same time with my mask in a vectorized fashion.
I translated my 2D mask into a 3D mask, in order to grab the RGB values of each pixel. That was this code:
maskImg = zeros(size(randImg));
imgMaskForRGB = repmat(imgMask,1,1,3);
Then to access all of these pixels in the image of interest, I used this call:
randImg(imgMaskForRGB)
These are your pixels of interest. If you want to divide these values in 1/2 you could do something like this:
randImg(imgMaskForRGB) = randImg(imgMaskForRGB)/2;
Loops
If you really want to traverse, one pixel at a time, you can always use a double for loop:
for r=1:size(randImg,1)
for c=1:size(randImg,2)
if(imgMask(r,c)) % traverse all the pixels
curPixel = randImg(r,c,:); % grab the ones that are flagged
end
end
end
Okay. I have solved this using the answer of #informaton,
I = imread('gray_bear.png');
J = rgb2gray(imread('marked_bear.png'));
mask = I-J;
for r=1:size(I,1)
for c=1:size(I,2)
if(mask(r,c))
I(r,c) = 255;
end
end
end
imshow(I);

Negative values in Watershed algorithm leading to black image

I'm using the watershed algorithm to try and segment touching nuclei. A typical image may look like:
or this:
I'm trying to apply the watershed algorithm with this code:
show(RGB_img)
%Convert to grayscale image
I = rgb2gray(RGB_img);
%Take structuring element of a disk of size 10, for the morphological transformations
%Attempt to subtract the background from the image: top hat is the
%subtraction of the open image from the original
%Morphological transformation to subtract background noise from the image
%Tophat is the subtraction of an opened image from the original. Remove all
%images smaller than the structuring element of 10
I1 = imtophat(I, strel('disk', 10));
%Increases contrast
I2 = imadjust(I1);
%show(I2,'contrast')
%Assume we have background and foreground and assess thresh as such
level = graythresh(I2);
%Convert to binary image based on graythreshold
BW = im2bw(I2,level);
show(BW,'C');
BW = bwareaopen(BW,8);
show(BW,'C2');
BW = bwdist(BW) <= 1;
show(BW,'joined');
%Complement because we want image to be black and background white
C = ~BW;
%Use distance tranform to find nearest nonzero values from every pixel
D = -bwdist(C);
%Assign Minus infinity values to the values of C inside of the D image
% Modify the image so that the background pixels and the extended maxima
% pixels are forced to be the only local minima in the image (So you could
% hypothetically fill in water on the image
D(C) = -Inf;
%Gets 0 for all watershed lines and integers for each object (basins)
L = watershed(D);
show(L,'L');
%Takes the labels and converts to an RGB (Using hot colormap)
fin = label2rgb(L,'hot','w');
% show(fin,'fin');
im = I;
%Superimpose ridgelines,L has all of them as 0 -> so mark these as 0(black)
im(L==0)=0;
clean_img = L;
show(clean_img)
After C = ~BW; the whole image goes dark. I believe this is because the image pixels are all -inf or some smaller negative number. This is there a way around this and if so what could I change in my code to get this algorithm working? I've experimented a ton and I don't really know what's happening. Any help would be great!
The problem is with your show command. As you said in the comments this uses imshow under the hood. If you try imshow directly you'll see you also get a black image. However, if you call it with appropriate limits:
imshow(clean_img,[min(clean_img(:)), max(clean_img(:))])
you'll see everything you expect to see.
In general I usually prefer imagesc for that reason. imshow makes arbitrary judgements as to what range to represent, and I usually can't be bothered to keep up with it. I think in your case, your end image is uint16 so imshow chooses to represent the range [1, 65025]. Since all your pixel values are below 400, they look black to the naked eye for that range.