c# 视觉的图像处理

2014-03-14 13:06:37 whw8007 阅读数 1621
  • 课程介绍

    基本掌握C#Winform自带的RDLC报表的设计方法 基本掌握C#Winform自带的RDLC报表的使用技巧

    0课时 0分钟 266人学习 武占文
    免费试看

作者:xiaotie

转自:http://www.cnblogs.com/xiaotie/archive/2010/03/08/1680662.html


前些天阅读《各种图像处理类库的比较及选择(The Comparison of Image Processing Libraries)》,对后面的比较结果感觉怪异。对计算密集型运算,C#和C/C++的性能应该差别不大才是。为了探讨问题,做了以下实验。

本实验比较了五种方式进行图像灰度化计算:

(1)EmguCV实现,见 《各种图像处理类库的比较及选择(The Comparison of Image Processing Libraries)》 文中代码

(2)OpenCV/PInvoke实现,见 《各种图像处理类库的比较及选择(The Comparison of Image Processing Libraries)》 文中代码

(3)BitmapData实现,见 《各种图像处理类库的比较及选择(The Comparison of Image Processing Libraries)》 文中代码

(4)Array实现(ArgbImage8),核心代码如下:

(每一个)ImageChannel8 内含1个Byte数组Data。GrayscaleImage8  继承自 ImageChannel8 。

    public class ArgbImage8 : ImageChannelSet8
    {
        public ImageChannel8 A { get { return this.Channels[0]; } }
        public ImageChannel8 R { get { return this.Channels[0]; } }
        public ImageChannel8 G { get { return this.Channels[0]; } }
        public ImageChannel8 B { get { return this.Channels[0]; } }

        public ArgbImage8(int width, int height)
            : base(4, width, height)
        {
        }

        public GrayscaleImage8 ToGrayscaleImage()
        {
            return ToGrayscaleImage(0.299, 0.587, 0.114);
        }

        public GrayscaleImage8 ToGrayscaleImage(double rCoeff, double gCoeff, double bCoeff)
        {
            GrayscaleImage8 img = new GrayscaleImage8(this.Width, this.Height);
            Byte[] r = R.Data;
            Byte[] g = G.Data;
            Byte[] b = B.Data;
            Byte[] dst = img.Data;

            for (int i = 0; i < r.Length; i++)
            {
                dst[i] = (Byte)(r[i] * rCoeff + g[i] * gCoeff + b[i] * bCoeff);
            }

            return img;
        }

        //性能低下,先这样写了
        public static ArgbImage8 CreateFromBitmap(Bitmap map)
        {
            if (map == null) throw new ArgumentNullException("map");

            ArgbImage8 img = new ArgbImage8(map.Width, map.Height);
            Byte[] a = img.A.Data;
            Byte[] r = img.R.Data;
            Byte[] g = img.G.Data;
            Byte[] b = img.B.Data;

            for (int row = 0; row < img.Height; row++)
            {
                for (int col = 0; col < img.Width; col++)
                {
                    int index = row * img.Width + col;
                    Color c = map.GetPixel(col, row);
                    a[index] = c.A;
                    r[index] = c.R;
                    r[index] = c.R;
                    r[index] = c.R;
                }
            }

            return img;
        }
    }

(5)C# 指针/unsafe 实现(ArgbImage32 ),核心代码如下:

    public class UnmanagedMemory<T> : IDisposable
        where T : struct
    {
        public Int32 ByteCount { get; private set; }
        public Int32 Length { get; private set; }
        public IntPtr Start { get; private set; }
        public Int32 SizeOfType { get; private set; }

        public UnmanagedMemory(Int32 length)
        {
            Length = length;
            SizeOfType = SizeOfT();
            ByteCount = SizeOfType * length;
            Start = Marshal.AllocHGlobal(ByteCount);
        }

        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (false == disposed)
            {
                 disposed = true;
                 Marshal.FreeHGlobal(Start);
            }
        }

        private bool disposed;

        ~UnmanagedMemory()
        {
            Dispose(false);
        }

        private Int32 SizeOfT()
        {
            return Marshal.SizeOf(typeof(T));
        }
    }

    public struct Argb32
    {
        public Byte Alpha;
        public Byte Red;
        public Byte Green;
        public Byte Blue;
    }

    public class Argb32Image : UnmanagedMemory<Argb32>
    {
        private unsafe Argb32* m_pointer;

        public unsafe Argb32* Pointer { get { return m_pointer; } }

        public unsafe Argb32Image(int length)
            : base(length)
        {
            m_pointer = (Argb32*)this.Start;
        }

        public unsafe Argb32 this[int index]
        {
            get { return *(m_pointer + index); }
            set { *(m_pointer + index) = value; }
        }

        public Grayscale8Image ToGrayscaleImage()
        {
            return ToGrayscaleImage(0.299, 0.587, 0.114);
        }

        public unsafe Grayscale8Image ToGrayscaleImage(double rCoeff, double gCoeff, double bCoeff)
        {
            Grayscale8Image img = new Grayscale8Image(this.Length);
            Argb32* p = Pointer;
            Byte* to = img.Pointer;
            Argb32* end = p + Length;

            while (p != end)
            {
                *to = (Byte)(p->Red * rCoeff + p->Green * gCoeff + p->Blue * bCoeff);
                p++;
                to++;
            }
            return img;
        }

        public unsafe static Argb32Image CreateFromBitmap(Bitmap map)
        {
            if (map == null) throw new ArgumentNullException("map");

            Argb32Image img = new Argb32Image(map.Width*map.Height);

            Argb32* p = img.Pointer;

            for (int row = 0; row < map.Height; row++)
            {
                for (int col = 0; col < map.Width; col++)
                {
                    Color c = map.GetPixel(col, row);
                    p->Alpha = c.A;
                    p->Red = c.R;
                    p->Green = c.G;
                    p->Blue = c.B;
                    p++;
                }
            }

            return img;
        }
    }

