0

For quick reference here is a github link.

I am attempting to implement a simple JPEG compression. I will provide some of the more notable methods.

The issue is that I am not seeing any notable runs of 0 so my RLE encoding does nothing to compress the image.

Code:

RGB to YCbCr conversion:

private static Ycbcr AlternativeRgbtoYCbCr ( Rgb rgb ) {
    var y = 16f + (65.481f*rgb.R + 128.553f*rgb.G + 24.966f*rgb.B);
    var cr = 128f + (-37.797f * rgb.R - 74.203f * rgb.G + 112.0f * rgb.B);
    var cb = 128f + (112.0f*rgb.R - 93.786f*rgb.G - 18.214f*rgb.B);

    return new Ycbcr(y, cb, cr);
}

Splitting into ColorSpaces:

/// <summary>
///     Loops through all the pixels and converts them to ycbcr
/// </summary>
public void SplitBytesIntoColorSpaces() {
    if (_imageIsSplit) return;
    for (var x = 0; x < LeftImageBitmap.Width; x++) {
        //var innerlist = new List<Ycbcr>();
        var innerY = new List<float>();
        var innerCr = new List<float>();
        var innerCb = new List<float>();
        for (var y = 0; y < LeftImageBitmap.Height; y++) {
            var color = ToRgb(LeftImageBitmap.GetPixel(x, y));
            //innerlist.Add(RgbtoYCbCr(color));
            innerY.Add(AlternativeRgbtoYCbCr(color).Y);
            innerCr.Add(AlternativeRgbtoYCbCr(color).Cr);
            innerCb.Add(AlternativeRgbtoYCbCr(color).Cb);
        }
        //ChromeList.Add(innerlist);
        LumList.Add(innerY);
        CrList.Add(innerCb);
        CbList.Add(innerCr);
    }
    _imageIsSplit = true;
}

Sub-Sampling:

/// <summary>
///     4:2:0 subsampling
/// </summary>
private void SubSample420() {
    var tempCrArray = new List<List<float>>(CrList.Count/2);
    var tempCbArray = new List<List<float>>(CbList.Count/2);

    for (var x = 0; x < CrList.Count/2; x++) {
        var rowCr = new List<float>();
        var rowCb = new List<float>();
        for (var y = 0; y < CrList.Count/2; y++) {
            rowCb.Add(0);
            rowCr.Add(0);
        }
        tempCrArray.Add(rowCr);
        tempCbArray.Add(rowCb);
    }


    for (int x = 0, x2 = 0; x < CrList.Count; x += 2, x2 ++) {
        if (x2 >= tempCrArray.Count) continue;
        var crrow = tempCrArray[x2];
        var cbrow = tempCbArray[x2];
        for (int y = 0, y2 = 0; y < CrList[x].Count; y += 2, y2++) {
            if (y2 >= crrow.Count) continue;
            crrow[y2] = CrList[x][y];
            cbrow[y2] = CbList[x][y];
        }
    }

    CrList = new List<List<float>>(tempCbArray);
    CbList = new List<List<float>>(tempCrArray);

    _subSampled420 = true;
}

Now for dct processing:

Step function to step through arrays 8x8 at a time.

/// <summary>
///     Steps through a 2d array 8 pixels at a time.
///     When x reaches the end, x is set to 0 and y
///     is incremented by 8.
/// </summary>
/// <param name="x">ref position to the loop's x</param>
/// <param name="y">ref position to the loop's y</param>
/// <param name="xl">maximum x position</param>
/// <param name="yl">maximum y position</param>
private static void Step(ref int x, ref int y, int xl, int yl) {
    if (x + 8 < xl) {
        x += 8;
    }
    else if (x + 8 >= xl) {
        x = 0;
        y += 8;
    }
}

DCT:

//DCT function that creates a task and runs dct on it
public double[,] Go(double[,] d) {
    _data = d;
    var task = Task<double[,]>.Factory.StartNew(() => {
        var output = new double[8, 8];
        for (var x = 0; x < _data.GetLength(0); x++)
            for (var y = 0; y < _data.GetLength(1); y++) {
                output[x, y] = GetValueForward(x, y);
            }
        return output;
    });
    return task.Result;
}
//gets a singular value for dct
private double GetValueForward(int u, int v) {
    double freq = 0;
    for (var i = 0; i < 8; i++) {
        for (var j = 0; j < 8; j++) {
            freq +=
                Math.Cos((2*i + 1)*u*Pi/16)*
                Math.Cos((2*j + 1)*v*Pi/16)*
                _data[i, j];
        }
    }

    freq *= 2*C(u)*C(v)/Math.Sqrt(8*8);
    return freq;
}

Function that does all of the processing:

/// <summary>
///     DctImageProcessor's main method.
///     This method will run on 8x8 chunks
///     and process them into double arrays by channel.
/// </summary>
public void Process() {
    //get data manager's ycbcr chromelist
    _luminanceDatalist = Manager.LumList;
    _crDatalist = Manager.CrList;
    _cbDatalist = Manager.CbList;
    _lumOutList = new List<double[,]>();
    _cbOutList = new List<double[,]>();
    _crOutList = new List<double[,]>();
    var lumx = _luminanceDatalist[0].Count;
    var lumy = _luminanceDatalist.Count;

    var channelx = _crDatalist[0].Count;
    var channely = _crDatalist.Count;

    //SetArray(_luminanceDatalist);

    //dct
    var dct = new Dct();

    //quantizer
    var q = new Quantizer();

    //loop and Step() + store values into _lumOutList
    for (int i = 0, j = 0; i < lumx && j < lumy; Step(ref i, ref j, lumx, lumy)) {
        var lumarr = ForDctLum(i, j);
        var lumdct = dct.Go(lumarr);
        q.QuantizeLuminance(ref lumdct);
        _lumOutList.Add(lumdct);
    }

    for ( int i = 0, j = 0; i < channelx && j < channely; Step(ref i, ref j, channelx, channely) ) {
        var crArr = ForDctCr(i, j);
        var cbArr = ForDctCb(i, j);
        var crdct = dct.Go(crArr);
        var cbdct = dct.Go(cbArr);

        q.QuantizeChrominance(ref crdct);
        q.QuantizeChrominance(ref cbdct);

        _crOutList.Add(crdct);
        _cbOutList.Add(cbdct);
    }

    //rle
    var rleOutputs = _lumOutList.Select(Rle.ZigZag).ToList();
    rleOutputs.AddRange(_crOutList.Select(Rle.ZigZag));
    rleOutputs.AddRange(_cbOutList.Select(Rle.ZigZag));

    var encoded = Rle.Encode(rleOutputs);

    File.WriteAllBytes("./output.dct", encoded);
}

