量化dct不会产生0的运行

时间:2016-02-19 20:05:59

标签: c# jpeg signal-processing image-compression dct

如需快速参考,请参阅github link

我正在尝试实现简单的JPEG压缩。 我将提供一些更值得注意的方法。

问题是我没有看到任何显着的0运行,所以我的RLE编码没有做任何压缩图像。

代码:

RGB到YCbCr转换:

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);
}

分裂到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;
}

子采样:

/// <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;
}

现在进行dct处理:

逐步执行数组8x8的步骤功能。

/// <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;
}

执行所有处理的功能:

/// <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: 之字形阅读:

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;
}

运行长度编码。

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();
}

我知道这是一个很长的帖子,但我无法自己解决这个问题。我们使用的这本书并没有多大帮助,所以我只能随意编写代码直到有效。

现在我可以使用可以均匀分割成8x8块的图像,例如github上的lena.tif图像。

问题出现在我的RGB&gt; YCBCR&gt;子样本&gt; DCT>量化 在这一点上,我的值没有像他们应该的那样运行0。

所有建议都表示赞赏。

编辑:

量化表:

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}
};

编辑2:

量化方法:

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];

        }
    }
}

0 个答案:

没有答案