Matlab image filtering without using conv2 - image

I've been given a task to create image filtering function for 3x3 matrices, and its outcome must be equal to conv2's. I have written this function, but it filters image incorrectly:
function [ image ] = Func134( img,matrix )
image=img;
len=length(img)
for i=2:1:len-1
for j=2:1:len-1
value=0;
for g=-1:1:1
for l=-1:1:1
value=value+img(i+g,j+l)*matrix(g+2,l+2);
end
end
image(i,j)=value;
end
end
i=1:1:length
image(i,1)=image(i,2)
image(i,len)=image(i,len-1)
image(1,i)=image(2,i)
image(len,i)=image(len-1,i)
end
Filtration matrix is [3,10,3;0,0,0;-3,-10,-3]
Please help to figure out what is wrong with my code.
Some sample results I get between conv2 and my code are seen below.

First off, this line doesn't make sense:
i=1:1:length;
I think you meant to use len instead of length as the ending index:
i=1:1:len;
Now referring to your code, it is correct, but what you are doing is correlation not convolution. In 2D convolution, you have to perform a 180 degree rotation of the kernel / mask and then do the weighted sum. As such, if you want to achieve the same results using conv2, you must pre-rotate the mask before calling it.
mask = [3,10,3;0,0,0;-3,-10,-3]
mask_flip = mask(end:-1:1,end:-1:1);
out = conv2(img, mask, 'same');
mask_flip contains the 180 degree rotated kernel. We use the 'same' flag to ensure that the output size of the result is the same size as the input. However, when using conv2, we are assuming that the borders of the image are zero-padded. Your code simply copies the border pixels of the original image into the resulting image. This is known as replicating behaviour but that is not what conv2 does natively. conv2 assumes that the border pixels are zero-padded as I mentioned before, so what I would suggest you do is create two additional images, one being the output image that has 2 more rows and 2 more columns and another being the input image that is the same size as the output image but you place the input image inside this matrix. Next, perform the filtering on this new image, place the resulting filtered pixels in the output image then crop this result. I've decided to create a new padded input image in order to keep most of your code intact.
I would also recommend that you abolish the use of length here. Use size instead to determine the image dimensions. Something like this will work:
function [ image ] = Func134( 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);
end
To compare, I've generated this random matrix:
>> rng(123);
>> A = rand(10,10)
A =
0.6965 0.3432 0.6344 0.0921 0.6240 0.1206 0.6693 0.0957 0.3188 0.7050
0.2861 0.7290 0.8494 0.4337 0.1156 0.8263 0.5859 0.8853 0.6920 0.9954
0.2269 0.4386 0.7245 0.4309 0.3173 0.6031 0.6249 0.6272 0.5544 0.3559
0.5513 0.0597 0.6110 0.4937 0.4148 0.5451 0.6747 0.7234 0.3890 0.7625
0.7195 0.3980 0.7224 0.4258 0.8663 0.3428 0.8423 0.0161 0.9251 0.5932
0.4231 0.7380 0.3230 0.3123 0.2505 0.3041 0.0832 0.5944 0.8417 0.6917
0.9808 0.1825 0.3618 0.4264 0.4830 0.4170 0.7637 0.5568 0.3574 0.1511
0.6848 0.1755 0.2283 0.8934 0.9856 0.6813 0.2437 0.1590 0.0436 0.3989
0.4809 0.5316 0.2937 0.9442 0.5195 0.8755 0.1942 0.1531 0.3048 0.2409
0.3921 0.5318 0.6310 0.5018 0.6129 0.5104 0.5725 0.6955 0.3982 0.3435
Now running with what we talked about above:
mask = [3,10,3;0,0,0;-3,-10,-3];
mask_flip = mask(end:-1:1,end:-1:1);
B = Func134(A,mask);
C = conv2(A, mask_flip,'same');
We get the following for your function and the output of conv2:
>> B
B =
-5.0485 -10.6972 -11.9826 -7.2322 -4.9363 -10.3681 -10.9944 -12.6870 -12.5618 -12.0295
4.4100 0.1847 -2.2030 -2.7377 0.6031 -3.7711 -2.5978 -5.8890 -2.9036 2.7836
-0.6436 6.6134 4.2122 -0.7822 -2.3282 1.6488 0.4420 2.2619 4.2144 3.2372
-4.8046 -1.0665 0.1568 -1.5907 -4.6943 0.3036 0.4399 4.3466 -2.5859 -3.4849
-0.7529 -5.5344 1.3900 3.1715 2.9108 4.6771 7.0247 1.7062 -3.9277 -0.6497
-1.9663 2.4536 4.2516 2.2266 3.6084 0.6432 -1.0581 -3.4674 5.3815 6.1237
-0.9296 5.1244 0.8912 -7.7325 -10.2260 -6.4585 -1.4298 6.2675 10.1657 5.3225
3.9511 -1.7869 -1.9199 -5.0832 -3.2932 -2.9853 5.5304 5.9034 1.4683 -0.7394
1.8580 -3.8938 -3.9216 3.8254 5.4139 1.8404 -4.3850 -7.4159 -4.9894 -0.5096
6.4040 7.6395 7.3643 11.8812 10.6537 10.8957 5.0278 3.0277 4.2295 3.3229
>> C
C =
-5.0485 -10.6972 -11.9826 -7.2322 -4.9363 -10.3681 -10.9944 -12.6870 -12.5618 -12.0295
4.4100 0.1847 -2.2030 -2.7377 0.6031 -3.7711 -2.5978 -5.8890 -2.9036 2.7836
-0.6436 6.6134 4.2122 -0.7822 -2.3282 1.6488 0.4420 2.2619 4.2144 3.2372
-4.8046 -1.0665 0.1568 -1.5907 -4.6943 0.3036 0.4399 4.3466 -2.5859 -3.4849
-0.7529 -5.5344 1.3900 3.1715 2.9108 4.6771 7.0247 1.7062 -3.9277 -0.6497
-1.9663 2.4536 4.2516 2.2266 3.6084 0.6432 -1.0581 -3.4674 5.3815 6.1237
-0.9296 5.1244 0.8912 -7.7325 -10.2260 -6.4585 -1.4298 6.2675 10.1657 5.3225
3.9511 -1.7869 -1.9199 -5.0832 -3.2932 -2.9853 5.5304 5.9034 1.4683 -0.7394
1.8580 -3.8938 -3.9216 3.8254 5.4139 1.8404 -4.3850 -7.4159 -4.9894 -0.5096
6.4040 7.6395 7.3643 11.8812 10.6537 10.8957 5.0278 3.0277 4.2295 3.3229

