天天看點

圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)

空間域壓縮:

  • Fractal Coding :https://blog.csdn.net/weixin_35811044/article/details/84349624
  • Run Length Coding: 圖像中連續出現的相同Pixel,隻記錄一個但需多一個符号記錄其出現的次數,無損壓縮。Ex.111110000003355 -->  51602325。 PCX圖像就是采用此壓縮方式,可以具體看我的另一篇blog:https://blog.csdn.net/weixin_35811044/article/details/83997166。
  • Sub-sampling:最基礎的圖像壓縮技術,有損壓縮,會降低圖像的品質,根據人類眼睛對圖像的色度(colour)不敏感而對亮度(luminance)很敏感,采取減少Pixel數的壓縮方式。壓縮方式:1、直接減,認為相靠近的Pixel都比較相似。比如2x2的方形,四個Pixel隻保留一個。2、取均值,取四周的Pixle的平均值後保留。解壓方式:1、直接複制。2、插值法。
  • Coarse Quantiztion: 與sub-sampling相似,但不同的是它的Pixel數不會減少,減少的是每個Pixel的bit數,也叫做bit depth reduction(pixel的深度減少)。如下圖:每一個pixel的bit數從左到右減少.(大概像是8bit-->6bit-->2bit)
圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)
  • Vector quantization:類似于字典,會有一個table,每一個table entry由4個pixel構成,作為一個sample。壓縮方式:将圖像的pixel,每4個分為一個block,然後與table裡的所有sample比較,取最為相似的sample記錄其table entry number。解壓方式:按照記錄的table entry number 對照table 取出相應的 sample(4個pixel值)。這同樣是一個有損壓縮,因為table 不可能會存所有的pixel組合,是以取的是最相似的sample。
圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)

頻域壓縮:

  • Transform Coding: 空間域轉換到頻域在圖像進行中最常用的即DCT(Discrete Cosine Transform),空間域轉換到頻域是無失真的,若什麼都不做直接反變換可得到原圖。對于一張圖來說資訊太大,為了降低複雜度,通常會先将圖檔按NxN分割成一個一個的小塊,再逐一進行DCT轉換 。如:8x8 有 64個Pixel,轉換後有 8x8 64個頻率系數,再用适合的方法zig.......來排序,排成為 低 -------------> 高 的一維頻率系數,然後再量化壓縮,最後反變換回空間域。

時域壓縮(Video):

  • Sub-sampling: 在空間域上sub-sampling是采用減少Pixel數,那麼在時間域上同樣可以如此,即減少幀數。比如: 30 frame / s  的視訊,那麼隻記錄1、3、5、7、9.....奇數幀,即可壓縮一半。解壓方式:要複原原來丢失2、4、6、8、10......。1、直接複制前面的幀。2、取前後幀的平均值。可以看出在時域依舊是有損壓縮。
  • Difference Coding: 也叫預測編碼(壓縮)法。在空間域裡即Relative-coding壓縮,記錄與前一個Pixel值的內插補點(原來要記125、128、130,現在隻要記125、3、2),這是根據相近的Pixel值變化通常不會太大。在時間域上也是如此,認為在短時間內,影像的動作變化也不會太大。是以壓縮時記錄時間軸上,與前一幀內插補點,為0的代表完全沒有變化不記錄的話,那就還需要多記錄內插補點的(x,y)坐标,這樣才知道內插補點是屬于幀內哪一個pixel,這是額外的開銷。如果很多pixel都變了那就需要很多而外的開銷來記錄內插補點的坐标,反而可能會出現比壓縮前還大的情況。兩種改進方式:1、本身Difference Coding是無失真的壓縮,但是對于影像我們并不需要無失真,是以為了提高壓縮比引入失真壓縮,即當pixel的差異超過某個值才記錄,否則就當作沒有變。2、從pixel level 上升到 block level,即将幀按塊分割(如:8x8),當一個block中所有的內插補點之和大于某一個值時才記錄,否則當作沒有變。(即Block Based Difference Coding)
  • Block Based Motion Compensation: 當視訊有大量的移動或者是錄影機的水準上下移動,Difference Coding就變的很無力了,就比如攝像頭隻要水準移動一點整個影像的pixel位置就全變了,內插補點就如雨後春筍般冒出。是以就需要移動補償法。如圖:目前壓縮的幀叫current frame,前一幀叫reference frame,幀按一定大小分成了不重疊的block。假設目前壓縮到current frame的藍色block叫target block,然後将其從頭(紅色block)開始與reference frame中的每一個block對比,找的最相似的block(藍色)叫matching block,然後記錄兩者之間的位置偏移(
    圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)
    ,
    圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)
    ),如果位置圖像沒變就記入(0,0)。解壓方式:按照記錄的偏移(
    圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)
    ,
    圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)
    )結合本身坐标,映射回reference frame 取其block值。

