Inexplicable results after using ind2sub in Matlab - image

I am having some problems in matlab i don't understand. The following piece of code analyses a collection of images, and should return a coherent image (and always did).
But since I've put an if-condition in the second for-loop (for optimisation purposes) it returns an interlaced image.
I don't understand why, and am getting ready to throw my computer out the window. I suspect it has something to do with ind2sub, but as far as i can see everything is working just fine! Does anyone know why it's doing this?
function imageMedoid(imageList, resizeFolder, outputFolder, x, y)
% local variables
medoidImage = zeros([1, y*x, 3]);
alphaImage = zeros([y x]);
medoidContainer = zeros([y*x, length(imageList), 3]);
% loop through all images in the resizeFolder
for i=1:length(imageList)
% get filename and load image and alpha channel
fname = imageList(i).name;
[container, ~, alpha] = imread([resizeFolder fname]);
% convert alpha channel to zeros and ones, add to alphaImage
alphaImage = alphaImage + (double(alpha) / 255);
% add (r,g,b) values to medoidContainer and reshape to single line
medoidContainer(:, i, :) = reshape(im2double(container), [y*x 3]);
end
% loop through every pixel
for i=1:(y * x)
% convert i to coordinates for alphaImage
[xCoord, yCoord] = ind2sub([x y],i);
if alphaImage(yCoord, xCoord) == 0
% write default value to medoidImage if alpha is zero
medoidImage(1, i, 1:3) = 0;
else
% calculate distances between all values for current pixel
distances = pdist(squeeze(medoidContainer(i,:,1:3)));
% convert found distances to matrix of distances
distanceMatrix = squareform(distances);
% find index of image with the medoid value
[~, j] = min(mean(distanceMatrix,2));
% write found medoid value to medoidImage
medoidImage(1, i, 1:3) = medoidContainer(i, j, 1:3);
end
end
% replace values larger than one (in alpha channel) by one
alphaImage(alphaImage > 1) = 1;
% reshape image to original proportions
medoidImage = reshape(medoidImage, y, x, 3);
% save medoid image
imwrite(medoidImage, [outputFolder 'medoid_modified.png'], 'Alpha', alphaImage);
end
I didn't include the whole code, just this function (for brevity's sake), if anyone needs more (for a better understanding of it), please let me know and i'll include it.

When you call ind2sub, you give the size [x y], but the actual size of alphaImage is [y x] so you are not indexing the correct location with xCoord and yCoord.

Related

Inpaint an image matlab [duplicate]

This question already has an answer here:
Efficient inpaint with neighbouring pixels
(1 answer)
Closed 6 years ago.
I am trying to replace all pixels with certain value in an image with the average values of the neighbors. Can interp2 be useful here? I tried this -
I = imread('test_image.JPG');
[r c] = size(I);
class_of_image = class(I);
[xi,yi] = meshgrid(1:0.5:c,1:0.5:r);
I1 = cast(interp2(double(image),xi,yi,'linear'),class_of_image);
[x_z,y_z] = find(I1==0);
I1(x_z,y_z) = I1(x_z-1,y_z)+I1(x_z+1,y_z)+I1(x_z,y_z-1)+I1(x_z,y_z+1);
This fails spectacularly with an error message - Index exceeds matrix dimensions.
I realize that the error is in trying to access I1 indices beyond r and c. Is there a generic way to incorporate this in the code?
Please help!
If you are trying to replace pixels in an image that are at a certain value to be the average of its 4 neighbours, then you don't have to use interp2. It looks like you are doubling the size of the image and then sampling from that image when you're done.
If you want to do what you're asking, you need to use column-major indices to facilitate the vectorized access of pixels. Specifically, you need to use sub2ind to help determine the locations you need to access in your matrix.
However, you will need to account for pixels that go out of bounds. There are many ways to accommodate this, but what I will implement is known as zero-padding where the border pixels are simply set to 0. I would create a zero-padded image where the top and bottom rows as well as the left and right values are all some sentinel value (like -1), use find on this image to find the coordinates then do the inpainting. Make sure you set the border pixels back to 0 before doing this so that you don't use -1 as part of the inpainting. You would then crop the border pixels of this new image when you're done to obtain the final output image.
Therefore, if you want to perform your "inpainting" try this instead:
% Read in image
I = imread('test_image.JPG');
% Create padded image with border pixels set to -1
Ipad = -ones(size(I) + 2);
% Place image in the middle
Ipad(2:end-1,2:end-1) = I;
% Find zero pixels
[r,c] = find(I == 0);
% Now set border pixels to 0
Ipad(Ipad == -1) = 0;
% Find column major indices for those elements that are 0
% as well as their 4 neighbours
ind = sub2ind(size(I), r, c);
ind_up = sub2ind(size(I), r-1, c);
ind_down = sub2ind(size(I), r+1, c);
ind_left = sub2ind(size(I), r, c-1);
ind_right = sub2ind(size(I), r, c+1);
% Perform the inpainting by averaging
Ipad(ind) = (Ipad(ind_up) + Ipad(ind_down) + Ipad(ind_left) + Ipad(ind_right))/4;
% Store the output in I1 after removing border pixels
I1 = Ipad(2:end-1,2:end-1);
However, a possibly shorter way to do this even though you would operate on the entire image would be to perform 2D convolution using a 3 x 3 kernel whose elements are 1 in the cardinal directions and ensuring that you divide by 4 to find the average value per location. After, you would simply copy over those values in the output that are 0 in the original image. You can use conv2 to do that and make sure you specify the 'same' flag to ensure that the output size is the same as the input size. The behaviour of conv2 when you approach the border elements is to zero-pad, which is what I did already in the first implementation:
% Read in image
I = imread('test_image.JPG');
% Specify kernel
kernel = [0 1 0; 1 0 1; 0 1 0] / 4;
% Perform convolution - make sure you cast image to double
% as convolution in 2D only works for floating-point types
out = conv2(double(I), kernel, 'same');
% Copy over those values from the output that match the value
% to be inpainted for the input. Also cast back to original
% data type.
I1 = I;
I1(I == 0) = cast(out(I == 0), class(I));