Related

Divide an image into non-overlapping blocks and applying the 2D DWT on each block

I am working on creating an image splicing detection software so I need to divide the image into non-overlapping blocsk and apply Discrete Meyer Wavelet Transform on each block of the image
I have tried the blockproc function to do that but I got no result:
I = imread('pears.png');
fun = #(block_struct)...
dwt2(block_struct.data,'dmey');
C = blockproc(I,[64 64],fun);
So how can I access the [cA,cH,cV,cD] of dwt2 using the above code?
blockproc assumes that you are outputting an actual image. You cannot use this for multiple outputs. If you truly want this to work with blockproc, you will unfortunately need to call blockproc four times, with each time extracting the different set of coefficients for the directions. Also note that the 2D DWT only works for grayscale images, so you need to convert to grayscale before actually doing any processing. The pears image you've chosen is a colour / RGB image.
I'd like to reference this post on how to select the Nth output given an input function: How do I get the second return value from a function without using temporary variables?. You will need to save this code to a file called nth_output.m, which allows you to programatically extract all output variables from a function and choose only one output.
function value = nth_output(N,fcn,varargin)
[value{1:N}] = fcn(varargin{:});
value = value{N};
end
Simply omitting the extra output arguments when you call the function only gives you the first output, which is what your blockproc code is doing. Once you do that, it's a matter of creating 4 anonymous functions to capture each output from dwt2, and running blockproc 4 times. Make sure you specify which output you want for each of the anonymous functions, so 1 up to 4 and you simply provide a handle to the function you want to run in addition to the input arguments that go into the function.
Therefore, try something like this:
I = rgb2gray(imread('pears.png'));
fun1 = #(block_struct) nth_output(1, #dwt2, block_struct.data,'dmey');
fun2 = #(block_struct) nth_output(2, #dwt2, block_struct.data,'dmey');
fun3 = #(block_struct) nth_output(3, #dwt2, block_struct.data,'dmey');
fun4 = #(block_struct) nth_output(4, #dwt2, block_struct.data,'dmey');
I = rgb2gray(I);
cA = blockproc(I,[64 64],fun1);
cH = blockproc(I,[64 64],fun2);
cV = blockproc(I,[64 64],fun3);
cD = blockproc(I,[64 64],fun4);
cA, cH, cV, and cD contain the DWT coefficients you need for each set of directions.

