Image manipulation in matlab - matlab

I am a very new programmer (this is my second exercise), and I have been asked to shift an image (of a Galaxy) by a sinusoidal shift. I have tried to write a programme to do this as follows:
My programme:
GalaxyImage = imread('hs-2013-06-a-web.jpg');
image(GalaxyImage)
GalaxyImage=double(GalaxyImage)/255;
image(Sess2GalaxyIm(GalaxyImage));
My function:
function [GalaxySlanted] = Sess2GalaxyIm(GalaxyImage)
A = 20;
k = 3;
Y = size(GalaxyImage, 1);
X = size(GalaxyImage, 2);
max_shift = ceil(A*2);
GalaxySlanted = zeros(Y, X+max_shift, 3);
for y=1:Y
local_shift = ceil(A*sind(k*y));
local_x = 1:X;
local_x = local_x + local_shift;
GalaxySlanted(y, local_x, :)=GalaxyImage(y, :, :);
end
end
At the moment, when I run it, it seems to just be shifting my image by a constant amount and I don't see why.

Your image's shift is constant because in local_shift = ceil(A*sind(k*Y)); you used constant values for A,K and Y. may be this modification makes your code correct:(y instead of Y)
local_shift = ceil(A*sind(k*y));

There is a possible issue with how you define local_x. You start by adding some padding onto the image to compensate for the fact that the output x could be up to "A" pixels shifted on either side:
max_shift = ceil(A*2);
GalaxySlanted = zeros(Y, X+max_shift, 3);
However, in the loop, you ignore this and only start off with local_x as 1:X - so if the output from sind is negative you will be trying to index negative numbers into GalaxySlanted and get a resulting error. Changing that line to local_x = (1:X)+A; should work (with oMiD's modification of the shift).
Another quick note - there is actually a function im2double which will handle the image conversion, and scaling, in one go.

Related

How to delete specific values from matrix in matlab

I have image matrix 420x700, and I want to delete a specific value in each row, changing the image dimensions. It is like deleting a column from it, but not in a straight line, to become 420x699 image. I should keep the values before the deleted value horizontally and shift all the values after it back by 1 position.
RGB = imread('image.jpg');
I1 = RGB(:,:,1);
How do I do that?
This is a good question, and I cannot think of a way to do this without a for-loop.
Let M be the nr-by-nc matrix from which you want to remove a column, and R the nr-by-1 vector with the column index of the element to be remove on each row.
The following code creates a new matrix A with the "column" removed from M, and vector B with the elements that were removed:
[nr,nc] = size(M);
A = zeros(nr,nc-1,'like',M);
B = zeros(nr,1,'like',M);
for k = 1:nr
r = R(k);
t = [ 1:r-1, r+1:nc ];
A(k,:) = M(k,t);
B(k) = M(k,r);
end
#beaker and #Cris are correct, but just to add some flavor to this, I've attempted to demonstrate an alternate method - using linear indexing, which can teach an interesting lesson on column major indexing of 2D arrays in MATLAB.
Another point to note is that this kind of process is what's followed in the seam carving algorithm, where we remove a vertical seam in this manner.
Load a test image to run this on - crop it to analyze easier.
I = imread('peppers.png');
I = I(100:100+9, 100:100+19, :);
figure, imshow(I)
Create a mask indicating which pixels are to be removed. This simulates the condition which I think you're pointing to - in this case, we choose random column indices for each row to be removed. You'd likely have this information as an input.
mask = zeros(size(I, [1:2]), 'logical');
for idx = 1:size(mask, 1)
randidx = randi(size(mask, 2));
mask(idx, randidx) = 1;
end
figure, imshow(mask)
Use the column major linear indexing trick to do the removal faster! Since we're removing a column at at time, we rotate the image 90 degrees, and translate this problem to removing one row at a time. MATLAB indexes 'vertically', and so we can then use linear indexing to simply remove the masked pixels all at once (rather than one row/column at a time), and then restore the shape using reshape, and finally rotate back to the original orientation.
It = rot90(I);
maskt = rot90(mask);
% Preallocate output
Ioutput = zeros([size(I, 1), size(I, 2) - 1, size(I, 3)], 'like', I);
for nchannel = 1:3
Icropped = It(:, :, nchannel);
% MATLAB indexes column wise - so, we can use linear indexing to make
% this computation simpler!
Icropped = Icropped(maskt(:) == 0);
Icropped = reshape(Icropped, [size(maskt, 1) - 1, size(maskt, 2)]);
% Restore the correct orientation after removing element!
Icropped = rot90(Icropped, 3);
Ioutput(:, :, nchannel) = Icropped;
end
figure, imshow(Ioutput)
I've cropped the 'peppers' image to demonstrate this, so that you can convince yourself that this is doing it right. This method should work similarly for larger images as well.

Average filter Matlab

I have written the 3x3 average filter. It works fine but it shows the same output image three times instead of one. How to resolve the problem?
The code is
function [filtr_image] = avgFilter(noisy_image)
[x,y] = size(noisy_image);
filtr_image = zeros(x,y);
for i = 2:x-1
for j =2:y-1
sum = 0;
for k = i-1:i+1
for l = j-1:j+1
sum = sum+noisy_image(k,l);
end
end
filtr_image(i,j) = sum/9.0;
filtr_image = uint8(filtr_image);
end
end
end
thanks in advance
What is most likely happening is the fact that you are supplying a colour image when the code is specifically meant for grayscale. The reason why you see "three" is because when you do this to allocate your output filtered image:
[x,y] = size(noisy_image)
If you have a 3D matrix, the number of columns reported by size will be y = size(noisy_image,2)*size(noisy_image,3);. As such, when you are iterating through each pixel in your image, in column major order each plane would be placed side by side each other. What you should do is either convert your image into grayscale from RGB or filter each plane separately.
Also, you have an unnecessary casting performed in the loop. Just do it once outside of the loop.
Option #1 - Filter per plane
function [filtr_image] = avgFilter(noisy_image)
[x,y,z] = size(noisy_image);
filtr_image = zeros(x,y,z,'uint8');
for a = 1 : z
for i = 2:x-1
for j =2:y-1
sum = 0;
for k = i-1:i+1
for l = j-1:j+1
sum = sum+noisy_image(k,l,a);
end
end
filtr_image(i,j,a) = sum/9.0;
end
end
end
end
Then you'd call it by:
filtr_image = avgFilter(noisy_image);
Option #2 - Convert to grayscale
filtr_image = avgFilter(rgb2gray(noisy_image));
Minor Note
You are using sum as a variable. sum is an actual function in MATLAB and you would be overshadowing this function with your variable. This will have unintended consequences if you have other functions that rely on sum later down the line.
I can't see why your code would repeat the image (unless it's a pattern cause by an integer overflow :/ ) but here are some suggestions:
if you want to use loops, at least drop the inner loops:
[x,y] = size(noisy_image);
filtr_image = zeros(x,y);
for i = 2:x-1
for j =2:y-1
% // you could do this in 1 line if you use mean2(...) instead
sub = noisy_image(i-1:i+1, j-1:j+1);
filtr_image = uint8(mean(sub(:)));
end
end
However do you know about convolution? Matlab has a built in function for this:
filter = ones(3)/9;
filtr_image = uint8(conv2(noisy_image, filter, 'same'));

