Fan Beam reconstruction artifacts in MATLAB - matlab

I have a set of pictures on which I need to perform Fan Beam projection and reconstruction by MATLAB. But for these pictures I've got weird artifacts.
Following the documentation on ifanbeam() function, I've written the following code:
ph = phantom(100);
d = 100;
fan_proj = fanbeam(ph,d);
fan_reproj = ifanbeam(fan_proj,d);
imshow(fan_reproj)
And it worked well:
But then i tried to explicitly save the phantom image on the drive (instead of loading it from MATLAB itself), load it from the drive, and perform the same operation.
ph = phantom(100);
imwrite(ph, 'phantom.png');
clear;
ph = imread('phantom.png');
d = 100;
fan_proj = fanbeam(ph,d);
fan_reproj = ifanbeam(fan_proj,d);
imshow(fan_reproj)
And the result was suffering from weird artifacts:
Why does this happen? What exactly changes in the image so the result of this function also changes so dramatically?

The issue is that imshow (by default) expects the input data to have values between 0 and 1. This is true when you first construct the phantom.
ph = phantom(100);
min(ph)
0
max(ph)
1
But when you load it back from the file it will have values between 0 and 255. This is because it was saved and loaded back in as an unsigned 8-bit integer:
ph = imread('phtnom.png');
class(ph)
uint8
min(ph)
0
max(ph)
255
And when you use imshow with the default color scaling it will still be [0,1] causing the extreme contrast that you are seeing.
You can use [] as the second input to imshow to ensure that the full dynamic range of the image is displayed.
imshow(fan_reproj, [])

Related

Matlab get vector of specific pixels

I am pretty new to Matlab and encountered a problem when working with images.
I want to get a pixel that is in a specific colour (blue) in the following image:
image
My current code looks something like this:
function p = mark(image)
%// display image I in figure
imshow(image);
%// first detect all blue values higher 60
high_blue = find(image(:,:,3)>60);
%cross elements is needed as an array later on, have to initialize it with 0
cross_elements = 0;
%// in this iteration the marked values are reduced to the ones
%where the statement R+G < B+70 applies
for i = 1:length(high_blue)
%// my image has the size 1024*768, so to access the red/green/blue values
%// i have to call the i-th, i+1024*768-th or i+1024*768*2-th position of the "array"
if ((image(high_blue(i))+image(high_blue(i)+768*1024))<...
image(high_blue(i)+2*768*1024)+70)
%add it to the array
cross_elements(end+1) = high_blue(i);
end
end
%// delete the zero element, it was only needed as a filler
cross_elements = cross_elements(cross_elements~=0);
high_vector = zeros(length(cross_elements),2);
for i = 1:length(cross_elements)
high_vector(i,1) = ceil(cross_elements(i)/768);
high_vector(i,2) = mod(cross_elements(i), 768);
end
black = zeros(768 ,1024);
for i = 1:length(high_vector)
black(high_vector(i,2), high_vector(i,1)) = 1;
end
cc = bwconncomp(black);
a = regionprops(cc, 'Centroid');
p = cat(1, a.Centroid);
%// considering the detection of the crosses:
%// RGB with B>100, R+G < 100 for B<150
%// consider detection in HSV?
%// close the figure
%// find(I(:,:,3)>150)
close;
end
but it is not optimized for Matlab, obviously.
So i was wondering if there was a way to search for pixels with specific values,
where the blue value is larger than 60 (not hard with the find command,
but at the same time the values in the red and green area not too high.
Is there a command I am missing?
Since English isn't my native language, it might even help if you gave me some suitable keywords for googling ;)
Thanks in advance
Based on your question at the end of the code, you could get what you want in a single line:
NewImage = OldImage(:,:,1) < SomeValue & OldImage(:,:,2) < SomeValue & OldImage(:,:,3) > 60;
imshow(NewImage);
for example, where as you see you provide a restriction for each channel using logical operators, that you can customize of course (eg. using | as logical OR). Is this what you are looking for? According to your code you seem to be looking for specific regions in the image like crosses or coins is that the case? Please provide more details if the code I gave you is completely off the track :)
Simple example:
A = imread('peppers.png');
B = A(:,:,3)>60 & A(:,:,2)<150 & A(:,:,1) < 100;
figure;
subplot(1,2,1);
imshow(A);
subplot(1,2,2)
imshow(B);
Giving this:

For Loop Matrix Error

I am writing some Matlab code to analyze spikes and their stimuli. In the first part of the code I get the timing of a spike and then find the frame that was shown on the screen just prior. I then want to take the image data from that frame and add it to my own movie data. The x and y components of myMovie and moviedata are both 128x128, however when I try to add the image values from a certain frame from the stimuli moviedata(:,:,j) to a specific frame in my movie myMovie(:,:,k) I get "error: matrix dimensions must agree". Is there any way to fix this?
Code:
for n=1:1100
t = blocks(5).spikes{1}(:,n);
for k=1:25
ind = find(round(double(blocks(5).frameEpocs*1000)/1000) == (t-(1/(25*10))));
j = blocks(5).frameEpocs(1,ind);
myMovie(:,:,k) = myMovie(:,:,k) + double(moviedata(:,:,j));
end
end
find may return an empty matrix and hence j would be empty as well.
In that case, the command myMovie(:,:,k) + double(moviedata(:,:,j)); attempts to add a 128x128x1 matrix with a 128x128x0 matrix and thus the error you got.

Matlab is opening a pgm image with different values using imread