這裡有個疑問點:目前要壓縮的幀多了前一幀中不存在的物體,那麼壓縮時按block對比,取前一幀中最相似的block作為記錄。是以解壓時這不存在的物體會用前一幀中很相似的block拼湊出來,可以說是失幀的。是以這兒還會使用一種技術叫GOP,即定時會更新一次幀,就是定時保留一個幀不壓縮作為之後壓縮幀的新reference frame。另外視訊資訊量是很大的1秒鐘30張圖檔,是以當一個球突然飛入視訊中,current frame有球,壓縮時,reference frame是無球的,就會用reference frame中與球相似的block把球拼出來,雖然不太準确但是視訊我們允許前面幾幀不太準确,之後馬上會有GOP更新,那麼就會以有球的幀作為之後要壓縮的幀參考的前一幀了。

圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)

C#實作視訊壓縮:

class VideoOperation
    {

         //擷取目前壓縮frame和其前一個frame的所有塊,并存在對應的bitmap數組中。
        public void VideoGetPool(Bitmap targetImage, Bitmap referenceImage, out List<Bitmap> CurrentPool, out List<Bitmap> CandidatePool)
        {
            CurrentPool = new List<Bitmap>();
            CandidatePool = new List<Bitmap>();
            int widthT = targetImage.Width;
            int heightT = targetImage.Height;
            int widthR = referenceImage.Width;
            int heightR = referenceImage.Height;
            //Rangeblock和Domainblock的大小(單邊)
            int rangeSize = 4;
            int domainSize = 4;

            //塊數
            int Rx = widthT / rangeSize;
            int Ry = heightT / rangeSize;
            int Dx = widthR / domainSize;
            int Dy = heightR / domainSize;

            int x1 = 0;
            int y1 = 0;
            int x2 = 0;
            int y2 = 0;

            for (int j = 0; j < Ry; j++)
            {
                for (int i = 0; i < Rx; i++)
                {
                    Bitmap CurrentBlock = targetImage.Clone(new Rectangle(x1, y1, rangeSize, rangeSize), PixelFormat.Format24bppRgb);
                    CurrentPool.Add(CurrentBlock);
                    x1 += rangeSize;
                }
                y1 += rangeSize;
                x1 = 0;
            }

            for (int j = 0; j < Dy; j++)
            {
                for (int i = 0; i < Dx; i++)
                {
                    Bitmap domainBlock = referenceImage.Clone(new Rectangle(x2, y2, domainSize, domainSize), PixelFormat.Format24bppRgb);
                    CandidatePool.Add(domainBlock);
                    x2 += domainSize;
                }
                y2 += domainSize;
                x2 = 0;
            }
        }


        //均值方式計算塊間差異:
        public double CalculateMin(Bitmap targetImage, Bitmap referenceImage)
        {
            double sumR = 0;
            double sumD = 0;
            double aveR = 0;
            double aveD = 0;

            for (int j = 0; j < 4; j++)
            {
                for (int i = 0; i < 4; i++)
                {
                    sumR += targetImage.GetPixel(i, j).R;
                    sumD += referenceImage.GetPixel(i, j).R;
                }
            }
            aveR = sumR / 16.0;
            aveD = sumD / 16.0;

            double s = Math.Abs(aveR - aveD);
            return s;
        }
    }
           
//輸入從VideoGetPool中擷取的參照塊池和目前塊池。逐一對比,找到最像的塊,記錄坐标偏移。

private void VideoEncode(List<Bitmap> CurrentPool, List<Bitmap> CandidatePool)
        {
            double s = 0;
            Dictionary<int, int> MotionVector = new Dictionary<int, int>();
            List<double> ssum = new List<double>();

            for (int k = 0; k < CurrentPool.LongCount(); k++)
            {
            
                if (vop.CalculateMin(CurrentPool[k], CandidatePool[k]) < 10)
                {
                    MotionVector.Add(k, k);
                }
                else
                {
                    for (int t = 0; t < CandidatePool.LongCount(); t++)
                    {
                        //調用均值計算,對比塊間差距。
                        s = vop.CalculateMin(CurrentPool[k], CandidatePool[t]);
                        ssum.Add(s);
                        //if (s < 1)  break; 
                    }

                    double min = ssum[0];
                    int motion = 0;
                    for (int n = 0; n < ssum.LongCount(); n++)
                    {
                        if (ssum[n] < min)
                        { //取差距最小的塊,儲存。
                            min = ssum[n];
                            motion = n;
                        }
                    }
                    MotionVector.Add(k, motion);
                    ssum.Clear();
                }
            }
            //将坐标偏移寫入文檔存儲,作為壓縮檔案。
            StreamWriter sfile = new StreamWriter(@"F:/VirtualStudioData/ImageProcessing01/encode.txt",true);
            
            int x = 0;
            int y = 0;
            for (int i = 0; i < MotionVector.LongCount(); i++)
            {
                if (i == MotionVector.LongCount()-1)
                {
                    x = MotionVector[i] % 64 * 4;
                    y = MotionVector[i] / 64 * 4;

                    sfile.Write(x + "," + y + ";" + "|");
                }
                else
                {
                    x = MotionVector[i] % 64 * 4;
                    y = MotionVector[i] / 64 * 4;

                    sfile.Write(x + "," + y + ";");
                }                   
            }
            sfile.Flush();
            sfile.Close();
            //file.Close();
        }
           

僅為個人了解,如有不足,請指教。 https://blog.csdn.net/weixin_35811044