Image template matching using correlation

I am developing a template matching program in MATLAB. The code runs well, and finds the closest result. My first question, in this code, I am using the function corr2(). I would like to try a version using the formula (I tried to upload a picture of but I need 10 reputations).
I understand the formula itself, but I am not sure what variables should I use to use it. For example, what is exactly the m and n mean in my images where can I get them? In another words, what does the formula take as inputs?
Second question is, when I run the code I have now, it takes long, is there any thing I can change to speed it up?
Original = imread('Red.jpg'); % Read original image
Template = imread('temp.png'); % Read template image
OriDu = im2double(Original); % convert original image
TempDu = im2double(Template); % convert template
OriH = size(Original, 1); %height of the Original image
OriW = size(Original, 2); %width of the Original image
OriD = size(Original, 3); %colour depth
TempH = size(Template, 1); %height of the Template image
TempW = size(Template, 2); %width of the Template image
TempD = size(Template, 3); %colour depth
TempDu = reshape(TempDu, TempH*TempW, 3);
corr = 0; % to check the best correlation found
%% two for loops to go through the original image.
for i = 1:OriH-TempH
for j = 1:OriW-TempW
% take a segment of the original image( same size as the template size)
segment = OriDu(i: (i - 1) + TempH, j: (j - 1) + TempW, :);
segment = reshape(segment, TempH*TempW, 3);
output = corr2(TempDu, segment);
if output > corr
corr = output;
x = i;
y = j;
end
end
end
figure;
subplot(1,2,1), imshow(Template), title('Template');
subplot(1,2,2), imshow(OriDu(x:x+TempH, y:y+TempW, :)),title('Segment of the similar part');