机器配置:

image

在每个方法测试前,均运行一段DoSomething()清空高速缓存:

private static int[] DoSomething()
        {
            int[] data = new Int32[20000000];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = i;
            }
            return data;
        }

测试结果(每个执行5次,计算耗时总和。单位ms):

图像1——

BitmapData:53
ArgbImage8:80
ArgbImage32:38
EmguCV:68
OpenCV:69

图像2——

BitmapData:25
ArgbImage8:45
ArgbImage32:19
EmguCV:42
OpenCV:45

图像3——

BitmapData:8
ArgbImage8:25
ArgbImage32:6
EmguCV:23
OpenCV:24

图像4——

BitmapData:48
ArgbImage8:76
ArgbImage32:39
EmguCV:67
OpenCV:69

图像5(大图:5000×6000)——

BitmapData:1584
ArgbImage8:1991
ArgbImage32:1229
EmguCV:1545
OpenCV:2817

下面删去ArgbImage8,仅比较剩下的4种(每个执行5次,计算耗时总和。单位ms):

图像6——

BitmapData:17
ArgbImage32:10
EmguCV:25
OpenCV:25

图像7——

BitmapData:88
ArgbImage32:56
EmguCV:69
OpenCV:70

图像8——

BitmapData:41
ArgbImage32:25
EmguCV:40
OpenCV:43

图像5(大图:5000×6000)——

BitmapData:2855
ArgbImage32:1849
EmguCV:1578
OpenCV:2522

下面,把执行顺序颠倒一下,让EmguCV和OpenCV在前面。剩下的2个在后面:

图像8——

EmguCV:41
OpenCV:42
BitmapData:38
ArgbImage32:26

图像9——

EmguCV:32
OpenCV:34
BitmapData:28
ArgbImage32:18

好了,不做试验了。根据上面结果,再考虑到纯C/C++程序比P/Invoke程序性能高一些,可得出这样的结论(在我的机器上):

(1)C#不直接用指针比P/Invoke 的 C/C++程序低效一些。

(2)C#直接用指针,可以写出非常高效的程序,至少比P/Invoke高效。从上面的代码可看出,C#下指针用很舒服,并且编译快。猜想:C#下玩指针+Struct,和C没啥区别。图像处理这样的基本类型简单的程序,非常适合用C#编写。大量用指针,大量用非托管内存,可以最大化性能,最小化内存占用,最小化GC对程序的影响,达到和C/C++所差无几的性能。

下面尝试直接使用硬件。对图像处理加速最有效果的是GPU,好吧,下面就尝试调用GPU的功能。

如何在无界面的情况下调用GPU呢?

下面是我写的一个测试程序(需要引用XNA):

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;

namespace Orc.SmartImage.Xna
{
    public class Shader
    {
        private class GameHelper : Game
        {
            public void Init()
            {
                this.Initialize();
                GraphicsDeviceManager m = new GraphicsDeviceManager(this);
                m.ApplyChanges();
            }
        }

        private GameHelper m_helper;

        public GraphicsDevice GraphicsDevice { get; set; }

        public Shader(IntPtr hwnd)
        {
            m_helper = new GameHelper();
            m_helper.Init();
            this.GraphicsDevice = m_helper.GraphicsDevice;
        }

        public void Test()
        {
            RenderTarget2D tar = new RenderTarget2D(this.GraphicsDevice, 100, 100, 1, SurfaceFormat.Color);
            this.GraphicsDevice.SetRenderTarget(0, tar);
            this.GraphicsDevice.Clear(Color.Yellow);
            this.GraphicsDevice.SetRenderTarget(0, null);
            Texture2D txt = tar.GetTexture();
            uint[] data = new uint[10000];
            txt.GetData(data);

            return;
        }
    }
}

 

