1. 程式人生 > 實用技巧 >簡單的LED影象壓縮實現

簡單的LED影象壓縮實現

把LED螢幕資料傳到下位機傳屏中資料有點大,串列埠速度有點慢,整個過程大約需要200ms,故想到了影象壓縮提高傳數效率, 雖然gzip壓縮率基本能到1/3, 但微控制器好像很難使用這些庫, 故網上查了一下無失真壓縮的原理,自已實現了一個最簡單的. 基本能達到壓縮1/2的樣子.壓縮和解壓速度也很快!

原理:
字串行程編碼
這裡壓縮一段字串:RRRRRGGGBBBBBRRRRGB
壓縮的結果為:5R3G5B4R1G1B
這種方法叫做行程編碼run-ength encoding(RLE),應用於像BMP,TIFF格式的影象檔案中。

 1 //呼叫程式示例
 2 private void btnTestZip_Click(object sender, EventArgs e)
 3 {
 4     //獲取LED影象資料.
 5     // LED寬度<=255, 每行按上面的原理 故長度資訊可以放在1個位元組中
 6     // LED影象每8行,換成一個點(即一個位元組)
 7     var srcData = GetData(); //獲取LED影象資料.
 8     var zipData = ZipLedData(srcData);
 9     var unZipData = UnZipLedData(zipData);
10     Debug.Assert(srcData.Length == unZipData.Length);
11     for (var i=0; i<srcData.Length; i++)
12     {
13          Debug.Assert(srcData[i] == unZipData[i]);
14     }
15 }
 1         /*=================壓縮===============*/
 2         private byte[] ZipLedData(byte[] srcData)
 3         {
 4             if (srcData == null || srcData.Length < 1) throw new Exception("引數異常");
 5             Console.WriteLine(srcData.Length);
 6             Stopwatch stopwatch = new Stopwatch();
 7             stopwatch.Start();
 8 
 9             using var ms = new System.IO.MemoryStream();
10             using var bw = new System.IO.BinaryWriter(ms);
11 
12             var width = pic.Width; // LED寬度<=255, 每行掃描,差異  故長度資訊可以放在1個位元組中
13             var rows = pic.Height / 8; //LED影象每8行,換成一個點(即一個位元組)
14             for (var row = 0; row < rows; row++)
15             {
16                 var idx = row * width; //行起始索引
17                 var v = srcData[idx]; //行第一個元素
18                 bw.Write(v);  //寫入第0個畫素值
19                 byte cnt = 1;
20                 for (var col = 1; col < width; col++)
21                 {
22                     idx++;
23                     if (srcData[idx] == v)
24                     {
25                         //資料未發生變化, 長度位元組增加
26                         cnt++;
27                     }
28                     else
29                     {
30                         //資料發生變化
31                         bw.Write(cnt); //寫入長度位元組
32                         v = srcData[idx]; //記錄新值
33                         bw.Write(v); //寫入新值
34                         cnt = 1; //重置統計長度位元組=1
35                     }
36                 }
37                 bw.Write(cnt); //寫每行最後一個長度資訊
38             }
39             bw.Flush();
40             bw.Close();
41             var retData = ms.ToArray();
42             stopwatch.Stop();
43             Console.WriteLine($"總時長: {stopwatch.Elapsed.TotalMilliseconds}");
44             Console.WriteLine(retData.Length);
45             return retData;
46         }

 1         /*=================解壓===============*/
 2         private byte[] UnZipLedData(byte[] zipData)
 3         {
 4             Stopwatch stopwatch = new Stopwatch();
 5             stopwatch.Start();
 6             using var ms = new System.IO.MemoryStream();
 7             using var bw = new BinaryWriter(ms);
 8 
 9             var i = 0;
10             var zipDataLen = zipData.Length;
11             while (i< zipDataLen)
12             {
13                 var v = zipData[i++];
14                 var len = zipData[i++];
15                 bw.Write(v);
16                 for(var j=len-1; j>0; j--)
17                 {
18                     bw.Write(v);
19                 }
20             }
21             bw.Flush();
22             bw.Close();
23             var ret = ms.ToArray();
24             stopwatch.Stop();
25             Console.WriteLine($"總時長: {stopwatch.Elapsed.TotalMilliseconds}");
26             return ret;
27         }