不安全的每像素的访问,为1756000像素30毫秒访问 [英] Unsafe Per Pixel access, 30ms access for 1756000 pixels

查看:242
本文介绍了不安全的每像素的访问,为1756000像素30毫秒访问的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以我一直在分享上述主题标题的一些想法在我的网站大约快,不安全像素的访问。有位先生给我的,他会怎么做C ++中的粗糙的例子,但是这并不能帮助我在C#中,除非我可以互操作它,互操作的快为好。我发现在使用MSDN帮助写的,到不安全的访问像素的互联网类。类是特别快,但它的速度不够快。这里的类:

 使用系统; 
使用System.Collections.Generic;
使用System.Text;
使用System.Drawing中;
使用System.Drawing.Imaging;

命名空间DCOMProductions.Desktop.ScreenViewer {
公众安全的类UnsafeBitmap {
位图位图;

// 3用于MakeGreyUnsafe
INT宽度元素;
的BitmapData位图数据= NULL;
字节* PBASE = NULL;

公共UnsafeBitmap(位图位图){
this.bitmap =新位图(位图);
}

公共UnsafeBitmap(INT宽度,高度INT){
this.bitmap =新位图(宽度,高度,PixelFormat.Format32bppArgb);
}

公共无效的Dispose(){
bitmap.Dispose();
}

公共位图位图{
获得{
回报(位图);
}
}

私人点PixelSize {
获得{
GraphicsUnit单位= GraphicsUnit.Pixel;
的RectangleF边界= bitmap.GetBounds(REF单位);

返回新的点((INT)bounds.Width,(INT)bounds.Height);
}
}

公共无效LockBitmap(){
GraphicsUnit单位= GraphicsUnit.Pixel;
的RectangleF boundsF = bitmap.GetBounds(REF单位);
矩形范围=新的Rectangle((INT)boundsF.X,
(INT)boundsF.Y,
(INT)boundsF.Width,
(INT)boundsF.Height) ;

//找出的字节数在连续
//这是舍入为4
//字节的倍数,因为在图像中的扫描线必须总是4字节
的倍数//长度。
宽度=(INT)boundsF.Width *的sizeof(像素);
如果(!宽度%4 = 0){
宽度= 4 *(宽度/ 4 + 1);
}
位图数据=
bitmap.LockBits(边界,ImageLockMode.ReadWrite,PixelFormat.Format32bppArgb);

PBASE =(BYTE *)bitmapData.Scan0.ToPointer();
}

公共像素与getPixel(INT X,int y)对{
像素的returnValue = * PixelAt(X,Y​​);
回报率的returnValue;
}

公共无效与setPixel(INT X,INT Y,像素颜色){
像素*像素= PixelAt(X,Y​​);
*像素=颜色;
}

公共无效UnlockBitmap(){
bitmap.UnlockBits(位图数据);
位图数据= NULL;
PBASE = NULL;
}
公共像素* PixelAt(INT X,int y)对{
回报(*像素)(PBASE + Y *宽+ X *的sizeof(像素));
}
}



}



基本上,我做的是复制整个屏幕,每个像素比较老的副本。在1680×1050的位图,这个使用下面的代码大约需要300毫秒。

 私人位图GetInvalidFrame(位图帧){
秒表SP =新的秒表();
sp.Start();

如果(m_FrameBackBuffer == NULL){
返回框架;
}

的Int32 pixelsToRead = frame.Width * frame.Height;
的Int32 X = 0,Y = 0;

UnsafeBitmap unsafeBitmap =新UnsafeBitmap(架);
UnsafeBitmap unsafeBuffBitmap =新UnsafeBitmap(m_FrameBackBuffer);
UnsafeBitmap retVal的=新UnsafeBitmap(frame.Width,frame.Height);

unsafeBitmap.LockBitmap();
unsafeBuffBitmap.LockBitmap();
retVal.LockBitmap();

做{
为(X = 0; X< frame.Width; X ++){$​​ B $ B像素newPixel = unsafeBitmap.GetPixel(X,Y);
像素oldPixel = unsafeBuffBitmap.GetPixel(X,Y);如果

(newPixel.Alpha!= oldPixel.Alpha || newPixel.Red!= oldPixel.Red || newPixel.Green!= oldPixel.Green || newPixel.Blue!= oldPixel.Blue){
retVal.SetPixel(X,Y,newPixel);
}
,否则{
//跳过像素
}
}

Y形++;
},而(Y = frame.Height!);

unsafeBitmap.UnlockBitmap();
unsafeBuffBitmap.UnlockBitmap();
retVal.UnlockBitmap();

sp.Stop();

System.Diagnostics.Debug.WriteLine(sp.Elapsed.Milliseconds.ToString());

sp.Reset();

返回retVal.Bitmap;
}