进一步就是写HLSL了。

 

============================

离C/C++又远了一步。

附:具体测试代码

(注:那个Shader是我测试GPU计算能否通过的部分。IntPtr hwnd是因为GraphicsDevice构造函数中有这样一个参数,不过后来,我绕了过去,但测试程序这里我没删掉,还留在这里。)

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using Orc.SmartImage;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.CvEnum;
using Orc.SmartImage.Gpu;
using Orc.SmartImage.UnmanagedObjects;

namespace Orc.SmartImage.PerformanceTest
{
    public class PerformanceTestCase0
    {
        public static String Test(IntPtr hwnd, Bitmap src, int count)
        {
            Shader sd = new Shader(hwnd);

//            ArgbImage8 img8 = ArgbImage8.CreateFromBitmap(src);
            Argb32Image img32 = Argb32Image.CreateFromBitmap(src);

            StringBuilder sb = new StringBuilder();
            Stopwatch sw = new Stopwatch();

            DoSomething();

            sw.Reset();
            sw.Start();
            for (int i = 0; i < count; i++)
                ProcessImageWithEmgucv(src);
            sw.Stop();
            sb.AppendLine("EmguCV:" + sw.ElapsedMilliseconds.ToString());

            DoSomething();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < count; i++)
                ProcessImageWithOpencv(src);
            sw.Stop();
            sb.AppendLine("OpenCV:" + sw.ElapsedMilliseconds.ToString());

            DoSomething();

            sw.Reset();
            sw.Start();
            for (int i = 0; i < count; i++)
                Grayscale(src);
            sw.Stop();
            sb.AppendLine("BitmapData:" + sw.ElapsedMilliseconds.ToString());

            //DoSomething();
            //sw.Reset();
            //sw.Start();
            //for (int i = 0; i < count; i++)
            //    img8.ToGrayscaleImage();
            //sw.Stop();
            //sb.AppendLine("ArgbImage8:" + sw.ElapsedMilliseconds.ToString());

            DoSomething();
            sw.Reset();
            sw.Start();
            for (int i = 0; i < count; i++)
                img32.ToGrayscaleImage();
            sw.Stop();
            sb.AppendLine("ArgbImage32:" + sw.ElapsedMilliseconds.ToString());

           

            //sw.Reset();
            //sw.Start();
            //for (int i = 0; i < count; i++)
            //    img8.ToGrayscaleImage();
            //sw.Stop();
            //sb.AppendLine("ArgbImage8:" + sw.ElapsedMilliseconds.ToString());

            

            return sb.ToString();
        }

        private static int[] DoSomething()
        {
            int[] data = new Int32[20000000];
            for (int i = 0; i < data.Length; i++)
            {
                data[i] = i;
            }
            return data;
        }

        private static GrayscaleImage TestMyConvert(ArgbImage img)
        {
            return img.ToGrayscaleImage();
        }

        /// <summary>
        /// 使用EmguCv处理图像
        /// </summary>
        private static void ProcessImageWithEmgucv(Bitmap bitmapSource)
        {
            //灰度
            Image<Bgr, Byte> imageSource = new Image<Bgr, byte>(bitmapSource);
            Image<Gray, Byte> imageGrayscale = imageSource.Convert<Gray, Byte>();
        }