I have a pgm image with 1251 different pixel values, ranging from 0 to 1250. I know this because I can open this image file with kate and see the values.
But when I open the same file using Matlab's imread, it also returns me 1251 different pixel values, but these values are not consecutive. The minimum value is 0 and the maximum value is 65483.
I want to iterate through these values in a for loop so I need to read the original and consecutive values as they exist in the file. How to do that in Matlab?
EDIT: That's the image if someone wants to try.
image
The values are scaled so that when you view the image it's not mostly black.
I tested that the scaling works with straight integer truncation by checking that:
[A] = imread( 'myfile.pgm', 'pgm' );
p = sort(unique(A(:));
q = uint16((0:1250) * 65535 / 1251)';
all(p == q) % returns 1
So, you can restore the image like this:
map = arrayfun( #(x) uint16(x * 1251 / 65536), 0:65535 );
B = arrayfun( #(x) map(x+1), A );

Different function returns from command line and within function

I have an extremely bizzare situation: I have a function in MATLAB which calls three other main functions and produces two figures for me. The function reads in an input jpeg image, crops it, segments it using kmeans clustering, and outputs 2 figures to the screen - the original image and the clustered image with the cluster centers indicated. Here is the function in MATLAB:
function [textured_avg_x photo_avg_x] = process_database_images()
clear all
warning off %#ok
type_num_max = 3; % type is 1='texture', 2='graph', or 3='photo'
type_num_max = 1;
img_max_num_photo = 100; % 400 photo images
img_max_num_other = 100; % 100 textured, and graph images
for type_num = 1:2:type_num_max
if(type_num == 3)
img_num_max = img_max_num_photo;
else
img_num_max = img_max_num_other;
end
img_num_max = 1;
for img_num = 1:img_num_max
[type img] = load_image(type_num, img_num);
%img = imread('..\images\445.jpg');
img = crop_image(img);
[IDX k block_bounds features] = segment_image(img);
end
end
end
The function segment_image first shows me the color image that was passed in, performs kmeans clustering, and outputs the clustered image. When I run this function on a particular image, I get 3 clusters (which is not what I expect to get).
When I run the following commands from the MATLAB command prompt:
>> img = imread('..\images\texture\1.jpg');
>> img = crop_image(img);
>> segment_image(img);
then the first image that is displayed by segment_image is the same as when I run the function (so I know that the clustering is done on the same image) but the number of clusters is 16 (which is what I expect).
In fact, when I run my process_database_images() function on my entire image database, EVERY image is evaluated to have 3 clusters (this is a problem), whereas when I test some images individually, I get in the range of 12-16 clusters, which is what I prefer and expect.
Why is there such a discrepancy? Am I having some syntax bug in my process_database_images() function? If more code is required from me (i.e. segment_images function, or crop_image function), please let me know.
Thanks.
EDIT:
I found the source of the problem. In my load_image function, after I call img = imread(filename), I convert the image to double: `img = im2double(img);'. When I comment this line out, I get the desired result. Anyone know why this happens? (and also how I can 'close' this question since I have located the problem).
clear all at the top of your function is unnecessary and may be the source of your trouble.
Also, turning off all warnings is a bad idea since it may mask other problems.
Let's look at this code, simplified by removing redundant code or unused code:
function [textured_avg_x photo_avg_x] = process_database_images()
type_num_max = 1;
img_max_num_photo = 100; % 400 photo images
img_max_num_other = 100; % 100 textured, and graph images
for type_num = 1:2:type_num_max %% 1:2:1 => 1
img_num_max = 1; %This nullfiies everything in the if block above anyways
for img_num = 1:img_num_max %% 1:1 => 1
[type img] = load_image(type_num, img_num); %% Input (1,1)
img = crop_image(img);
[IDX k block_bounds features] = segment_image(img);
end
end
end
It looks like this code runs through the double nested for loop exactly once, maybe that is why you get only one answer, three clusters.
Try calling your function on the command line with the same amount of return values as in the function you wrote. Instead of
>> segment_image(img);
Try:
>> [IDX k block_bounds features] = segment_image(img);
Functions in Matlab check how many return values are expected, and may behave differently depending on that.

How do I write an image to a file

I am particularly stuck in this case
I = imread('liftingbody.png');
S = qtdecomp(I,.27);
blocks = repmat(uint8(0),size(S));
for dim = [512 256 128 64 32 16 8 4 2 1];
numblocks = length(find(S==dim));
if (numblocks > 0)
values = repmat(uint8(1),[dim dim numblocks]);
values(2:dim,2:dim,:) = 0;
blocks = qtsetblk(blocks,S,dim,values);
end
end
blocks(end,1:end) = 1;
blocks(1:end,end) = 1;
imshow(I), figure, imshow(blocks,[])
( The example above is from the MATLAB help )
If I try to write the image i.e blocks using imwrite(blocks) then the whole image appears to be black. This happens for any input images. But I want to write exactly the output that imshow shows here. Can anyone help ?
You created blocks as a uint8 matrix. By convention, MATLAB and Image Processing Toolbox treat a uint8 grayscale as having a range of values from 0 to 255. That is, 0 is black and 255 is white. So your blocks matrix, which contains only 0s and 1s, would normally be displayed as black and almost-black.
When you displayed blocks using:
imshow(blocks,[])
You used the "auto-ranging" syntax of imshow, which displays the minimum value of blocks as black and the maximum value of blocks as white.
But then when you saved blocks using imwrite, it made the normal assumption of 0 as black and 255 as white.
Try initializing blocks as a logical matrix instead, like this:
blocks = logical(size(S));
MATLAB and Image Processing Toolbox treat a logical matrix as a binary image and will display 0 as black and 1 and white. If you pass a logical matrix to imwrite, it will create a 1-bit-depth binary image file.
a generic answer is to normalize the image to be in the range for imwrite(blocks):
imwrite((blocks-min(blocks))/(max(blocks)-min(blocks)))