我的图像是显示在pictureBox中的,现在的要求是实现图像自底向上平滑的滚动,我能够实现,算法都写好了。但是CPU利用率太高了,而且屏幕有抖动的现象(提高刷新频率后,抖动可以解决)
具体描述:
有一个取数的线程,实时采集图像数据,然后将数据一行行显示出来,第一行显示在控件的第一行上,第二行显示在控件的第二行上,以此类推。当显示的行数到达控件的高度时,将控件中的图像数据向上平移一行,新的一行数据显示在控件的最低下一行上。这样就形成了滚动效果,问题是:每次往控件上写数据,都要刷新,pictureBox.refresh();这太消耗CPU了,几乎就死机了。我的图像基本上10000行。希望我们可以进行交流,您的回复是我前进的指南,我已经郁闷了好长时间了。

解决方案 »

  1.   

    你在timer中改变picturebox的位置不行么?
      

  2.   

    感谢楼上各位的留言,我做的程序是winform的。
    改变pictureBox位置的方法不太好,图像有抖动的效果,而且没改变一次位置,控件就会刷新一次,这样消耗CPU,我的数据采集效率是每1ms 返回一行数据,如果是刷新空间的话,就是1ms刷新一次CScrollView如何来做?
    我把源代码贴出来吧。这是我写的测试程序,可能有些地方地方不太健全。
    //读取一张bmp文件,然后将它滚动显示。图像高度一般大于1000行,至少400行,要不看不出效果。
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;using System.IO;using System.Drawing;namespace ScrollImage
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }        private static byte[] bmpData = null;
            private static Bitmap bm;
            private static int width = 0;
            private static int height = 0;
            private static System.Drawing.Imaging.ColorPalette colorPalette;
            private void button1_Click(object sender, EventArgs e)
            {
                colorPalette = (new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed)).Palette;
                for (int i = 0; i < 256; i++)
                {
                    colorPalette.Entries[i] = Color.FromArgb(255, i, i, i);
                }
                bmpData = readDataFormImg(ref bm, ref width, ref height);            bm = new Bitmap(width, 400, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                bm.Palette = colorPalette;
                pictureBox1.Image = bm;
            }        public static byte[] readDataFormImg(ref Bitmap bm, ref int w, ref int h)
            {
                byte[] data = null;
                string bmpPath = "";
                OpenFileDialog f = new OpenFileDialog();
                f.Filter = "(bmp文件)*.bmp|*.bmp";
                if (f.ShowDialog() == DialogResult.OK)
                {
                    bmpPath = f.FileName;
                    FileStream fs = File.OpenRead(bmpPath);
                    bm = (Bitmap)Bitmap.FromStream(fs);
                    w = bm.Width;
                    h = bm.Height;
                    data = new byte[w * h];
                    fs.Read(data, 0, w * h);
                    fs.Close();
                }
                return data;
            }
            /* 算法思想:
             * 要形成滚动时,程序在屏幕上逐一显示读入的各行。
             * 因此,读入的第一行显示在屏幕的第一行上,
             * 读入的第二行直接在下面显示在屏幕的第二行上,
             * 以此类推。当程序到达屏幕的最底下一行,
             * 整个屏幕的内容随每一读入的行向上移动,
             * 新行显示在屏幕的最底下一行。由此,形成平滑的滚动。 
             * 该算法在消息事件中调用。
             * Author:NIESS 06/03/2009
             */
    //高度我设定为400 
            private void button2_Click(object sender, EventArgs e)
            {
                totalData = new byte[width * height];
                Rectangle rec = new Rectangle(0, 0, width, 400);
                for (int i = 0; i < height; i++)
                {
                    byte[] temp = new byte[width];
                    for (int j = 0; j < width; j++)
                    {
                        temp[j] = bmpData[i * width + j];
                    }
                    temp.CopyTo(totalData, i * width);
                    System.Drawing.Imaging.BitmapData bitData = bm.LockBits(
                    rec, System.Drawing.Imaging.ImageLockMode.ReadWrite, bm.PixelFormat);
                    try
                    {
                        if (i >= 400)
                        {
                            byte[] dd = new byte[(400 - 1) * width];
                            System.Runtime.InteropServices.Marshal.Copy(totalData, (i - 400) * width, bitData.Scan0,
                                400 * width);
                        }
                        else
                        {
                            IntPtr ptr = (IntPtr)(bitData.Scan0.ToInt32() + i * width);
                            System.Runtime.InteropServices.Marshal.Copy(temp, 0, ptr, (width * 1));
                        }
                    }
                    finally
                    {
                        bm.UnlockBits(bitData);
                    }
    //在这个地方,如果加上pictureBox1.refresh();的话,CPU使用率80%;不加,cpu使用率仅仅8%
    // 但是如果不是用刷新的话,看不出图像是在变化的呀,
                }
            }
            private static byte[] totalData = null;
            private void Form1_Load(object sender, EventArgs e)
            {
                System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
                
            }    }
    }
      

  3.   

    感谢楼上各位的留言,我做的程序是winform的。
    改变pictureBox位置的方法不太好,图像有抖动的效果,而且没改变一次位置,控件就会刷新一次,这样消耗CPU,我的数据采集效率是每1ms 返回一行数据,如果是刷新空间的话,就是1ms刷新一次CScrollView如何来做?
    我把源代码贴出来吧。这是我写的测试程序,可能有些地方地方不太健全。
    //读取一张bmp文件,然后将它滚动显示。图像高度一般大于1000行,至少400行,要不看不出效果。
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;using System.IO;using System.Drawing;namespace ScrollImage
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }        private static byte[] bmpData = null;
            private static Bitmap bm;
            private static int width = 0;
            private static int height = 0;
            private static System.Drawing.Imaging.ColorPalette colorPalette;
            private void button1_Click(object sender, EventArgs e)
            {
                colorPalette = (new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed)).Palette;
                for (int i = 0; i < 256; i++)
                {
                    colorPalette.Entries[i] = Color.FromArgb(255, i, i, i);
                }
                bmpData = readDataFormImg(ref bm, ref width, ref height);            bm = new Bitmap(width, 400, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                bm.Palette = colorPalette;
                pictureBox1.Image = bm;
            }        public static byte[] readDataFormImg(ref Bitmap bm, ref int w, ref int h)
            {
                byte[] data = null;
                string bmpPath = "";
                OpenFileDialog f = new OpenFileDialog();
                f.Filter = "(bmp文件)*.bmp|*.bmp";
                if (f.ShowDialog() == DialogResult.OK)
                {
                    bmpPath = f.FileName;
                    FileStream fs = File.OpenRead(bmpPath);
                    bm = (Bitmap)Bitmap.FromStream(fs);
                    w = bm.Width;
                    h = bm.Height;
                    data = new byte[w * h];
                    fs.Read(data, 0, w * h);
                    fs.Close();
                }
                return data;
            }
            /* 算法思想:
             * 要形成滚动时,程序在屏幕上逐一显示读入的各行。
             * 因此,读入的第一行显示在屏幕的第一行上,
             * 读入的第二行直接在下面显示在屏幕的第二行上,
             * 以此类推。当程序到达屏幕的最底下一行,
             * 整个屏幕的内容随每一读入的行向上移动,
             * 新行显示在屏幕的最底下一行。由此,形成平滑的滚动。 
             * 该算法在消息事件中调用。
             * Author:NIESS 06/03/2009
             */
    //高度我设定为400 
            private void button2_Click(object sender, EventArgs e)
            {
                totalData = new byte[width * height];
                Rectangle rec = new Rectangle(0, 0, width, 400);
                for (int i = 0; i < height; i++)
                {
                    byte[] temp = new byte[width];
                    for (int j = 0; j < width; j++)
                    {
                        temp[j] = bmpData[i * width + j];
                    }
                    temp.CopyTo(totalData, i * width);
                    System.Drawing.Imaging.BitmapData bitData = bm.LockBits(
                    rec, System.Drawing.Imaging.ImageLockMode.ReadWrite, bm.PixelFormat);
                    try
                    {
                        if (i >= 400)
                        {
                            byte[] dd = new byte[(400 - 1) * width];
                            System.Runtime.InteropServices.Marshal.Copy(totalData, (i - 400) * width, bitData.Scan0,
                                400 * width);
                        }
                        else
                        {
                            IntPtr ptr = (IntPtr)(bitData.Scan0.ToInt32() + i * width);
                            System.Runtime.InteropServices.Marshal.Copy(temp, 0, ptr, (width * 1));
                        }
                    }
                    finally
                    {
                        bm.UnlockBits(bitData);
                    }
    //在这个地方,如果加上pictureBox1.refresh();的话,CPU使用率80%;不加,cpu使用率仅仅8%
    // 但是如果不是用刷新的话,看不出图像是在变化的呀,
                }
            }
            private static byte[] totalData = null;
            private void Form1_Load(object sender, EventArgs e)
            {
                System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
                
            }    }
    }
      

  4.   

    感谢楼上各位的留言,我做的程序是winform的。
    改变pictureBox位置的方法不太好,图像有抖动的效果,而且没改变一次位置,控件就会刷新一次,这样消耗CPU,我的数据采集效率是每1ms 返回一行数据,如果是刷新空间的话,就是1ms刷新一次CScrollView如何来做?
    我把源代码贴出来吧。这是我写的测试程序,可能有些地方地方不太健全。
    //读取一张bmp文件,然后将它滚动显示。图像高度一般大于1000行,至少400行,要不看不出效果。
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    using System.Threading;using System.IO;using System.Drawing;namespace ScrollImage
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }        private static byte[] bmpData = null;
            private static Bitmap bm;
            private static int width = 0;
            private static int height = 0;
            private static System.Drawing.Imaging.ColorPalette colorPalette;
            private void button1_Click(object sender, EventArgs e)
            {
                colorPalette = (new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed)).Palette;
                for (int i = 0; i < 256; i++)
                {
                    colorPalette.Entries[i] = Color.FromArgb(255, i, i, i);
                }
                bmpData = readDataFormImg(ref bm, ref width, ref height);            bm = new Bitmap(width, 400, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                bm.Palette = colorPalette;
                pictureBox1.Image = bm;
            }        public static byte[] readDataFormImg(ref Bitmap bm, ref int w, ref int h)
            {
                byte[] data = null;
                string bmpPath = "";
                OpenFileDialog f = new OpenFileDialog();
                f.Filter = "(bmp文件)*.bmp|*.bmp";
                if (f.ShowDialog() == DialogResult.OK)
                {
                    bmpPath = f.FileName;
                    FileStream fs = File.OpenRead(bmpPath);
                    bm = (Bitmap)Bitmap.FromStream(fs);
                    w = bm.Width;
                    h = bm.Height;
                    data = new byte[w * h];
                    fs.Read(data, 0, w * h);
                    fs.Close();
                }
                return data;
            }
            /* 算法思想:
             * 要形成滚动时,程序在屏幕上逐一显示读入的各行。
             * 因此,读入的第一行显示在屏幕的第一行上,
             * 读入的第二行直接在下面显示在屏幕的第二行上,
             * 以此类推。当程序到达屏幕的最底下一行,
             * 整个屏幕的内容随每一读入的行向上移动,
             * 新行显示在屏幕的最底下一行。由此,形成平滑的滚动。 
             * 该算法在消息事件中调用。
             * Author:NIESS 06/03/2009
             */
    //高度我设定为400 
            private void button2_Click(object sender, EventArgs e)
            {
                totalData = new byte[width * height];
                Rectangle rec = new Rectangle(0, 0, width, 400);
                for (int i = 0; i < height; i++)
                {
                    byte[] temp = new byte[width];
                    for (int j = 0; j < width; j++)
                    {
                        temp[j] = bmpData[i * width + j];
                    }
                    temp.CopyTo(totalData, i * width);
                    System.Drawing.Imaging.BitmapData bitData = bm.LockBits(
                    rec, System.Drawing.Imaging.ImageLockMode.ReadWrite, bm.PixelFormat);
                    try
                    {
                        if (i >= 400)
                        {
                            byte[] dd = new byte[(400 - 1) * width];
                            System.Runtime.InteropServices.Marshal.Copy(totalData, (i - 400) * width, bitData.Scan0,
                                400 * width);
                        }
                        else
                        {
                            IntPtr ptr = (IntPtr)(bitData.Scan0.ToInt32() + i * width);
                            System.Runtime.InteropServices.Marshal.Copy(temp, 0, ptr, (width * 1));
                        }
                    }
                    finally
                    {
                        bm.UnlockBits(bitData);
                    }
    //在这个地方,如果加上pictureBox1.refresh();的话,CPU使用率80%;不加,cpu使用率仅仅8%
    // 但是如果不是用刷新的话,看不出图像是在变化的呀,
                }
            }
            private static byte[] totalData = null;
            private void Form1_Load(object sender, EventArgs e)
            {
                System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
                
            }    }
    }
      

  5.   

    其实数据多不是显示慢的原因,主要是看你的算法。
    你只需要画出需要画的部分而不是整个显示出来的内容。你可能需要用到双缓冲,对窗体来说可以方便的通过属性来实现:Control.DoubleBuffered 属性 
    在绘制的时候需要使用OnPaint的参数e.Graphics,需要更新显示内容的时候调用Control.Invalidate(Rectangle)。最主要的是要使用的利用PaintEventArgs.ClipRectangle 属性,这个属性是标识要绘制的最小矩形区域。必要的时候还可以使用ScrollWindow函数,参考MSDN的双缓冲图形