        /// <summary>
        /// 使用Open Cv P/Invoke处理图像
        /// </summary>
        unsafe private static void ProcessImageWithOpencv(Bitmap bitmapSource)
        {
            Image<Bgr, Byte> imageSource = new Image<Bgr, byte>(bitmapSource);
            IntPtr ptrSource = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MIplImage)));
            Marshal.StructureToPtr(imageSource.MIplImage, ptrSource, true);
            IntPtr ptrGrayscale = CvInvoke.cvCreateImage(imageSource.Size, IPL_DEPTH.IPL_DEPTH_8U, 1);
            CvInvoke.cvCvtColor(ptrSource, ptrGrayscale, COLOR_CONVERSION.CV_BGR2GRAY);
        }



        /// <summary>
        /// 将指定图像转换成灰度图
        /// </summary>
        /// <param name="bitmapSource">源图像支持3通道或者4通道图像,支持Format24bppRgb、Format32bppRgb和Format32bppArgb这3种像素格式</param>
        /// <returns>返回灰度图,如果转化失败,返回null。</returns>
        private static Bitmap Grayscale(Bitmap bitmapSource)
        {
            Bitmap bitmapGrayscale = null;
            if (bitmapSource != null && (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb || bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb))
            {
                int width = bitmapSource.Width;
                int height = bitmapSource.Height;
                Rectangle rect = new Rectangle(0, 0, width, height);
                bitmapGrayscale = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
                //设置调色板
                ColorPalette palette = bitmapGrayscale.Palette;
                for (int i = 0; i < palette.Entries.Length; i++)
                    palette.Entries[i] = Color.FromArgb(255, i, i, i);
                bitmapGrayscale.Palette = palette;
                BitmapData dataSource = bitmapSource.LockBits(rect, ImageLockMode.ReadOnly, bitmapSource.PixelFormat);
                BitmapData dataGrayscale = bitmapGrayscale.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
                byte b, g, r;
                int strideSource = dataSource.Stride;
                int strideGrayscale = dataGrayscale.Stride;
                unsafe
                {
                    byte* ptrSource = (byte*)dataSource.Scan0.ToPointer();
                    byte* ptr1;
                    byte* ptrGrayscale = (byte*)dataGrayscale.Scan0.ToPointer();
                    byte* ptr2;
                    if (bitmapSource.PixelFormat == PixelFormat.Format24bppRgb)
                    {
                        for (int row = 0; row < height; row++)
                        {
                            ptr1 = ptrSource + strideSource * row;
                            ptr2 = ptrGrayscale + strideGrayscale * row;
                            for (int col = 0; col < width; col++)
                            {
                                b = *ptr1;
                                ptr1++;
                                g = *ptr1;
                                ptr1++;
                                r = *ptr1;
                                ptr1++;
                                *ptr2 = (byte)(0.114 * b + 0.587 * g + 0.299 * r);
                                ptr2++;
                            }
                        }
                    }
                    else    //bitmapSource.PixelFormat == PixelFormat.Format32bppArgb || bitmapSource.PixelFormat == PixelFormat.Format32bppRgb
                    {
                        for (int row = 0; row < height; row++)
                        {
                            ptr1 = ptrSource + strideGrayscale * row;
                            ptr2 = ptrGrayscale + strideGrayscale * row;
                            for (int col = 0; col < width; col++)
                            {
                                b = *ptr1;
                                ptr1++;
                                g = *ptr1;
                                ptr1++;
                                r = *ptr1;
                                ptr1 += 2;
                                *ptr2 = (byte)(0.114 * b + 0.587 * g + 0.299 * r);
                                ptr2++;
                            }
                        }
                    }
                }
                bitmapGrayscale.UnlockBits(dataGrayscale);
                bitmapSource.UnlockBits(dataSource);
            }
            return bitmapGrayscale;
        }

    }
}

2016-12-05 09:50:49 ryb666666 阅读数 1371
  • 课程介绍

    基本掌握C#Winform自带的RDLC报表的设计方法 基本掌握C#Winform自带的RDLC报表的使用技巧

    0课时 0分钟 266人学习 武占文
    免费试看

作者:彭军 http://pengjun.org.cn

这里之所以说“浅谈”是因为我这里只是简单的介绍如何使用Visual C#进行图像的读入、保存以及对像素的访问。而不涉及太多的算法。

一、读入图像

在Visual C#中我们可以使用一个Picture Box控件来显示图片,如下:
        private void btnOpenImage_Click(object sender, EventArgs e)
        {
            OpenFileDialog ofd = new OpenFileDialog();
            ofd.Filter = "BMP Files(*.bmp)|*.bmp|JPG Files(*.jpg;*.jpeg)|*.jpg;*.jpeg|All Files(*.*)|*.*";
            ofd.CheckFileExists = true;
            ofd.CheckPathExists = true;
            if (ofd.ShowDialog() == DialogResult.OK)
            {
                //pbxShowImage.ImageLocation = ofd.FileName;
                bmp = new Bitmap(ofd.FileName);
                if (bmp==null)
                {
                    MessageBox.Show("加载图片失败!", "错误");
                    return;
                }
                pbxShowImage.Image = bmp;
                ofd.Dispose();
            }
        }
其中bmp为类的一个对象:private Bitmap bmp=null;
在使用Bitmap类和BitmapData类之前,需要使用using System.Drawing.Imaging;
二、保存图像
        private void btnSaveImage_Click(object sender, EventArgs e)
        {
            if (bmp == null) return;

            SaveFileDialog sfd = new SaveFileDialog();
            sfd.Filter = "BMP Files(*.bmp)|*.bmp|JPG Files(*.jpg;*.jpeg)|*.jpg;*.jpeg|All Files(*.*)|*.*";
            if (sfd.ShowDialog() == DialogResult.OK)
            {
                pbxShowImage.Image.Save(sfd.FileName);
                MessageBox.Show("保存成功!","提示");
                sfd.Dispose();
            }
        }
