Replication of cat-dog example.
The first decision I made was how to pad the image so that it could be filtered and retain its size. The easiest reasonable way to do this was to extend the outer row of pixels out. The way I did this was by concatenating together a 3x3 grid if images. The corners were solid blocks of the respective corner pixel colors, the sides were repeated rows or columns of the edge pixels, and the center is just the original image. It would probably be quicker to just extend in one dimension, and then the other. The result is continuous looking edges on all the images.
% Image padding by edge extension.
s = size(image); % image size
fs = size(filter); % filter size
yPad = int32((fs(1)-1)/2);
xPad = int32((fs(2)-1)/2);
topLeft = repmat(image(1, 1), yPad, xPad);
topMid = repmat(image(1, :), yPad, 1);
topRight = repmat(image(1, end), yPad, xPad);
top = cat(2, topLeft, topMid, topRight);
midLeft = repmat(image(:, 1), 1, xPad);
midRight = repmat(image(:, end), 1, xPad);
mid = cat(2, midLeft, image, midRight);
bottomLeft = repmat(image(end, 1), yPad, xPad);
bottomMid = repmat(image(end, :), yPad, 1);
bottomRight = repmat(image(end, end), yPad, xPad);
bottom = cat(2, bottomLeft, bottomMid, bottomRight);
padded = cat(1, top, mid, bottom);
Fantastic cat padding.
The actual filtering/convolution was straight forward. Loop through each pixel in the filtered image and cut a window out of the padded image starting from that pixel. The filtered image pixel value is the dot product of the window and the filter. In the following code, I commented out the alternative method of iterating through the filter and summing the products. The alternative method was actually faster for small filters, but I stuck with the approach that scaled better.
% Image filtering / convolution.
output = zeros(s);
for y = 1:s(1) % image y
for x = 1:s(2) % image x
% slower for small filters:
window = padded(y:y+fs(1)-1, x:x+fs(2)-1);
output(y, x) = sum(sum(window .* filter));
% slower for large filters
% for fy = 1:fs(1) % filter y
% for fx = 1:fs(2) % filter x
% output(y, x) = output(y, x) + padded(y-fy+fs(1), x-fx+fs(2))*filter(fy, fx);
% end
% end
end
end
Excellent whisker detection.
The low pass filter was simply a filter with a wide gaussian curve. For the high pass filter, I found that a simplified mexican hat curve or Ricker wavelet worked best. The curve was simply the sum of a positive thin gaussian and a negative wider gaussian. The thin gaussian captured the immediate colors of each pixel, while the wide gaussian subtracted away surrounding colors, leaving just the difference, and capturing small features. Another benefit of this filter was that it simultaneously captured some of the color of the image, instead of yielding only gray values.
The low- and high-pass filters were combined with element-wise addition. However, as the high-pass filter was zero mean, it could be reasonably weighted to make the image look more or less like either input image.
% Filter combination.
highWeight = 1;
hybrid_image = low_frequencies+high_frequencies*highWeight;
The resulting images looked a lot like the example ones, except with more continuous edges, so I was satisfied.
A pyramid of cat-dogs.
Location dependent seasons.
Bonus: This one had to be done. Unfortunately hybrid images don't manage color differences too well, so I made this one gray-scale. I'm pretty sure this isn't racist.
Sorry, Bush.