Issue with averaging images in matlab

when I try to average a folder of jpeg matlab images, all that I get is a blank image. I've gone over my code a million times, and I don't know where I'm
going wrong. (also I know I hard coded some of the numbers but that just because I wanted it to take a specific folder and I've double checked those a million times, they're right.)
%takes all the images in a folder and averages their values
%opens folder
function avg_image = average_images()
folder_name = uigetdir;
folder_directory = dir(folder_name);
filename = folder_directory(3).name;
len = length(folder_directory);
org_image = imread(filename);
sum_image = org_image;
%adds files together
for i = 4:len
filename = folder_directory(i).name;
org_image = imread(filename);
sum_image = sum_image + org_image;
end
%calculates average
avg_image = sum_image/(len-2);
%saves average as a fits file and displays it
imwrite(avg_image, 'averagefile.jpg');
read_image = imread('averagefile.jpg');
imshow(read_image)
end
The problem with your code is that you are reading in the JPGs as uint8 (default) and then doing math with the images as matrices of uint8's (0-255 integers). As you read in org_image, above and inside the for loop, cast the result as a double:org_image = double(imread(filename)). After you're done with the averaging, you need to cast it back, avg_image = uint8(sum_image/(len-2)).
When you do math with uint8's, divisions are messy since decimals are truncated. 4 divided by 8 when both are doubles gives you 0.5. When both are integers, you get 0.

Using MATLAB to save a montage of many images as one large image file at full resolution

I am trying to save a montage of many (~500, 2MB each) images using MATLAB function imwrite, however I keep getting this error:
Error using imwrite>validateSizes (line 632)
Images must contain fewer than 2^32 - 1 bytes of data.
Error in imwrite (line 463)
validateSizes(data);
here is the code I am working with:
close all
clear all
clc
tic
file = 'ImageRegistrations.txt';
info = importdata(file);
ImageNames = info.textdata(:,1);
xoffset = info.data(:,1);
yoffset = info.data(:,2);
for i = 1:length(ImageNames);
ImageNames{i,1} = imread(ImageNames{i,1});
ImageNames{i,1} = flipud(ImageNames{i,1});
end
ImageNames = flipud(ImageNames);
for i=1:length(ImageNames)
diffx(i) = xoffset(length(ImageNames),1) - xoffset(i,1);
end
diffx = (diffx)';
diffx = flipud(diffx);
for j=1:length(ImageNames)
diffy(j) = yoffset(length(ImageNames),1) - yoffset(j,1);
end
diffy = (diffy)';
diffy = flipud(diffy);
matrix = zeros(max(diffy)+abs(min(diffy))+(2*1004),max(diffx)+abs(min(diffx))+(2*1002));
%matrix(1:size(ImageNames{1,1},1),1:size(ImageNames{1,1},2)) = ImageNames{1,1};
for q=1:length(ImageNames)
matrix((diffy(q)+abs(min(diffy))+1):(diffy(q)+abs(min(diffy))+size(ImageNames{q,1},1)),(diffx(q)+abs(min(diffx))+1):((diffx(q)+abs(min(diffx))+size(ImageNames{q,1},2)))) = ImageNames{q,1};
end
graymatrix = mat2gray(matrix);
graymatrix = flipud(graymatrix);
figure(2)
imshow(graymatrix)
imwrite(graymatrix, 'montage.tif')
toc
I use imwrite because it perserves the final montage in a full resolution file, whereas if I simply click save on the figure file it saves it as a low resolution file.
thanks!
Error does what it says on the tin, really. There is some sort of inbuilt limitation to input variable size in imwrite, and you're going over it.
Note that most images are stored as uint8 but I would guess that you end up with doubles as a result of your processing. That increases the memory usage.
It may be, therefore, that casting to another type would help. Try using im2uint8 (presuming your variable graymatrix is double, scaled between 0 and 1), before calling imwrite.