三、对像素的访问
我们可以来建立一个GrayBitmapData类来做相关的处理。整个类的程序如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Windows.Forms;

namespace ImageElf
{
    class GrayBitmapData
    {
        public byte[,] Data;//保存像素矩阵
        public int Width;//图像的宽度
        public int Height;//图像的高度

        public GrayBitmapData()
        {
            this.Width = 0;
            this.Height = 0;
            this.Data = null;
        }

        public GrayBitmapData(Bitmap bmp)
        {
            BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
            this.Width = bmpData.Width;
            this.Height = bmpData.Height;
            Data = new byte[Height, Width];
            unsafe
            {
                byte* ptr = (byte*)bmpData.Scan0.ToPointer();
                for (int i = 0; i < Height; i++)
                {
                    for (int j = 0; j < Width; j++)
                    {
    //将24位的RGB彩色图转换为灰度图
                        int temp = (int)(0.114 * (*ptr++)) + (int)(0.587 * (*ptr++))+(int)(0.299 * (*ptr++));
                        Data[i, j] = (byte)temp;
                    }
                    ptr += bmpData.Stride - Width * 3;//指针加上填充的空白空间
                }
            }
            bmp.UnlockBits(bmpData);
        }

        public GrayBitmapData(string path)
            : this(new Bitmap(path))
        {
        }

        public Bitmap ToBitmap()
        {
            Bitmap bmp=new Bitmap(Width,Height,PixelFormat.Format24bppRgb);
            BitmapData bmpData=bmp.LockBits(new Rectangle(0,0,Width,Height),ImageLockMode.WriteOnly,PixelFormat.Format24bppRgb);
            unsafe
            {
                byte* ptr=(byte*)bmpData.Scan0.ToPointer();
                for(int i=0;i<Height;i++)
                {
                    for(int j=0;j<Width;j++)
                    {
                        *(ptr++)=Data[i,j];
                        *(ptr++)=Data[i,j];
                        *(ptr++)=Data[i,j];
                    }
                    ptr+=bmpData.Stride-Width*3;
                }
            }
            bmp.UnlockBits(bmpData);
            return bmp;
        }

        public void ShowImage(PictureBox pbx)
        {
            Bitmap b = this.ToBitmap();
            pbx.Image = b;
            //b.Dispose();
        }

        public void SaveImage(string path)
        {
            Bitmap b=ToBitmap();
            b.Save(path);
            //b.Dispose();
        }
//均值滤波
        public void AverageFilter(int windowSize)
        {
            if (windowSize % 2 == 0)
            {
                return;
            }

            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    int sum = 0;
                    for (int g = -(windowSize - 1) / 2; g <= (windowSize - 1) / 2; g++)
                    {
                        for (int k = -(windowSize - 1) / 2; k <= (windowSize - 1) / 2; k++)
                        {
                            int a = i + g, b = j + k;
                            if (a < 0) a = 0;
                            if (a > Height - 1) a = Height - 1;
                            if (b < 0) b = 0;
                            if (b > Width - 1) b = Width - 1;
                            sum += Data[a, b];
                        }
                    }
                    Data[i,j]=(byte)(sum/(windowSize*windowSize));
                }
            }
        }
//中值滤波
        public void MidFilter(int windowSize)
        {
            if (windowSize % 2 == 0)
            {
                return;
            }

            int[] temp = new int[windowSize * windowSize];
            byte[,] newdata = new byte[Height, Width];
            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    int n = 0;
                    for (int g = -(windowSize - 1) / 2; g <= (windowSize - 1) / 2; g++)
                    {
                        for (int k = -(windowSize - 1) / 2; k <= (windowSize - 1) / 2; k++)
                        {
                            int a = i + g, b = j + k;
                            if (a < 0) a = 0;
                            if (a > Height - 1) a = Height - 1;
                            if (b < 0) b = 0;
                            if (b > Width - 1) b = Width - 1;
                            temp[n++]= Data[a, b];
                        }
                    }
                    newdata[i, j] = GetMidValue(temp,windowSize*windowSize);
                }
            }

            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    Data[i, j] = newdata[i, j];
                }
            }
        }
//获得一个向量的中值
        private byte GetMidValue(int[] t, int length)
        {
            int temp = 0;
            for (int i = 0; i < length - 2; i++)
            {
                for (int j = i + 1; j < length - 1; j++)
                {
                    if (t[i] > t[j])
                    {
                        temp = t[i];
                        t[i] = t[j];
                        t[j] = temp;
                    }
                }
            }

            return (byte)t[(length - 1) / 2];
        }
