2015-08-15 73 views
0

我正在一個項目和在這個項目中工作,我需要捕獲桌面屏幕並通過互聯網發送到其他客戶端。要壓縮圖像,我想將其轉換爲256色圖像。我有一個普通的25​​6色調色板,我使用歐幾里得距離來找到最接近的顏色。問題是我需要以每秒10-15幀的速度發送這些圖像,並使256色圖像需要7秒。我想知道其他程序(如teamviewer或真正的VNC)是如何做到這一點的。捕獲桌面,使其256色,並通過互聯網發送

for (int y=0;y<900;y++) //loop through the height 
    for (int x=0;x<1600;x++) //loop through the width 
     for (int p=0;p<256;p++) //loop through palette colors 
      { 
       calculate Euclidean distance of each pixel for each color in pallette and 
       find the nearest color 
       ** these nested loops take 7 seconds to complete 
      } 

感謝

+4

無需將圖像轉換爲256色,只需將截圖保存爲24位PNG或JPEG圖像,即可獲得更清晰可讀的內容,而且尺寸僅爲幾百KiB。 – Dai

+0

VNC爲幀使用JPEG壓縮,Windows中的遠程桌面使用不同的技術,以協議複雜度爲代價,涉及更多的巫術。 TeamViewer使用他們自己的協議,採用一些黑客技術,如顏色下采樣,但也包括線倍增。現在RDP採用JPEG壓縮技術處理快速移動的場景,但其拼貼方式不起作用。 – Dai

+0

謝謝,但最終所有這些應用程序都有256色選項,並且在慢速連接中它們會自動選擇此選項。 256色圖像大約70-80 KiB,質量合理。我正在尋找能夠在1/15秒內製作256色圖像的最快方法。 – miladrasooli

回答

0

好的。經過幾天與許多捕獲方法和色彩量化師的鬥爭後,我終於找到了解決方案。現在,我可以以10〜14 FPS的速度發送整個桌面圖像,並以20〜30 FPS的速度改變桌面區域。

在代碼中我使用rcravens的類來捕獲屏幕和屏幕的變化。然後我把這幅圖像剪成10張。在此之後,我帶了一些小片,並使用Octree Color Quantizier解釋here使它們成爲256色,這要歸功於@Dai,他指出了我的發展方向。減色後,我將每一塊轉換爲一個字節數組,並使用LZ4.Net庫進行壓縮。

下面是代碼:

int all_count = 0; 
    Bitmap _desktop = null; 

    Bitmap _merged_bitmap = new Bitmap(1600, 900); 

    int _height_part_ = 0; 
    int _total_rows = 10; 
    Bitmap[] crops = null; 
    Bitmap[] _new_crops = null; 
    Stopwatch sw = new Stopwatch(); 

    int _desktop_height = 0; 
    int _desktop_width = 0; 

    ImageManipulation.OctreeQuantizer _q ; 
    RLC.RemoteDesktop.ScreenCapture cap = new RLC.RemoteDesktop.ScreenCapture(); 



    private void CaptureAndSend() 
    { 
     sw.Restart(); 

     //cap = new RLC.RemoteDesktop.ScreenCapture(); 

     int _left = -1, _top = -1; //Changed regions 
     _desktop = cap.Screen(out _left, out _top); //Capture desktop or changed region of it 

     if (_desktop == null) return; //if nothing has changed since last capture skip everything 

     _desktop_height = _desktop.Height; 
     _desktop_width = _desktop.Width; 

     // If very small part has changed since last capture skip everything 
     if (_desktop_height < 10 || _desktop_width < 10) return; 

     TotalRows(_total_rows); // Calculate the total number of rows 

     crops = new Bitmap[_total_rows]; // Cropped pieces of image 
     _new_crops = new Bitmap[_total_rows]; 

     for (int i = 0; i < _total_rows - 1; i++) //Take whole image and split it into smaller images 
      crops[i] = CropRow(i); 
     crops[_total_rows - 1] = CropLastRow(_total_rows - 1); 


     Parallel.For(0, _total_rows, i => 
     { 
      ImageManipulation.OctreeQuantizer _q = new ImageManipulation.OctreeQuantizer(255, 4); // Initialize Octree 
      _new_crops[i] = _q.Quantize(crops[i]); 

      using (MemoryStream ms=new MemoryStream()) 
      { 
       _new_crops[i].Save(ms, ImageFormat.Png); 
       //Install-Package LZ4.net 
       //Compress each part and send them over network 
       byte[] data = Lz4Net.Lz4.CompressBytes(ms.ToArray(), Lz4Net.Lz4Mode.HighCompression); 

       all_count += data.Length; //Just to check the final size of image 
      }     
     }); 



     Console.WriteLine(String.Format("{0:0.0} FPS , {1} seconds , size {2} kb", 1.0/sw.Elapsed.TotalSeconds, sw.Elapsed.TotalSeconds.ToString(), all_count/1024)); 
     all_count = 0; 

    } 
    private void TotalRows(int parts) 
    { 
     _height_part_ = _desktop_height/parts; 
    } 
    private Bitmap CropRow(int row) 
    { 
     return Crop(_desktop, new Rectangle(0, row * _height_part_, _desktop_width, _height_part_)); 
    } 
    private Bitmap CropLastRow(int row) 
    { 
     return Crop(_desktop, new Rectangle(0, row * _height_part_, _desktop_width, _desktop_height - (row * _height_part_))); 
    } 
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)] 
    private unsafe static extern int memcpy(byte* dest, byte* src, long count); 

    private unsafe Bitmap Crop(Bitmap srcImg, Rectangle rectangle) 
    { 
     if ((srcImg.Width == rectangle.Width) && (srcImg.Height == rectangle.Height)) 
      return srcImg; 

     var srcImgBitmapData = srcImg.LockBits(new Rectangle(0, 0, srcImg.Width, srcImg.Height), ImageLockMode.ReadOnly, srcImg.PixelFormat); 
     var bpp = srcImgBitmapData.Stride/srcImgBitmapData.Width; // 3 or 4 
     var srcPtr = (byte*)srcImgBitmapData.Scan0.ToPointer() + rectangle.Y * srcImgBitmapData.Stride + rectangle.X * bpp; 
     var srcStride = srcImgBitmapData.Stride; 

     var dstImg = new Bitmap(rectangle.Width, rectangle.Height, srcImg.PixelFormat); 
     var dstImgBitmapData = dstImg.LockBits(new Rectangle(0, 0, dstImg.Width, dstImg.Height), ImageLockMode.WriteOnly, dstImg.PixelFormat); 
     var dstPtr = (byte*)dstImgBitmapData.Scan0.ToPointer(); 
     var dstStride = dstImgBitmapData.Stride; 

     for (int y = 0; y < rectangle.Height; y++) 
     { 
      memcpy(dstPtr, srcPtr, dstStride); 
      srcPtr += srcStride; 
      dstPtr += dstStride; 
     } 

     srcImg.UnlockBits(srcImgBitmapData); 
     dstImg.UnlockBits(dstImgBitmapData); 
     return dstImg; 
    } 

我知道,我的代碼是不是記憶效率。如果有人能幫我優化這個代碼,我會很感激。 再次感謝我的朋友A.阿布拉莫夫,戴,HansPassant,TaW等人。

0

編輯2:我刪除了我的舊文章完全是因爲它是不相關的!通過256色,我認爲你的意思是256位 - 而你說的是256字節!我想通過在我的計算器中放入原來的座標(900 x 900),並將顏色乘以256。結果是20,7360,000位,大致爲2.5 MB。壓縮後,這可能會達到約1 MB - 而位顏色equivallent(除以8)將爲300 KB基數,並且壓縮將會小得多。解決方案很簡單 - 這確實需要很長時間才能拍出這樣的圖像。基於計算機的性能,大部分您所說的應用程序(如teamviewer)的圖像質量都較低。因此,我很抱歉 - 但解決辦法是在您請求使用像您這樣的計算機的時間內完成這項工作可能不太可能。

編輯3:漢斯在你的問題的評論中做了數學 - 我們在談論22 GB。這對普通電腦來說不是一項正常的工作。這並非不可能,但從2015年開始,家用計算機在一秒鐘內處理這麼多數據的情況並不普遍。

+0

謝謝,但我認爲你已經錯過了這一點。我知道如何將桌面捕獲到位圖中,我希望在1/15秒內使它變成256色,以儘可能快地通過互聯網發送。 – miladrasooli

+0

如果您告訴我哪些方法具有捕獲參數256色。我到處搜索,我還沒有找到它。 – miladrasooli

+0

@ user2840253第二個是PNG,所以我會默認使用它 - 第三個也很容易創建256色,你只需要修改一下。 –