2015-08-15 76 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色,你只需要修改一下。 –