//一种新的滤波方法,是亮的更亮、暗的更暗
        public void NewFilter(int windowSize)
        {
            if (windowSize % 2 == 0)
            {
                return;
            }

            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    int sum = 0;
                    for (int g = -(windowSize - 1) / 2; g <= (windowSize - 1) / 2; g++)
                    {
                        for (int k = -(windowSize - 1) / 2; k <= (windowSize - 1) / 2; k++)
                        {
                            int a = i + g, b = j + k;
                            if (a < 0) a = 0;
                            if (a > Height - 1) a = Height - 1;
                            if (b < 0) b = 0;
                            if (b > Width - 1) b = Width - 1;
                            sum += Data[a, b];
                        }
                    }
                    double avg = (sum+0.0) / (windowSize * windowSize);
                    if (avg / 255 < 0.5)
                    {
                        Data[i, j] = (byte)(2 * avg / 255 * Data[i, j]);
                    }
                    else
                    {
                        Data[i,j]=(byte)((1-2*(1-avg/255.0)*(1-Data[i,j]/255.0))*255);
                    }
                }
            }
        }
//直方图均衡
        public void HistEqual()
        {
            double[] num = new double[256] ;
            for(int i=0;i<256;i++) num[i]=0;

            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    num[Data[i, j]]++;
                }
            }

            double[] newGray = new double[256];
            double n = 0;
            for (int i = 0; i < 256; i++)
            {
                n += num[i];
                newGray[i] = n * 255 / (Height * Width);
            }

            for (int i = 0; i < Height; i++)
            {
                for (int j = 0; j < Width; j++)
                {
                    Data[i,j]=(byte)newGray[Data[i,j]];
                }
            }
        }

}
}
在GrayBitmapData类中,只要我们对一个二维数组Data进行一系列的操作就是对图片的操作处理。在窗口上,我们可以使用
一个按钮来做各种调用:
//均值滤波
        private void btnAvgFilter_Click(object sender, EventArgs e)
        {
            if (bmp == null) return;
            GrayBitmapData gbmp = new GrayBitmapData(bmp);
            gbmp.AverageFilter(3);
            gbmp.ShowImage(pbxShowImage);
        }
//转换为灰度图
        private void btnToGray_Click(object sender, EventArgs e)
        {
            if (bmp == null) return;
            GrayBitmapData gbmp = new GrayBitmapData(bmp);
            gbmp.ShowImage(pbxShowImage);
        }

 

四、总结

在Visual c#中对图像进行处理或访问,需要先建立一个Bitmap对象,然后通过其LockBits方法来获得一个BitmapData类的对象,然后通过获得其像素数据的首地址来对Bitmap对象的像素数据进行操作。当然,一种简单但是速度慢的方法是用Bitmap类的GetPixel和SetPixel方法。其中BitmapData类的Stride属性为每行像素所占的字节。

 

 

 

 

 

 

 

 

 

 

 

C# colorMatrix 对图片的处理 : 亮度调整 抓屏 翻转 随鼠标画矩形

 

1.图片亮度处理

 

        private void btn_Grap_Click(object senderEventArgs e)

        {

            //亮度百分比

            int percent = 50;

            Single v = 0.006F * percent;    

            Single[][] matrix = {         

                new Single[] { 1, 0, 0, 0, 0 },         

                new Single[] { 0, 1, 0, 0, 0 },          

                new Single[] { 0, 0, 1, 0, 0 },         

                new Single[] { 0, 0, 0, 1, 0 },         

                new Single[] { vvv, 0, 1 }     

            };    

            System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix(matrix);

            System.Drawing.Imaging.ImageAttributes attr = newSystem.Drawing.Imaging.ImageAttributes();    

            attr.SetColorMatrix(cm);    

            //Image tmp 

            Image tmp = Image.FromFile("1.png");

 

            this.pictureBox_Src.Image = Image.FromFile("1.png");

 

            Graphics g = Graphics.FromImage(tmp);  

            try  

            {

                Rectangle destRect = new Rectangle(0, 0, tmp.Widthtmp.Height);        

                g.DrawImage(tmpdestRect, 0, 0, tmp.Widthtmp.HeightGraphicsUnit.Pixelattr);    

            }    

            finally    

            {        

                g.Dispose();    

            }

 

            this.pictureBox_Dest.Image = (Image)tmp.Clone();

        }

 

 

2.抓屏将生成的图片显示在pictureBox

 

        private void btn_Screen_Click(object senderEventArgs e)

        {

            Image myImage = new Bitmap(Screen.PrimaryScreen.Bounds.Width,Screen.PrimaryScreen.Bounds.Height);

            Graphics g = Graphics.FromImage(myImage);

            g.CopyFromScreen(new Point(0, 0), new Point(0, 0), newSize(Screen.PrimaryScreen.Bounds.WidthScreen.PrimaryScreen.Bounds.Height));

            //IntPtr dc1 = g.GetHdc();      //此处这两句多余,具体看最后GetHdc()定义

            //g.ReleaseHdc(dc1);           

            g.Dispose();

            this.pictureBox_Src.SizeMode = PictureBoxSizeMode.StretchImage;

            this.pictureBox_Src.Image = myImage;

            myImage.Save("Screen"ImageFormat.Png);

     }

 