是否有任何可能的方法/工具/方法,我可以加速这一可达约30毫秒?我可以在大约30毫秒使用Graphics.CopyFromScreen()屏幕拷贝,从而使生产约30帧每秒。然而,一个程序只运行一样快,它较慢的对手,所以在GetInvalidFrame的300ms的延迟,减缓下来大约1 - 3帧每秒。这不是很好的一个会议软件。



任何意见,办法,在正确的方向指针是绝对精彩! 。同样,用于绘制在客户端的位图的代码如下以及



要对德米特里的答案/发表评论评论:

 #地区RootWorkItem 

私人ScreenClient m_RootWorkItem;
///<总结>
///获取RootWorkItem
///< /总结>
公共ScreenClient RootWorkItem {
获得{
如果(m_RootWorkItem == NULL){
m_RootWorkItem =新ScreenClient();
m_RootWorkItem.FrameRead + =新的EventHandler< FrameEventArgs>(RootWorkItem_FrameRead);
}
返回m_RootWorkItem;
}
}

#endregion

私人无效RootWorkItem_FrameRead(对象发件人,FrameEventArgs E){
如果(e.Frame!= NULL){
如果(uxSurface.Image = NULL){
位图帧=(位图)uxSurface.Image!;

图形G = Graphics.FromImage(架);
g.DrawImage(e.Frame,0,0); //只画出更新像素

uxSurface.Image =框架;
}
,否则{
uxSurface.Image = e.Frame; //绘制初始,完整映像
}
}
,否则{
uxSurface.Image = NULL;
}
}


解决方案

不安全方法与整数的使用,而不是像素数和单回路:

 私有静态位图GetInvalidFrame(位图oldFrame,位图newFrame)
{
如果(oldFrame.Size = newFrame.Size!)
{
抛出新的ArgumentException();
}
位图的结果=新位图(oldFrame.Width,oldFrame.Height,oldFrame.PixelFormat);

矩形lockArea =新的Rectangle(Point.Empty,oldFrame.Size);
格式的PixelFormat = PixelFormat.Format32bppArgb;
的BitmapData OLDDATA = oldFrame.LockBits(lockArea,ImageLockMode.ReadOnly,格式);
的BitmapData newData = newFrame.LockBits(lockArea,ImageLockMode.ReadOnly,格式);
的BitmapData resultData = result.LockBits(lockArea,ImageLockMode.WriteOnly,格式);

INT LEN = resultData.Height * Math.Abs​​(resultData.Stride)/ 4;

不安全
{
为int * POLD =(INT *)oldData.Scan0;
为int * Pnew最多=(INT *)newData.Scan0;
为int * pResult =(INT *)resultData.Scan0;

的for(int i = 0; I< LEN,我++)
{
INT属性oldValue = * POLD ++;
INT为newValue = * Pnew最多++;
* pResult ++ =属性oldValue!=为newValue?为newValue:0 / *使用0xff<取代;< 24,如果你需要非透明的黑色像素* /;
// * pResult ++ = * POLD ++ ^ * Pnew最多++; //如果你可以使用异或。
}
}

oldFrame.UnlockBits(OLDDATA);
newFrame.UnlockBits(newData);
result.UnlockBits(resultData);

返回结果;
}



我觉得你真的可以使用异或这里框架,我希望这个可以有双方更好的表现。

 私有静态无效XorFrames(位图leftFrame,位图rightFrame)
{
如果(!leftFrame.Size = rightFrame.Size)
{
抛出新的ArgumentException();
}

矩形lockArea =新的Rectangle(Point.Empty,leftFrame.Size);
格式的PixelFormat = PixelFormat.Format32bppArgb;
的BitmapData leftData = leftFrame.LockBits(lockArea,ImageLockMode.ReadWrite,格式);
的BitmapData rightData = rightFrame.LockBits(lockArea,ImageLockMode.ReadOnly,格式);

INT LEN = leftData.Height * Math.Abs​​(rightData.Stride)/ 4;

不安全
{
为int * pLeft =(INT *)leftData.Scan0;
为int * pRight =(INT *)rightData.Scan0;

的for(int i = 0; I< LEN,我++)
{
* pLeft ++ ^ = * pRight ++;
}
}

leftFrame.UnlockBits(leftData);
rightFrame.UnlockBits(rightData);
}

您可以在以下方式双方使用此过程:

在服务器端,您需要评估新老框架之间的区别,将其发送到客户端和新的取代旧的框架。服务器代码应该是这个样子:

  XorFrames(oldFrame,newFrame); // oldFrame ^ = newFrame 
