空間域壓縮:
- 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)

- 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。
頻域壓縮:
- 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#實作) ),如果位置圖像沒變就記入(0,0)。解壓方式:按照記錄的偏移(圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作) ,圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作) )結合本身坐标,映射回reference frame 取其block值。圖像處理(Image Processing) ---------- 圖像和影像壓縮(Compression)(C#實作)
這裡有個疑問點:目前要壓縮的幀多了前一幀中不存在的物體,那麼壓縮時按block對比,取前一幀中最相似的block作為記錄。是以解壓時這不存在的物體會用前一幀中很相似的block拼湊出來,可以說是失幀的。是以這兒還會使用一種技術叫GOP,即定時會更新一次幀,就是定時保留一個幀不壓縮作為之後壓縮幀的新reference frame。另外視訊資訊量是很大的1秒鐘30張圖檔,是以當一個球突然飛入視訊中,current frame有球,壓縮時,reference frame是無球的,就會用reference frame中與球相似的block把球拼出來,雖然不太準确但是視訊我們允許前面幾幀不太準确,之後馬上會有GOP更新,那麼就會以有球的幀作為之後要壓縮的幀參考的前一幀了。
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