我的程序中需要一个把图像灰度化的功能,我利用网上提供的算法自己做了一个,不过速度非常慢,处理一个2560 X 1920像素的jpg图像大约需要25秒,因为对速度不满,我在网上又找了一个图像处理控件(估计是C++做的),利用这个控件处理同样的图像只需要不到1秒钟,差距实在太大了,我想请问是不是C#确实存在这个问题(处理图像速度非常慢),还是因为我的算法有问题?
算法如下:
Bitmap bm = image;for(int y=0;y<bm.Height;y++)
{
         for(int x=0;x<bm.Width;x++)
{
Color c = bm.GetPixel(x,y);
int luma = (int)(c.R*0.3 + c.G*0.59+ c.B*0.11);
bm.SetPixel(x,y,Color.FromArgb(luma,luma,luma));
}
}

解决方案 »

  1.   

    C#不可能比C++快,算法只是提高点速度,但还是没有C++快,随便你怎么优化算法,C++比C#快40%
    特别是在图像处理,和大型计算方面
      

  2.   

    C#做图形图像是比较慢,因为你不知道C#中GetPixel,Color.FromArgb等等函数底层是如何实现的,也就无法评测时间.楼主的算法是逐像素点进行灰度化,其实是没必要的.可以设置若干阈值,按阈值对像素点进行融合,然后对融合后的像素区域进行整体的灰度化.
      

  3.   

    GetPixel和SetPixel本身就非常慢,加上你又是一个嵌套循环,更不用说了...最基本的优化:可以直接操作图片颜色数组,改为单层循环.
      

  4.   

    多谢各位,我又找到了一种方法,就是使用GDI+来处理,效果很明显,同样的图片处理速度大约只需要3秒钟,远远快于最初直接逐个处理像素的方法(25秒),不过还是没有达到采用图像处理控件的速度(不到1秒)。我估计那个图像控件可能采用了指针来直接在内存中操作,我查到网上有在C#中使用指针来处理图像的方法,不过同时又说在C#中使用指针是不安全(unsafe)的方法,因为它可能指向其他有用的内存地址。我想请问各位大虾是不是这样?
      

  5.   

    用下面的算法BitmapData 不是很慢 应该在0.2秒图像要有Alpha通道         public Bitmap ColorFade(Bitmap source)
            {
                // 基本数据
                int width = source.Width;
               int height = source.Height;
               Rectangle rect = new Rectangle(0, 0, width, height);
               PixelFormat format = source.PixelFormat;
                // 图象结果
                Bitmap result = new Bitmap(width, height, format);
                // 图像数据
                BitmapData sourceData = source.LockBits(rect, ImageLockMode.ReadOnly, format);
                BitmapData resultData = result.LockBits(rect, ImageLockMode.WriteOnly, format);
                // 处理图像数据
                unsafe
                {
                    byte* sourcePtr = (byte*)(sourceData.Scan0);
                    byte* resultPtr = (byte*)(resultData.Scan0);                for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            // 计算明度
                            byte min = sourcePtr[0]; byte max = sourcePtr[1];
                            if (min > max) { min = sourcePtr[1]; max = sourcePtr[0]; }
                            if (max < sourcePtr[2]) { max = sourcePtr[2]; }
                            if (min > sourcePtr[2]) { min = sourcePtr[2]; }
                            float light = ((float)min + (float)max) * 0.5f;
                            // 处理数据
                            resultPtr[0] = (byte)(sourcePtr[0] * light);
                            resultPtr[1] = (byte)(sourcePtr[1] * light);
                            resultPtr[2] = (byte)(sourcePtr[2] * light);
                            // 更新指针
                            sourcePtr += 4;  // 包含Alpha通道所以为4 否则用3
                         resultPtr += 4;
                        }
                        sourcePtr += sourceData.Stride - width * 4;
                        resultPtr += sourceData.Stride - width * 4;
                    }
                }
                // 解锁
                source.UnlockBits(sourceData);
                result.UnlockBits(resultData);
                // 返回结果
                return result;
            }
      

  6.   

    上面的算法并不符合你的要求,用这个        /// <summary>图象灰度化</summary>
            /// <returns>图象</returns>
            public Bitmap ColorGray()
            {
                // 基本数据
                int width = this._source.Width;
                int height = this._source.Height;
                Rectangle rect = new Rectangle(0, 0, width, height);
                PixelFormat format = this._source.PixelFormat;
                // 图象结果
                Bitmap result = new Bitmap(width, height, format);
                // 图像数据
                BitmapData sourceData = this._source.LockBits(rect, ImageLockMode.ReadOnly, format);
                BitmapData resultData = result.LockBits(rect, ImageLockMode.WriteOnly, format);
                // 处理图像数据
                unsafe
                {
                    byte* sourcePtr = (byte*)(sourceData.Scan0);
                    byte* resultPtr = (byte*)(resultData.Scan0);                for (int i = 0; i < height; i++)
                    {
                        for (int j = 0; j < width; j++)
                        {
                            // 计算明度
                            byte luma = (byte)(sourcePtr[2] * 0.3 + sourcePtr[1] * 0.59 + sourcePtr[0] * 0.11); 
                            // 处理数据
                            resultPtr[0] = luma;
                            resultPtr[1] = luma;
                            resultPtr[2] = luma;
                            resultPtr[3] = sourcePtr[3];
                            // 更新指针
                            sourcePtr += 4;
                            resultPtr += 4;
                        }
                        sourcePtr += sourceData.Stride - width * 4;
                        resultPtr += sourceData.Stride - width * 4;
                    }
                }
                // 解锁
                this._source.UnlockBits(sourceData);
                result.UnlockBits(resultData);
                // 返回结果
                return result;
            }
      

  7.   

    将 for 循环修改为如下还可以加快 30-40% 速度for (int i = 0; i < height; i++)
    {
        for (int j = 0; j < width; j++)
        {
            // 计算明度
            byte luma = (byte)(((int)sourcePtr[2] * 77 + (int)sourcePtr[1] * 151 + (int)sourcePtr[0] * 28) >> 8);
            // 处理数据
            resultPtr[0] = luma;
            resultPtr[1] = luma;
            resultPtr[2] = luma;
            resultPtr[3] = sourcePtr[3];
            // 更新指针
            sourcePtr += 4;
            resultPtr += 4;
        }
    }
      

  8.   

    BitmapData tempBitmapData = bm.LockBits(new Rectangle(new Point(0, 0), new Size(bm.Width, bm.Height)), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                    IntPtr iPtr = tempBitmapData.Scan0;
                    int iStride = tempBitmapData.Stride;
                    int byteslength = iStride * img.Height;
                    byte[] inputbyte = new byte[byteslength];
                    System.Runtime.InteropServices.Marshal.Copy(iPtr, inputbyte, 0, byteslength);
                    for (int i = 0; i < byteslength; i += 3)
                    {
                        byte temp = (int)(inputbyte[i]*0.3+inputbyte[i + 1];*0.59+inputbyte[i + 2]*0.11); 
                        inputbyte[i] = temp
                        inputbyte[i + 1] = temp
                        inputbyte[i + 2] = temp;
                    }
                    System.Runtime.InteropServices.Marshal.Copy(inputbyte, 0, iPtr, byteslength);
                    img.UnlockBits(tempBitmapData);
    这个应该快!
      

  9.   

    同理:public static byte[] GetBuffers(Bitmap srcImage)
            {
                unsafe
                {
                    int w = srcImage.Width;
                    int h = srcImage.Height;
                    Rectangle Rect = new Rectangle(0, 0, w, h);                BitmapData dat = srcImage.LockBits(Rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    byte[] buffers = new byte[w * h];
                    byte* srcp = (byte*)dat.Scan0.ToPointer();
                    int ofs = dat.Stride - 3 * dat.Width;
                    for (int i = 0; i < h; i++)
                    {
                        for (int j = 0; j < w; j++)
                        {
                            buffers[i * w + j] = (byte)(((*srcp++) + (*srcp++) + (*srcp++)) / 3);
                        }
                        srcp += ofs;
                    }
                    srcImage.UnlockBits(dat);
                    return buffers;
                }
            }        public static byte[] GetRGBBuffers(Bitmap srcImage)
            {
                unsafe
                {
                    int w = srcImage.Width;
                    int h = srcImage.Height;
                    Rectangle Rect = new Rectangle(0, 0, w, h);                BitmapData dat = srcImage.LockBits(Rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
                    byte[] buffers = new byte[w * h * 3];
                    byte* srcp = (byte*)dat.Scan0.ToPointer();
                    int ofs = dat.Stride - 3 * dat.Width;
                    int index = 0;
                    for (int i = 0; i < h; i++)
                    {
                        for (int j = 0; j < w; j++)
                        {
                            buffers[index ++] = *srcp++;
                            buffers[index ++] = *srcp++;
                            buffers[index ++] = *srcp++;
                        }
                        srcp += ofs;
                    }
                    srcImage.UnlockBits(dat);
                    return buffers;
                }
            }        public static Bitmap GetRGBBitmap(byte[] buffers, int w, int h)
            {
                unsafe
                {
                    byte b;
                    Bitmap tbmp = new Bitmap(w, h);
                    Rectangle Rect = new Rectangle(0, 0, w, h);
                    BitmapData dat = tbmp.LockBits(Rect, ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                    byte* p = (byte*)dat.Scan0.ToPointer();
                    int ofs = dat.Stride - 3 * dat.Width;                int index = 0;
                    for (int i = 0; i < h; i++)
                    {
                        for (int j = 0; j < w; j++)
                        {
                            *(p++) = buffers[index ++];
                            *(p++) = buffers[index ++];
                            *(p++) = buffers[index ++];
                        }
                        p += ofs;
                    }
                    tbmp.UnlockBits(dat);
                    return tbmp;
                }
            }
      

  10.   

    楼上的还漏了一个offest,因为大的图片在扫描时,扫描宽度一般都不等于width*height*N