3.翻转

 

        private void btn_RotateFlip_Click(object senderEventArgs e)

        {

            this.pictureBox_Src.Image = Image.FromFile("1.png");

 

            Image tmp = Image.FromFile("1.png");

 

            tmp.RotateFlip(RotateFlipType.Rotate90FlipNone);

            this.pictureBox_Dest.Image = tmp;

        }

4.跟随鼠标在 pictureBox的图片上画矩形

        private int intStartX = 0;

        private int intStartY = 0;

        private bool isMouseDraw = false;

 

        private void pictureBox_Src_MouseDown(object senderMouseEventArgs e)

        {

            isMouseDraw = true;

 

            intStartX = e.X;

            intStartY = e.Y;

        }

 

        private void pictureBox_Src_MouseMove(object senderMouseEventArgs e)

        {

            if (isMouseDraw)

            {

                try

                {

                    //Image tmp = Image.FromFile("1.png");

                    Graphics g = this.pictureBox_Src.CreateGraphics();

                    //清空上次画下的痕迹

                    g.Clear(this.pictureBox_Src.BackColor);

                    Brush brush = new SolidBrush(Color.Red);

                    Pen pen = new Pen(brush, 1);

                    pen.DashStyle = DashStyle.Solid;

                    g.DrawRectangle(pennew Rectangle(intStartX > e.X ? e.X : intStartXintStartY > e.Ye.Y : intStartYMath.Abs(e.X - intStartX), Math.Abs(e.Y - intStartY)));

                    g.Dispose();

                    //this.pictureBox_Src.Image = tmp;

                }

                catch (Exception ex)

                {

                    ex.ToString();

                }

            }

        }

 

        private void pictureBox_Src_MouseUp(object senderMouseEventArgs e)

        {

            isMouseDraw = false;

 

            intStartX = 0;

            intStartY = 0;

        }

5.取灰度

 

        private void btn_GetGray_Click(object senderEventArgs e)

        {

            this.pictureBox_Src.Image = Image.FromFile("1.png");

            Bitmap currentBitmap = new Bitmap(this.pictureBox_Src.Image);

            Graphics g = Graphics.FromImage(currentBitmap);

            ImageAttributes ia = new ImageAttributes();

            float[][] colorMatrix =   {    

                new   float[]   {0.299f,   0.299f,   0.299f,   0,   0},

                new   float[]   {0.587f,   0.587f,   0.587f,   0,   0},

                new   float[]   {0.114f,   0.114f,   0.114f,   0,   0},

                new   float[]   {0,   0,   0,   1,   0},

                new   float[]   {0,   0,   0,   0,   1}

            };

            ColorMatrix cm = new ColorMatrix(colorMatrix);

            ia.SetColorMatrix(cmColorMatrixFlag.DefaultColorAdjustType.Bitmap);

            g.DrawImage(currentBitmapnew Rectangle(0, 0, currentBitmap.WidthcurrentBitmap.Height), 0, 0, currentBitmap.WidthcurrentBitmap.HeightGraphicsUnit.Pixelia);

            this.pictureBox_Dest.Image = (Image)(currentBitmap.Clone());

            g.Dispose();

        }

 

 

 

 

Graphics.GetHdc 方法

.NET Framework 4

获取与此 Graphics 关联的设备上下文的句柄。

命名空间:  System.Drawing
程序集:  System.Drawing(在 System.Drawing.dll 中)

语法

[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags =

SecurityPermissionFlag.UnmanagedCode)]

public IntPtr GetHdc()

返回值

类型:System.IntPtr
与此 Graphics 关联的设备上下文的句柄。

实现

IDeviceContext.GetHdc()

备注


设备上下文是一个基于 GDI 的 Windows 结构,它定义一组图形对象及其关联的特性,以及影响输出的图形模式。 此方法返回该设备上下文(字体除外)。由于未选择字体,使用 GetHdc 方法返回的句柄对 FromHdc方法进行调用将会失败。

GetHdc 方法调用和 ReleaseHdc 方法调用必须成对出现。 在 GetHdc 和 ReleaseHdc 方法对的范围内,通常仅调用 GDI 函数。 在该范围内对 Graphics(它产生 hdc 参数)的 GDI+ 方法的调用因 ObjectBusy错误而失败。 此外,GDI+ 忽略后续操作中对 hdc 参数的 Graphics 所做的所有状态更改。

示例


