14

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:

enter image description here

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:

enter image description here

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:

  1. Start from the top-left of the block.
  2. We move one space to the right, then downleft diagonally until we hit the image border.
  3. Once we hit an image border in Step #2, we move one space down, then move upright diagonally until we hit the image border.
  4. 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!

rayryeng
  • 102,964
  • 22
  • 184
  • 193
  • 1
    The book, remember to add this there. – Ander Biguri Feb 06 '15 at 15:44
  • @AnderBiguri - Hehe :) – rayryeng Feb 06 '15 at 15:44
  • @Jonas - HUH. What's funny is that I looked on StackOverflow and I couldn't even find something like that. SO really needs to improve their search engine logic. Thanks! – rayryeng Feb 06 '15 at 15:54
  • 1
    @rayryeng: Nevertheless, thanks for a much more detailed and well-formulated question ! – Jonas Feb 06 '15 at 15:58
  • 2
    @Jonas - Thanks :) I spent about half an hour writing it. Kind of sad that it's marked as a duplicate... mainly because it was my fault for not being able to find the correct duplicate, but I'm going to leave this question up as the duplicate question doesn't explain the mechanics of zigzag ordering very well. I think it'll be useful in that aspect! – rayryeng Feb 06 '15 at 16:07
  • 1
    I haven't much of a fan of the search engine used here either! I guess if the keywords are *sorta* unique, you could go through google. So, as an example using this problem case, "zigzag matlab site:stackoverflow.com" would have gotten you there quickly! This was a huge effort to put up all the contents in the question nevertheless!! – Divakar Feb 06 '15 at 16:32
  • 2
    @Divakar - Yeah, Google got me to the dupe in no time. *face palm*. I'll do that next time, but thanks for all of the upvotes to my question nonetheless :) I appreciate it! – rayryeng Feb 06 '15 at 16:35

0 Answers0