How do I format a MATLAB figure text annotation into rows and columns?

I have a script which fits some optical data to a sum of Lorentzian oscillators, and then spits out a figure with the original data and the fit. I would also like to include a text annotation with a table of the fitting parameters, but cannot figure out how to get rows and columns in my text box.
Each peak has 3 parameters and then there are 3 more global fitting parameters. My first try was to do this:
ParamTableLabels = {'\omega_p (cm^{-1})', '\omega_0 (cm^{-1})', '\Gamma (cm^{-1})'};
ParamTableVals = num2cell(Ef);
ParamTableLabels2 = {'d (\mu{m})','\epsilon_\infty','Scale'};
ParamTableVals2 = {ThickFit,EinfFit,ScaleFit};
ParamTable = vertcat(ParamTableLabels,ParamTableVals,ParamTableLabels2,ParamTableVals2);
where Ef is my 3xN matrix of fitting parameters. After generating my figure, I try to place the table in my plot at a suitable set of coordinates X,Y using:
text(X,Y,ParamTable)
and the result is a single column of text, no rows. My second attempt, which sort of works is to break up each column:
text(X, Y,ParamTable(:,1));
text(X+dX, Y,ParamTable(:,2));
text(X+2*dX,Y,ParamTable(:,3));
This almost works, but the subscripts in the labels throw off the vertical alignment of the last few rows, and it takes an undue amount of tinkering to get the spacing correct. I'm spending more time trying to get the text box to look right than to do the actual modelling.
How can I programatically format a block of text, containing both labels and variables, into rows and columns, and then use it as a text annotation in a figure with minimal user tinkering?
This is a not well supported using basic commands. But you can at least save yourself the trouble of guessing the subsequent X positions by making Matlab do the work for you.
The key is the "Extent" read-only parameter attached to a text block. Use docsearch text properties to see the documentation.
Putting this into some code:
padFraction = 0.1; %This is roughly the unitless padding between columns, as a fraction of the column on the left.
curX = X; %Leave the initial X variable unchanged
%For each text block column, add the text block, get the extent, and adjust curX
h = text(curX, Y,ParamTable(:,1));
curExtent = get(h, 'Extent');
curX = curExtent(1) + curExtent(3)*(1+padFraction);
h = text(curX, Y,ParamTable(:,2));
curExtent = get(h, 'Extent');
curX = curExtent(1) + curExtent(3)*(1+padFraction);
text(curX,Y,ParamTable(:,3));
The full script used to generate/test is below:
ParamTableLabels = {'\omega_p (cm^{-1})', '\omega_0 (cm^{-1})', '\Gamma (cm^{-1})'};
Ef = round(rand(10,3)*100);
ParamTableVals = num2cell(Ef);
ParamTableLabels2 = {'d (\mu{m})','\epsilon_\infty','Scale'};
ParamTableVals2 = {'ThickFit','EinfFit','ScaleFit'};
ParamTable = vertcat(ParamTableLabels,ParamTableVals,ParamTableLabels2,ParamTableVals2);
X = 1; Y = 1.1;
%Put something in the plot
figure(1); clf; hold on;
plot(-10:10, randn(21,1)*20,'.');
codeblock = 3;
switch codeblock
case 1
text(X,Y,ParamTable)
case 2
dX = 3;
text(X, Y,ParamTable(:,1));
text(X+dX, Y,ParamTable(:,2));
text(X+2*dX,Y,ParamTable(:,3));
case 3
padFraction = 0.1;
curX = X;
h = text(curX, Y,ParamTable(:,1));
curExtent = get(h, 'Extent');
curX = curExtent(1) + curExtent(3)*(1+padFraction);
h = text(curX, Y,ParamTable(:,2));
curExtent = get(h, 'Extent');
curX = curExtent(1) + curExtent(3)*(1+padFraction);
text(curX,Y,ParamTable(:,3));
end