下面的代码示例设计为与 Windows 窗体一起使用,它需要 PaintEventArgse,即 Paint 事件处理程序的一个参数。 该示例演示如何调用 Windows GDI 函数以执行与 GDI+ Graphics 方法相同的任务。 代码执行下列操作:

  • 为 Windows DLL 文件 gdi32.dll 定义互操作性 DllImportAttribute 特性。 此 DLL 包含所需的GDI 函数。
  • 将该 DLL 中的 Rectangle 函数定义为外部函数。
  • 创建一支红色钢笔。
  • 利用该钢笔,使用 GDI+ DrawRectangle 方法将矩形绘制到屏幕。
  • 定义内部指针类型变量 hdc 并将它的值设置为窗体的设备上下文句柄。
  • 使用 GDI Rectangle 函数将矩形绘制到屏幕。

释放由 hdc 参数表示的设备上下文。

 

public class GDI

{

    [System.Runtime.InteropServices.DllImport("gdi32.dll")]

    internal static extern bool Rectangle(

       IntPtr hdc,

       int ulCornerX, int ulCornerY,

       int lrCornerX, int lrCornerY);

}

 

[System.Security.Permissions.SecurityPermission(

System.Security.Permissions.SecurityAction.LinkDemand, Flags =

System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)]           

private void GetHdcForGDI1(PaintEventArgs e)

{

    // Create pen.

    Pen redPen = new Pen(Color.Red, 1);

 

    // Draw rectangle with GDI+.

    e.Graphics.DrawRectangle(redPen, 10, 10, 100, 50);

 

    // Get handle to device context.

    IntPtr hdc = e.Graphics.GetHdc();

 

    // Draw rectangle with GDI using default pen.

    GDI.Rectangle(hdc, 10, 70, 110, 120);

 

    // Release handle to device context.

    e.Graphics.ReleaseHdc(hdc);

}

 

 

 

 

2019-01-17 17:40:55 whf227 阅读数 860
  • 课程介绍

    基本掌握C#Winform自带的RDLC报表的设计方法 基本掌握C#Winform自带的RDLC报表的使用技巧

    0课时 0分钟 266人学习 武占文
    免费试看

        此工具是当年自己在学习Opencv的时候,慢慢积累的,包含了常用的图像处理算法,非常适合新人学习,现放出源码,由于是以前做的,功能不全。

        当时Emgucv的学习资料非常之少,没有一本书是讲Emgucv的,大都需要参考C++的代码,OpenCV3的书籍也只有英文版的。后来有民间高手自己写了书,对初学者还是非常有帮助的。

 说明: 1.识别那一块准确率不高,后期没有时间进行优化

             2.测量没有涉及

             3.关于标定,想抽个时间写篇文章复习复习,当时是花了很多时间才搞清楚的。  

 

源码地址 : https://github.com/Uhifon/EmguCVTool

2010-02-03 13:57:00 lulu831110 阅读数 18972
  • 课程介绍

    基本掌握C#Winform自带的RDLC报表的设计方法 基本掌握C#Winform自带的RDLC报表的使用技巧

    0课时 0分钟 266人学习 武占文
    免费试看

吐血推荐以下在C#下开发图像处理应用和研究能用的开源的库,还有很多更强悍的功能库,但是没有源代码的不一一列出,一下库都经过测试,我正在使用,欢迎交流!

http://www.emgu.com/wiki/index.php/Tutorial#Emgu,这个库封装了OpenCV,可以在C#中使用OpenCV

http://research.microsoft.com/en-us/um/cambridge/projects/infernet/default.aspx 这个库提供推理机和概率统计的功能,微软的产品

http://mathnet.opensourcedotnet.info/ 超级强悍的数学运算库,包含线性代数和信号处理以及符号运算模块

http://code.google.com/p/aforge/ C#图像处理库,吐血推荐

http://www.matthewajohnson.org/software/svm.html SVM.NET库支持支持向量机运算是libSVM

http://www.csie.ntu.edu.tw/~cjlin/libsvm/

的C#版本,该站点还提供VISION.NET库支持视频处理。

 OPENCV相信搞图像的都知道,绝大部分的图像处理算法里面都实现了。

其他的一些机器学习的算法(只有matlab代码)

http://www.cs.uiuc.edu/homes/dengcai2/Data/data.html


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/yunhaiC/archive/2009/11/04/4768788.aspx

2008-05-13 12:46:00 xuebao2 阅读数 1381
  • 课程介绍

    基本掌握C#Winform自带的RDLC报表的设计方法 基本掌握C#Winform自带的RDLC报表的使用技巧

    0课时 0分钟 266人学习 武占文
    免费试看

C# 图像处理算法集合

http://blog.csdn.net/ki1381/category/240835.aspx

C# 图像灰度处理

阅读数 2778