发送(oldFrame); //送两帧
oldFrame = newFrame的XOR;

在客户端需要更新与服务器收到XOR帧的当前帧:



  XorFrames((位图)uxSurface.Image,e.Frame); 


So I've been sharing some thoughts on the above topic title on my website about fast, unsafe pixel access. A gentlemen gave me a rough example of how he'd do it in C++, but that doesn't help me in C# unless I can interop it, and the interop is fast as well. I had found a class in the internet that was written using MSDN help, to unsafely access pixels. The class is exceptionally fast, but it's not fast enough. Here's the class:

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;

namespace DCOMProductions.Desktop.ScreenViewer {
public unsafe class UnsafeBitmap {
    Bitmap bitmap;

    // three elements used for MakeGreyUnsafe
    int width;
    BitmapData bitmapData = null;
    Byte* pBase = null;

    public UnsafeBitmap(Bitmap bitmap) {
        this.bitmap = new Bitmap(bitmap);
    }

    public UnsafeBitmap(int width, int height) {
        this.bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb);
    }

    public void Dispose() {
        bitmap.Dispose();
    }

    public Bitmap Bitmap {
        get {
            return (bitmap);
        }
    }

    private Point PixelSize {
        get {
            GraphicsUnit unit = GraphicsUnit.Pixel;
            RectangleF bounds = bitmap.GetBounds(ref unit);

            return new Point((int)bounds.Width, (int)bounds.Height);
        }
    }

    public void LockBitmap() {
        GraphicsUnit unit = GraphicsUnit.Pixel;
        RectangleF boundsF = bitmap.GetBounds(ref unit);
        Rectangle bounds = new Rectangle((int)boundsF.X,
      (int)boundsF.Y,
      (int)boundsF.Width,
      (int)boundsF.Height);

        // Figure out the number of bytes in a row
        // This is rounded up to be a multiple of 4
        // bytes, since a scan line in an image must always be a multiple of 4 bytes
        // in length. 
        width = (int)boundsF.Width * sizeof(Pixel);
        if (width % 4 != 0) {
            width = 4 * (width / 4 + 1);
        }
        bitmapData =
      bitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);

        pBase = (Byte*)bitmapData.Scan0.ToPointer();
    }

    public Pixel GetPixel(int x, int y) {
        Pixel returnValue = *PixelAt(x, y);
        return returnValue;
    }

    public void SetPixel(int x, int y, Pixel colour) {
        Pixel* pixel = PixelAt(x, y);
        *pixel = colour;
    }

    public void UnlockBitmap() {
        bitmap.UnlockBits(bitmapData);
        bitmapData = null;
        pBase = null;
    }
    public Pixel* PixelAt(int x, int y) {
        return (Pixel*)(pBase + y * width + x * sizeof(Pixel));
    }
}

}

Basically what I am doing is copying the entire screen and comparing each pixel to and old copy. On a 1680x1050 bitmap, this takes approximately 300 milliseconds using the following code.

private Bitmap GetInvalidFrame(Bitmap frame) {
        Stopwatch sp = new Stopwatch();
        sp.Start();

        if (m_FrameBackBuffer == null) {
            return frame;
        }

        Int32 pixelsToRead = frame.Width * frame.Height;
        Int32 x = 0, y = 0;

        UnsafeBitmap unsafeBitmap = new UnsafeBitmap(frame);
        UnsafeBitmap unsafeBuffBitmap = new UnsafeBitmap(m_FrameBackBuffer);
        UnsafeBitmap retVal = new UnsafeBitmap(frame.Width, frame.Height);

        unsafeBitmap.LockBitmap();
        unsafeBuffBitmap.LockBitmap();
        retVal.LockBitmap();

        do {
            for (x = 0; x < frame.Width; x++) {
                Pixel newPixel = unsafeBitmap.GetPixel(x, y);
                Pixel oldPixel = unsafeBuffBitmap.GetPixel(x, y);

                if (newPixel.Alpha != oldPixel.Alpha || newPixel.Red != oldPixel.Red || newPixel.Green != oldPixel.Green || newPixel.Blue != oldPixel.Blue) {
                   retVal.SetPixel(x, y, newPixel);
                }
                else {
                    // Skip pixel
                }
            }

            y++;
        } while (y != frame.Height);

        unsafeBitmap.UnlockBitmap();
        unsafeBuffBitmap.UnlockBitmap();
        retVal.UnlockBitmap();

        sp.Stop();

        System.Diagnostics.Debug.WriteLine(sp.Elapsed.Milliseconds.ToString());

        sp.Reset();

        return retVal.Bitmap;
    }

