For this project, there were two tasks. The first was to implement a convolution-like image filter which replicated MATLAB's built-in imfilter function. The second was to use image filtering to create hybrid images which yield different percieved images when viewed from different distances.
Image filtering is, fundamentally, evaluating a matrix for the neighborhood of every pixel in the original image. A problem arises when trying to evaluate the matrix at the corners, as the neighborhood defined by the filter requires some information about pixels outside the image boundary, and pixels outside the image boundary are undefined. So image filtering must happen in two steps. First, the boundary of the image must be somehow padded with extra information so that every pixel in the actual image has a valid neighborhood. Then the filter must actually be appled. You can see both steps in the below code, which filters one color channel of an image.
function out = filter_channel(c_image)
filter_size = size(filter);
% pad edges
pad = floor(filter_size/2);
padded = padarray(c_image,pad,'symmetric');
%stuff into columns, then multiply by the filter
to_filter = im2col(padded,filter_size,'sliding');
filter_vec = reshape(filter,1,numel(filter));
filtered = filter_vec*to_filter;
%reshape back into a picture, padding's cut out for you
out = col2im(filtered,[1 1],size(c_image));
end
First, I find the size of the margin I have to pad around the image by finding the half-size of the filter. Then I use the built-in padarray function to pad the array (symmetrically, because it looks better). I took the advice on the site and used the im2col and col2im built-in functions to make my life a lot easier. Using im2col reshapes my padded image so that I have a column representing pixel intensities for each valid neighborhood of every pixel in the original image. This means I can similarly reshape my filter into a row vector and use matrix multiplication to evaluate the filter at every neighborhood (as evaluating the filter is just taking element-wise products and summing). Explicit looping is avoided entirely and I don't even have to de-pad the image because the matrix only has columns for pixels present in the original image. Finally, col2im reverses the reshaping operation and I return the resulting rectangular image.
![]() ![]() ![]() |
![]() ![]() ![]() |
Above, you can see examples of my filtering algorithm applying different fiters to an image of an airplane. From top-left to bottom-right: original image (identity filter), small blur (averaging), large blur (Gaussian), high pass (image minus a Gaussian-filtered version of itself), Laplacian (outlines) and Sobel (edges).
An interesting application of image filtering is to use it to generate hybrid images which can appear as two different pictures depending on where you look at them. The reason this works is because from a short distance, it's easier to see high-frequency components as you're able to see finer detail, while from a long distance, the low-frequency components of the image dominate, because you can see less detail. Below, you can see an example of this in action, as a picture that looks like a fish when viewed close up becomes a submarine when shrunk.
To produce an image like the above example, we must filter the two constituent images then sum them together. For the image we want to have the viewer percieve from a distance, we use a low-pass filter like a Gaussian, which yields a blurry image. For the image we want to have the viewer percieve up-close, we use a high-pass filter (which is really an image from which we subtract a low-pass filtered version of itself). Below you can see the original and filtered versions of the images used to construct the above hybrid.
![]() ![]() |
![]() ![]() |