Matlab: seqlogo with uniform plot column heights

In Matlab, I want to make a seqlogo plot of an amino acid sequence profile. But instead of scaling the heights of the plot columns by entropy, I want all the columns to be the same height.
I'm in the process of modifying the code from the answers to this question, but I wonder if there is a parameter to seqlogo or some other function that I have missed that will make the column heights uniform.
Alternatively, is there a statistical transformation I can apply to the sequence profile to hack the desired output? (column heights uniform, height of each letter linearly proportion to
its probability in the seqprofile)
Probably the easiest way around this problem is to directly modify the code for the Bioinformatics Toolbox function SEQLOGO (if possible). In R2010b, you can do:
edit seqlogo
And the code for the function will be shown in the editor. Next, find the following lines (lines 267-284) and either comment them out or remove them entirely:
S_before = log2(nSymbols);
freqM(freqM == 0) = 1; % log2(1) = 0
% The uncertainty after the input at each position
S_after = -sum(log2(freqM).*freqM, 1);
if corrError
% The number of sequences correction factor
e_corr = (nSymbols -1)/(2* log(2) * numSeq);
R = S_before - (S_after + e_corr);
else
R = S_before - S_after;
end
nPos = (endPos - startPos) + 1;
for i =1:nPos
wtM(:, i) = wtM(:, i) * R(i);
end
Then put this line in their place:
wtM = bsxfun(#times,wtM,log2(nSymbols)./sum(wtM));
You will probably want to save the file under a new name, like seqlogo_norm.m, so you can still use the original unmodified SEQLOGO function. Now you can create sequence profile plots with all the columns normalized to the same height. For example:
S = {'LSGGQRQRVAIARALAL',... %# Sample amino acid sequence
'LSGGEKQRVAIARALMN',...
'LSGGQIQRVLLARALAA',...
'LSGGERRRLEIACVLAL',...
'FSGGEKKKNELWQMLAL',...
'LSGGERRRLEIACVLAL'};
seqlogo_norm(S,'alphabet','aa'); %# Use the modified SEQLOGO function
OLD ANSWER:
I'm not sure how to transform the sequence profile information to get the desired output from the Bioinformatics Toolbox function SEQLOGO, but I can show you how to modify the alternative seqlogo_new.m that I wrote for my answer to the related question you linked to. If you change the line that initializes bitValues from this:
bitValues = W{2};
to this:
bitValues = bsxfun(#rdivide,W{2},sum(W{2}));
Then you should get each column scaled to a height of 1. For example:
S = {'ATTATAGCAAACTA',... %# Sample sequence
'AACATGCCAAAGTA',...
'ATCATGCAAAAGGA'};
seqlogo_new(S); %# After applying the above modification
For now, my workaround is to generate a bunch of fake sequences that match the sequence profile, then feed those sequences to http://weblogo.berkeley.edu/logo.cgi . Here is the code to make the fake sequences:
function flatFakeSeqsFromPwm(pwm, letterOrder, nSeqsToGen, outFilename)
%translates a pwm into a bunch of fake seqs with the same probabilities
%for use with http://weblogo.berkeley.edu/
%pwm should be a 4xn or a 20xn position weight matrix. Each col must sum to 1
%letterOrder = e.g. 'ARNDCQEGHILKMFPSTWYV' for my data
%nSeqsToGen should be >= the # of pixels tall you plan to make your chart
[height windowWidth] = size(pwm);
assert(height == length(letterOrder));
assert(isequal(abs(1-sum(pwm)) < 1.0e-10, ones(1, windowWidth))); %assert all cols of pwm sum to 1.0
fd = fopen(outFilename, 'w');
for i = 0:nSeqsToGen-1
for seqPos = 1:windowWidth
acc = 0; %accumulator
idx = 0;
while i/nSeqsToGen >= acc
idx = idx + 1;
acc = acc + pwm(idx, seqPos);
end
fprintf(fd, '%s', letterOrder(idx));
end
fprintf(fd, '\n');
end
fclose(fd);
end