How would I implement these equations in Matlab?

I'm trying to implement a paper. In it I need to calculate the centre of gravity and second order moment of an image.
The equations of centre of gravity and second order moment are respectively given as:
Im having trouble trying to code this in Matlab ss from what I understand p(x,y) is the pixel of the image, but I'm having trouble what y represents and how would I implement in in the sum function. This is my implementation of the first equation but since I did not incorporate the y in there I'm sure the result given is wrong.
img = imread(path);
m = numel(img);
cog = sum(img(:))/m;
i think, m should be the maximum of y, because f2 is a function of x which means in Matlab it should be a vector.
try this code to implement f2:
img = magic(10)
m = 10;
temp = 0;
for y = 1:m
temp = temp+y*img(:,y);
%temp = temp+y*img(y,:); % depends on your image coordinates system
end
f2 = temp/m
Try the following code that uses vectorized anonymous functions.
% Read the image into an array (3 dimensions).
% Note: you may need to convert to doubles
img = im2double(imread(path));
% Get the size (may need to switch m and n).
[m, n, o] = size(img);
% Create y vector
y = 1:m;
% Create functions (not sure how you want to handle the RGB values).
f2 = #(x, p) sum(y.*p(x,:,1)/m);
f3 = #(x, p) sum(y.^2.*p(x,:,1)/(m^2));
% Call the functions
x = 10; % Some pixel x position
f2_result = f2(x, img);
f3_result = f3(x, img);
Note: I may have the x and y switched depending on the orientation of your image. If that's the case then switch things around like this:
[n, m, o] = size(img);
...
f2 = #(x, p) sum(y.*p(:,x)/m);
etc...
I'm not at work so I can't run the im2double function (don't have the library) but I think it will work.

Matlab transparent overlay matrix

I want to load an image and then create a Matrix with the size of the image.
The Matrix should be transparent with only some spare values (points).
I then want to show the image in a figure and put the Matrix on top.
The code so far:
world = imread('map1.jpg'); % import image of location
[x_world,y_world] = size(world); % get the size of the image
A = zeros(x_world,y_world); % create matrix with dimension of image
imshow(world); % display image
axis image; % label the axis
My Matrix contains some points:
A(200,300) = 1;
A(500,500) = 5;
A(580,200) = 3;
if I now iterate through each value in the Matrix like that:
for i = 1:x_world
for j = 1:y_world
if(A(i,j) == 1)
plot(i,j,'r.','MarkerSize',20); % plot a single point
elseif(A(i,j) == 2)
plot(i,j,'y.','MarkerSize',20); % plot a single point
elseif(A(i,j) == 3)
plot(i,j,'m.','MarkerSize',20); % plot a single point
elseif(A(i,j) == 4)
plot(i,j,'g.','MarkerSize',20); % plot a single point
elseif(A(i,j) == 5)
plot(i,j,'b.','MarkerSize',20); % plot a single point
elseif(A(i,j) == 6)
plot(i,j,'w.','MarkerSize',20); % plot a single point
end
end
end
it would be really slow.
So what I want to do is create a transparent Matrix and then set some points, so that I just can print the Matrix over the original image.
Is that possible? How do I do that? Is there maybe another better way to do that?
As a start, you could avoid looping over all of theese rows and columns by actually looping over your 6 groups. The code blow should give the same result:
markers = {'r.', 'y.', 'm.', 'g.', 'b.', 'w.'}; % // define some markers
figure
hold on
for group = 1:6
[i, j] = ind2sub(size(A), find(A==group)); % // find indices of current group
plot(i, j, markers{group}, 'markersize', 20) % // plot them with their marker
end
If speed is an issue, you maybe can have a look at gscatter() and or sparse().

Masking an RGB Image with Binary Mask

I have an RGB image (M x N x 3 matrix) in MATLAB that I read in. I also have a Binary mask (M x N matrix) for the image, which is simply 0 for some region of interest and 1 everywhere else.
I'm trying to figure out how to mask the RGB image with that binary mask. I've tried changing datatypes around (working with either double or uint8 to see if results change, but sometimes they don't or I get an error) and I've tried using various functions like conv2, immultiply, imfilter, and so on.
What I currently do is try to apply the mask individually (as it is size M x N) to each R, G, and B channel of the original image. Anywhere that the mask is 0, I wish to make exactly 0 in the original image, while anywhere the mask is 1 I just want to leave alone.
None of those functions above have seemed to work so far. Obviously, the method I know that will work is if I just went through and did a for-loop through all of this, but that would be terrible as MATLAB has these image functions, but I can't seem to get them to work.
Sometimes imfilter or immultiply (depending on how I'm messing around with the images) will just stall and crash MATLAB completely. Sometimes they finish quickly but I either get an all-white image or an all-black image (through both imshow AND imagesc).
I've checked to make sure that my image channels match up in size with the mask, and I've checked the values in both the image and the mask and they're correct. I just can't seem to get the actual masking operation to work.
Any ideas please? Maybe I'm missing something in MATLAB's rules?
Here's a current attempt:
% NOTE: The code may not be "elegant" but I\'ll worry about optimization later.
%
% Setup image and size
image = imread(im);
[numrows, numcols, temp] = size(image); % not used currently
% Request user polygon for ROI
bw = roipoly(image);
% Set up the mask -- it is indeed all 0's and 1's
t = double(imcomplement(bw));
% "Mask" the image
z = double(image); % Double to match up with t as a double
z(:, :, 1) = imfilter(z(:, :, 1), t);
z(:, :, 2) = imfilter(z(:, :, 2), t);
z(:, :, 3) = imfilter(z(:, :, 3), t);
imshow(z); figure; imagesc(z);
=================
EDIT
Found out that the following works:
im_new = im_old .* repmat(mask, [1,1,3]); % if both image and mask are uint8
imshow(im_new);
You are misusing imfilter() there. Imfilter is used for linear filter operations, not for masking or thresholding. Better do this:
z = image; % image() is also a function.
% Overwriting this name should be avoided
% Request user polygon for ROI
bw = roipoly(z);
% Create 3 channel mask
mask_three_chan = repmat(bw, [1, 1, 3]);
% Apply Mask
z(~mask_three_chan) = 0;
% Display
imshow(z);

Plot images as axis labels in MATLAB

I am plotting a 7x7 pixel 'image' in MATLAB, using the imagesc command:
imagesc(conf_matrix, [0 1]);
This represents a confusion matrix, between seven different objects. I have a thumbnail picture of each of the seven objects that I would like to use as the axes tick labels. Is there an easy way to do this?
I don't know an easy way. The axes properties XtickLabel which determines the labels, can only be strings.
If you want a not-so-easy way, you could do something in the spirit of the following non-complete (in the sense of a non-complete solution) code, creating one label:
h = imagesc(rand(7,7));
axh = gca;
figh = gcf;
xticks = get(gca,'xtick');
yticks = get(gca,'ytick');
set(gca,'XTickLabel','');
set(gca,'YTickLabel','');
pos = get(axh,'position'); % position of current axes in parent figure
pic = imread('coins.png');
x = pos(1);
y = pos(2);
dlta = (pos(3)-pos(1)) / length(xticks); % square size in units of parant figure
% create image label
lblAx = axes('parent',figh,'position',[x+dlta/4,y-dlta/2,dlta/2,dlta/2]);
imagesc(pic,'parent',lblAx)
axis(lblAx,'off')
One problem is that the label will have the same colormap of the original image.
#Itmar Katz gives a solution very close to what I want to do, which I've marked as 'accepted'. In the meantime, I made this dirty solution using subplots, which I've given here for completeness. It only works up to a certain size input matrix though, and only displays well when the figure is square.
conf_mat = randn(5);
A = imread('peppers.png');
tick_images = {A, A, A, A, A};
n = length(conf_mat) + 1;
% plotting axis labels at left and top
for i = 1:(n-1)
subplot(n, n, i + 1);
imshow(tick_images{i});
subplot(n, n, i * n + 1);
imshow(tick_images{i});
end
% generating logical array for where the confusion matrix should be
idx = 1:(n*n);
idx(1:n) = 0;
idx(mod(idx, n)==1) = 0;
% plotting the confusion matrix
subplot(n, n, find(idx~=0));
imshow(conf_mat);
axis image
colormap(gray)

Resources