I'm asking this question mainly because it has bothered me for some time and I haven't figured out the most efficient way to implement this. This is mainly for my own curiosity so there is certainly no rush on answering this question, but if I see a solution that is very effective and very efficient, I may award a bounty so there is some incentive!
Main Question
What is the most efficient way in MATLAB that you can think of that takes an image block / matrix and transforms it into a vector that conforms to zigzag ordering? You can assume that the block is square, where the number of rows equals the number of columns.
Some Background
Zigzag ordering originally comes from the JPEG compression standard. The basic steps behind the JPEG compression algorithm can be shown in the following diagram:
Source: Wikipedia
To summarize, for an image, it is split up into 8 x 8 distinct image blocks, the 2D Discrete Cosine Transform (DCT) is taken on each block, then quantization is performed on each block to allow for encoding that minimizes the total entropy of the bit sequence. The most vital stage to this is the entropy encoding stage and is the last part of the algorithm.
Part of the entropy encoding stage involves zigzag ordering - The block is reordered into a 64 element vector in such a way where the most important frequency components from the DCT come first, followed by those components that don't really matter in order to reconstruct the block.
Here is a pictorial representation of zigzag ordering on a 8 x 8 block:
Source: Wikipedia
The blue arrow starts from the top-left or origin of the image. Next, the direction that the arrow moves indicates how the 64 element vector needs to be populated with elements.
Here is the basic algorithm for zigzag with reference to the above figure:
- Start from the top-left of the block.
- We move one space to the right, then downleft diagonally until we hit the image border.
- Once we hit an image border in Step #2, we move one space down, then move upright diagonally until we hit the image border.
- Alternate between Steps #2 and #3 until we reach the bottom-right of the block.
There are a couple of corner cases you need to be aware of:
- Corner case #1 - If we are in Step #2, we hit the image border, and we are at the last column of the block, do not move one space to the right. Move one space down instead.
- Corner case #2 - If we are in Step #3, we hit the image border, and we are at the last row of the block, do not move one space down. Move one space to the right instead.
Once zigzag ordering occurs, any lossless compression algorithm, such as Huffman... and yes, even entropy encoding (as what this stage is named after) is performed on this reordered data. These 64 element vectors are streamed to the compression algorithm and you thus have your compressed image.
Back to the Problem Description
You're probably seeing my dilemma now. Because of the corner cases, I have not found an efficient way to do this without trying to do this with loops. After doing some initial searching on the Internet, I have found a very similar post on Rosetta Code that talks about creating a zig-zag matrix. The goal here is to produce a matrix of size M x M
where given an integer M > 0
, each location in the matrix is enumerated from 0
to M^2 - 1
where the numbers are delineated in the order that the zig-zag ordering is performed and not as a single vector. With some very simple modifications, you can solve the above problem, but I was wondering if there was any way to do this more efficient than loops. If you check out the code, it's very unintuitive and doesn't make much sense unless you start displaying steps in the Command Window. Even when this is the case, it's still pretty tough to understand, and I daresay obfuscated.
For self-containment, this is the "reference" code that implements zig-zag ordering with loops. This was pulled off of Rosetta Code, but I modified it so that it conforms with the proposed problem. This takes in a 2D square matrix blk
, and out
is a vector that produces the elements taken from the square matrix in zigzag ordering:
function out = zigzag(blk)
%This is very unintiutive. This algorithm parameterizes the
%zig-zagging movement along the matrix indicies. The easiest way to see
%what this algorithm does is to go through line-by-line and write out
%what the algorithm does on a peace of paper.
[m,n] = size(blk);
if m ~= n
error('Not square');
end
out = zeros(numel(blk), 1);
flipCol = true;
flipRow = false;
out(1) = blk(1);
counter = 2;
%This for loop does the top-diagonal of the matrix
for i = (2:n)
row = (1:i);
column = (1:i);
%Causes the zig-zagging. Without these conditionals,
%you would end up with a diagonal matrix.
%To see what happens, comment these conditionals out.
if flipCol
column = fliplr(column);
flipRow = true;
flipCol = false;
elseif flipRow
row = fliplr(row);
flipRow = false;
flipCol = true;
end
%Selects a diagonal of the zig-zag matrix and places the
%correct integer value in each index along that diagonal
for j = (1:numel(row))
out(counter) = blk(row(j),column(j));
counter = counter + 1;
end
end
%This for loop does the bottom-diagonal of the matrix
for i = (2:n)
row = (i:n);
column = (i:n);
%Causes the zig-zagging. Without these conditionals,
%you would end up with a diagonal matrix.
%To see what happens comment these conditionals out.
if flipCol
column = fliplr(column);
flipRow = true;
flipCol = false;
elseif flipRow
row = fliplr(row);
flipRow = false;
flipCol = true;
end
%Selects a diagonal of the zig-zag matrix and places the
%correct integer value in each index along that diagonal
for j = (1:numel(row))
out(counter) = blk(row(j),column(j));
counter = counter + 1;
end
end
end
As such, this is a call to the MATLAB SO community to see if there is a more simple implementation, taking advantage of MATLAB's vectorized and indexing constructs.
Thanks for your time for reading, and looking forward to your answers!