Creating Image stacks and writing GDF file

I am attempting to write a function that stack up series of images into image stack and converting it into a gdf file. I don't really know much about GDF files, so please help me out.
X=[];
for i=1:10
if numel(num2str(i))==1
X{i}=imread(strcat('0000',num2str(i),'.tif'));
elseif numel(num2str(i))==2
X{i}=imread(strcat('000',num2str(i),'.tif'));
end
end
myImage=cat(3,X{1:10});
s=write_gdf('stack.gdf',myImage);
Above is to read my images labeled 00001 to 00010, all in grayscale. Everything is fine except in the last line
s=write_gdf('stack.gdf',myImage);
as when I run it, I receive an error:
Data type uint8 not supported
Any help on what this means? Should I convert it to some other colour format?
Thank you in advance!
I would write the code rather like this (I do not have write_gdf function so I can not properly test the code):
NumberOfFiles = 10;
X={}; % preallocate CELL array
for n=1:NumberOfFiles % do not use "i" as your varable because it is imaginary unit in MatLab
FileName = sprintf('%05d.tif',n);
img = imread(FileName); % load image
X{i} = double(img); % and convert to desired format
end
myImage = cat(3,X{1:NumberOfFiles});
s = write_gdf('stack.gdf',myImage);
Keep in mind that
double(img); % and convert to desired format
will not change data range. Your image even in double format will have data range from 0 to 255 if it was in uint8 format on disk. If you need to normalize your data to 0..1 range you should do
X{i} = double(img)/255;
or in more unversal form
X{i} = double(img) / intmax(class(img));

RGB to norm rgb transformation. Vectorizing

I'm writing a piece of code that has to transform from an RGB image to an rgb normalized space. I've got it working with a for format but it runs too slow and I need to evaluate lots of images. I'm trying to vectorize the full function in order to faster it. What I have for the moment is the following:
R = im(:,:,1);
G = im(:,:,2);
B = im(:,:,3);
r=reshape(R,[],1);
g=reshape(G,[],1);
b=reshape(B,[],1);
clear R G B;
VNormalizedRed = r(:)/(r(:)+g(:)+b(:));
VNormalizedGreen = g(:)/(r(:)+g(:)+b(:));
VNormalizedBlue = b(:)/(r(:)+g(:)+b(:));
NormalizedRed = reshape(VNormalizedRed,height,width);
NormalizedGreen = reshape(VNormalizedGreen,height,width);
NormalizedBlue = reshape(VNormalizedBlue,height,width);
The main problem is that when it arrives at VNormalizedRed = r(:)/(r(:)+g(:)+b(:)); it displays an out of memory error (wich is really strange because i just have freed three vectors of the same size). Were is the error? (solved)
Its possible to do the same process in a more efficiently way?
Edit:
After using Martin sugestions I found the reshape function was not necessary, being able to do the same with a simple code:
R = im(:,:,1);
G = im(:,:,2);
B = im(:,:,3);
NormalizedRed = R(:,:)./sqrt(R(:,:).^2+G(:,:).^2+B(:,:).^2);
NormalizedGreen = G(:,:)./sqrt(R(:,:).^2+G(:,:).^2+B(:,:).^2);
NormalizedBlue = B(:,:)./sqrt(R(:,:).^2+G(:,:).^2+B(:,:).^2);
norm(:,:,1) = NormalizedRed(:,:);
norm(:,:,2) = NormalizedGreen(:,:);
norm(:,:,3) = NormalizedBlue(:,:);
I believe you want
VNormalizedRed = r(:)./(r(:)+g(:)+b(:));
Note the dot in front of the /, which specifies an element-by-element divide. Without the dot, you're solving a system of equations -- which is likely not what you want to do. This probably also explains why you're seeing the high memory consumption.
Your entire first code can be rewritten in one vectorized line:
im_normalized = bsxfun(#rdivide, im, sum(im,3,'native'));
Your second slightly modified version as:
im_normalized = bsxfun(#rdivide, im, sqrt(sum(im.^2,3,'native')));
BTW, you should be aware of the data type used for the image, otherwise one can get unexpected results (due to integer division for example). Therefore I would convert the image to double before performing the normalization calculations:
im = im2double(im);

Resources