Is there any possible method/means/approach that I could speed this up to about 30ms? I can copy the screen in about 30ms using Graphics.CopyFromScreen(), so that produces approximately 30 frames each second. However, a program only runs as fast as its slower counterpart, so the 300ms delay in GetInvalidFrame, slows this down to about 1 - 3 frames each second. This isn't good for a meeting software.

Any advice, approaches, pointers in the right direction would be absolutely wonderful! Also, the code that is used to draw the bitmap on the client-side is below as well.

To comment on Dmitriy's answer/comment:

#region RootWorkItem

    private ScreenClient m_RootWorkItem;
    /// <summary>
    /// Gets the RootWorkItem
    /// </summary>
    public ScreenClient RootWorkItem {
        get {
            if (m_RootWorkItem == null) {
                m_RootWorkItem = new ScreenClient();
                m_RootWorkItem.FrameRead += new EventHandler<FrameEventArgs>(RootWorkItem_FrameRead);
            }
            return m_RootWorkItem;
        }
    }

    #endregion

    private void RootWorkItem_FrameRead(Object sender, FrameEventArgs e) {
        if (e.Frame != null) {
            if (uxSurface.Image != null) {
                Bitmap frame = (Bitmap)uxSurface.Image;

                Graphics g = Graphics.FromImage(frame);
                g.DrawImage(e.Frame, 0, 0); // Draw only updated pixels

                uxSurface.Image = frame;
            }
            else {
                uxSurface.Image = e.Frame; // Draw initial, full image
            }
        }
        else {
            uxSurface.Image = null;
        }
    }

解决方案

Unsafe approach with usage of integers instead of Pixels and single loop:

private static Bitmap GetInvalidFrame(Bitmap oldFrame, Bitmap newFrame)
{
    if (oldFrame.Size != newFrame.Size)
    {
        throw new ArgumentException();
    }
    Bitmap result = new Bitmap(oldFrame.Width, oldFrame.Height, oldFrame.PixelFormat);

    Rectangle lockArea = new Rectangle(Point.Empty, oldFrame.Size);
    PixelFormat format = PixelFormat.Format32bppArgb;
    BitmapData oldData = oldFrame.LockBits(lockArea, ImageLockMode.ReadOnly, format);
    BitmapData newData = newFrame.LockBits(lockArea, ImageLockMode.ReadOnly, format);
    BitmapData resultData = result.LockBits(lockArea, ImageLockMode.WriteOnly, format);

    int len = resultData.Height * Math.Abs(resultData.Stride) / 4;

    unsafe
    {
        int* pOld = (int*)oldData.Scan0;
        int* pNew = (int*)newData.Scan0;
        int* pResult = (int*)resultData.Scan0;

        for (int i = 0; i < len; i++)
        {
            int oldValue = *pOld++;
            int newValue = *pNew++;
            *pResult++ = oldValue != newValue ? newValue : 0 /* replace with 0xff << 24 if you need non-transparent black pixel */;
            // *pResult++ = *pOld++ ^ *pNew++; // if you can use XORs.
        }
    }

    oldFrame.UnlockBits(oldData);
    newFrame.UnlockBits(newData);
    result.UnlockBits(resultData);

    return result;
}

I think you really can use XORed frames here and I hope that this can have better performance on both sides.

    private static void XorFrames(Bitmap leftFrame, Bitmap rightFrame)
    {
        if (leftFrame.Size != rightFrame.Size)
        {
            throw new ArgumentException();
        }

        Rectangle lockArea = new Rectangle(Point.Empty, leftFrame.Size);
        PixelFormat format = PixelFormat.Format32bppArgb;
        BitmapData leftData = leftFrame.LockBits(lockArea, ImageLockMode.ReadWrite, format);
        BitmapData rightData = rightFrame.LockBits(lockArea, ImageLockMode.ReadOnly, format);

        int len = leftData.Height * Math.Abs(rightData.Stride) / 4;

        unsafe
        {
            int* pLeft = (int*)leftData.Scan0;
            int* pRight = (int*)rightData.Scan0;

            for (int i = 0; i < len; i++)
            {
                *pLeft++ ^= *pRight++;
            }
        }

        leftFrame.UnlockBits(leftData);
        rightFrame.UnlockBits(rightData);
    }

You can use this procedure on both sides in following way:
On server side you need to evaluate difference between old and new frame, send it to client and replace old frame by new. The server code should look something like this:

  XorFrames(oldFrame, newFrame); // oldFrame ^= newFrame
  Send(oldFrame); // send XOR of two frames
  oldFrame = newFrame;

On client side you need to update your current frame with xor frame recieved from server:

  XorFrames((Bitmap)uxSurface.Image, e.Frame);

这篇关于不安全的每像素的访问,为1756000像素30毫秒访问的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