RLE: Zigzag reading:

public static byte[] ZigZag(double[,] input) {
    var result = new double[8, 8];
    var output = new byte[64];
    int i = 0, j = 0;
    var d = -1; // -1 for top-right move, +1 for bottom-left move
    int start = 0, end = 8*8 - 1;
    do {
        output[start++] = (byte) input[i, j];
        output[end--] = (byte) input[8 - i - 1, 8 - j - 1];
        i += d;
        j -= d;

        if (i < 0) {
            i++;
            d = -d; // top reached, reverse
        }
        else if (j < 0) {
            j++;
            d = -d; // left reached, reverse
        }
    } while (start < end);
    if (start == end)
        result[i, j] = start;
    return output;
}

Run Length Encoding.

public static byte[] Encode(List<byte[]> list) {
    var ret = new List<byte>();
    var prev = list[0][0];
    byte count = 0;
    const byte delim = 255;

    foreach (var val in list.SelectMany(arr => arr)) {
        if (val != prev) {
            if (count > 1) {
                ret.Add(delim);
                ret.Add(count);
                ret.Add(prev);
            }
            else {
                ret.Add(prev);
            }
            prev = val;
            count = 1;
        }
        else {
            count ++;
            prev = val;
        }
    }
    return ret.ToArray();
}

I know this is a pretty long post but I have not been able to get this resolved on my own. The book we are using isn't of much help so I am left to just randomly writing code until something works.

Right now I am able to work with images that can be split evenly into 8x8 blocks like the lena.tif image on github.

The issue comes when I RGB > YCBCR > SubSample > DCT > Quantize At this point my values do not have a run of 0s like they should.

All suggestions appreciated.

Edit:

Quantization Tables:

private double[,] ChrominanceQuantizationMatrix { get; } = new double[8, 8] {
    {17, 18, 24, 47, 99, 99, 99, 99},
    {18, 21, 26, 66, 99, 99, 99, 99},
    {24, 26, 56, 99, 99, 99, 99, 99},
    {47, 66, 99, 99, 99, 99, 99, 99},
    {99, 99, 99, 99, 99, 99, 99, 99},
    {99, 99, 99, 99, 99, 99, 99, 99},
    {99, 99, 99, 99, 99, 99, 99, 99},
    {99, 99, 99, 99, 99, 99, 99, 99}
};

private double[,] LuminanceQuantizationMatrix { get; } = new double[8, 8] {
    {16, 11, 10, 16, 24, 40, 51, 61},
    {12, 12, 14, 19, 26, 58, 60, 55},
    {14, 13, 16, 24, 40, 57, 69, 56},
    {14, 17, 22, 29, 51, 87, 80, 62},
    {18, 22, 37, 56, 68, 109, 103, 77},
    {24, 35, 55, 64, 81, 104, 113, 92},
    {49, 64, 78, 87, 103, 121, 120, 101},
    {72, 92, 95, 98, 112, 100, 103, 99}
};

Edit 2:

Method to quantize:

public void QuantizeLuminance(ref double[,] data) {
    for (var i = 0; i < 8; i++) {
        for (var j = 0; j < 8; j++) {
            data[i, j] /= LuminanceQuantizationMatrix[i, j];

        }
    }
}
Dimitry Rakhlei
  • 191
  • 1
  • 13
  • I think at this point you should look at some working source code to see how to correct the many mistakes in your code. The RLE encoding is only for runs of 0's and this is not where much compression takes place. The DCT values are compressed with Huffman (variable length codes) which include encoding runs of 0's. You don't show your quantization function, so we can't tell if it works correctly. Even if this code were made to work, it would be extremely slow. Is this just a thought exercise? – BitBank Feb 19 '16 at 20:43
  • This is an early version of JPEG and we are told to only use a table. The code for the table simply divides the values at [0 to 7] [0 to 7] from the dct return values by the values at the same indexes in the tables. – Dimitry Rakhlei Feb 19 '16 at 20:48
  • What do your DCT coefficients look like? – user3344003 Feb 20 '16 at 15:55
  • Wow, you posted everything except the quantization, which is probably where the problem is. I would guess that you didn't properly round toward zero after dividing by the quantization coefficient. – Matt Timmermans Feb 20 '16 at 22:50
  • What should I provide with an update? @user3344003 – Dimitry Rakhlei Feb 21 '16 at 00:07
  • To a zero, c [n,m] / q [n,m] must be zero. In other words c[n,m] < q[n,m]. What are those values? – user3344003 Feb 22 '16 at 01:49
  • For a dct of an example in the book: http://puu.sh/ngXu9/5bace97880.png I got http://puu.sh/ngXwb/7eb0ae766e.png after dct. And http://puu.sh/ngXwU/8b90e1b025.png after quantizing. – Dimitry Rakhlei Feb 22 '16 at 05:23

0 Answers0