C#图形图像处理(1)

本文讨论了C#代码中图形图像处理的基本操作,包括Image、Bitmap、Graphics、ImageFormat等类型的应用,以及图片格式转换、缩放、旋转和翻转的基本操作。

概述

本文使用的图形图像处理资源定义在System.Drawing命名空间及其子命名空间中,代码中注意引用。其中,Image为抽象类,可以使用一些静态方法操作;Bitmap类可以按像素处理图像,绘制时需要关联Gruphics对象操作,如绘制像素、线条、图形、填充图形、图像等。

处理图像时还需要注意分辨率,使用DPI,即每英寸的点数(像素数),在位图中包含水平方向和垂直方向的分辨率。稍后讨论相关应用。

绘制图形时还需要关注的类型包括:

  • 颜色,使用Color结构类型,可以使用透明度和RGB(红、绿、蓝)值定义。
  • 画笔,使用Pen类,定义绘制线条的颜色、宽度、样式等。Pens类中使用静态属性定义了一系列命名颜色的实线样式画笔对象,绘制线条图形时可以直接使用。
  • 格式刷,使用Brush类为主类型,可以定义填充图形的颜色、图案等。Brushes类的静态属性定义了一系列命名的颜色填充格式刷对象,绘制填充图形时可以直接使用。

下面的代码会创建一个200*200像素的正方形图像,并由四个不同颜色的小正方形组成。

C#
using System.Drawing;
using System.Drawing.Imaging;
namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            using (Bitmap bmp = new Bitmap(200, 200))
            {
                using (Graphics g = Graphics.FromImage(bmp))
                {
                    g.FillRectangle(Brushes.GhostWhite, 0, 0, 100, 100);
                    g.FillRectangle(Brushes.Red, 100, 0, 100, 100);
                    g.FillRectangle(Brushes.Blue, 0, 100, 100, 100);
                    g.FillRectangle(Brushes.Black, 100, 100, 100, 100);
                    bmp.Save(@"d:\block.bmp", ImageFormat.Bmp);
                }
            }
        }
    }
}

代码绘制的图案如下图所示。

绘制填充矩形

代码中,首先定义了200*200像素的位图对象(Bitmap类型),Bitmap()构造函数的参数分别是图像的宽度和高度,单位为像素;然后,使用Graphics.FromImage()方法创建了基于bmp对象的图形对象(Graphics类型);接下来,使用Graphics对象的FillRectangle()方法绘制了四个颜色填充的矩形,方法的参数分别是格式刷对象、左上角水平坐标、左上角垂直坐标、宽度、高度,坐标和尺寸单位均为像素。

Graphics对象完成图像绘制后,使用Bitmap对象的Save()方法将图像保存到指定的文件,其中,参数一指定文件的路径,参数二指定保存的图像格式,这里使用位图格式。ImageFormat类定义在System.Drawing.Imaging命名空间,其中,使用一系列静态属性定义了所支持的图像格式。

此外,在绘制图像的坐标系中,左上角为原点(0,0),如下图所示。

绘制坐标系

接下来的常用操作会封装到tImage和tCanvas类,它们都定义在cfx.image命名空间。其中,tImage类定义为静态类,定义了图像和图片文件的常用操作;tCanvas类则用于图像的绘制。

格式转换与图片缩放

ImageFormat类中,静态属性指定图片格式,处理图片文件时,可以根据扩展名判断格式;下面的代码(cfx/image/tImage.cs),在tImage类中定义GetImageFormat()方法,其功能是根据文件的扩展名返回图像格式。

C#
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace cfx.image
{
    public static class tImage
    {
        // 根据文件扩展名返回图片格式
        public static ImageFormat GetImageFormat(string filename)
        {
            string ext = Path.GetExtension(filename).ToLower();
            if (ext == ".jpeg" || ext == ".jpg") return ImageFormat.Jpeg;
            else if (ext == ".png") return ImageFormat.Png;
            else if (ext == ".gif") return ImageFormat.Gif;
            else if (ext == ".ico" || ext == ".icon") return ImageFormat.Icon;
            else return ImageFormat.Bmp;
        }
    }
}

代码中只处理了JPEG、PNG、GIF、Icon和BMP五种格式,如果需要也可以添加更多的支持代码,如Emf、Exif、Tiff、Wmf格式。

下面的代码(cfx/image/tImage.cs)创建了转换图片文件格式的操作方法。

C#
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace cfx.image
{
    public static class tImage
    {
        // 其它代码
        // 将图片文件转换格式
        public static bool Convert(string source, string target)
        {
            try
            {
                using (Image img = Image.FromFile(source))
                {
                    img.Save(target, GetImageFormat(target));
                    return true;
                }
            }
            catch { return false; }
        }
        // 将图像对象转换格式
        public static bool Convert(Image img, string target)
        {
            try
            {
                img.Save(target, GetImageFormat(target));
                return true;
            }
            catch { return false; }
        }
        //
        public static bool Convert(Stream s, string target)
        {
            try
            {
                Image img = Image.FromStream(s);
                img.Save(target, GetImageFormat(target));
                return true;
            }
            catch { return false; }
        }
    }
}

在tImage类中定义了三个重载版本的Convert()方法,可以将图片文件、Image对象、Stream对象另存为指定的图片格式文件;代码中用了Image对象的Save()方法,其中,参数一为保存的图片文件路径,参数二为保存的图片格式,这里,使用GetImageFormat()方法自动识别文件格式。图片保存成功时,Convert()方法会返回true,否则返回false。

下面的代码,在Program.cs文件中测试tImage.Convert()方法的应用。

C#
using System;
using cfx.image;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            bool result = tImage.Convert(@"d:\block.bmp",
                @"d:\1.jpg");
            Console.WriteLine(result);
        }
    }
}

代码会将d:\block.bmp文件转换为JPEG格式,并保存到d:\1.jpg文件;成功转换后会显示True,此时可以在d:盘下看到1.jpg文件,其尺寸要比block.bmp文件小很多。

转换图片文件格式的同时,也可以对图片尺寸进行缩放,如下面的代码,会在tImage类中添加一系列重载版本的Resize()方法。

C#
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace cfx.image
{
    public static class tImage
    {
        // 其它代码
        //
        public static bool Resize(string source, string target, 
            int width, int height)
        {
            try
            {
                using (Image img = Image.FromFile(source))
                {
                    using (Bitmap newImg = 
                        new Bitmap(img, width, height))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        //
        public static bool Resize(Image img, string target, 
            int width, int height)
        {
            try
            {
                using (Bitmap newImg = 
                    new Bitmap(img, width, height))
                {
                    newImg.Save(target, GetImageFormat(target));
                    return true;
                }
            }
            catch { return false; }
        }
        //
        public static bool Resize(Stream s, string target, 
            int width, int height)
        {
            try
            {
                using (Image img = Image.FromStream(s))
                {
                    using (Bitmap newImg = 
                        new Bitmap(img, width, height))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        // 图片缩放
        public static bool Resize(string source, 
            string target, float scale)
        {
            try
            {
                using (Image img = Image.FromFile(source))
                {
                    using (Bitmap newImg = new Bitmap(img,
                        (int)(img.Width * scale),
                        (int)(img.Height * scale)))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        //
        public static bool Resize(Image img, string target, float scale)
        {
            try
            {
                using (Bitmap newImg = new Bitmap(img,
                    (int)(img.Width * scale), 
                    (int)(img.Height * scale)))
                {
                    newImg.Save(target, GetImageFormat(target));
                    return true;
                }
            }
            catch { return false; }
        }
        //
        public static bool Resize(Stream s, string target, float scale)
        {
            try
            {
                using (Image img = Image.FromStream(s))
                {
                    using (Bitmap newImg = new Bitmap(img,
                        (int)(img.Width * scale), 
                        (int)(img.Height * scale)))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
    }
}

代码虽不少,但并不难理解,方法的参数中,图像源分别使用了图片文件路径、Image对象和Stream对象;保存目标(target参数)均为图片文件路径;缩放的方法有两种,一是指定宽度和高度尺寸(width和height参数),另一种是按比例缩放(scale参数)。

下面的代码,在Program.cs文件中测试图片的缩放操作。

C#
using cfx.image;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            tImage.Resize(@"d:\block.bmp", @"d:\2.jpg", 0.5F);
            tImage.Resize(@"d:\block.bmp", @"d:\3.jpg", 2F);
        }
    }
}

代码将d:\block.bmp文件缩放为0.5倍和2倍,分别保存在d:盘下的2.jpg和3.jpg文件。可以通过文件属性中的“详细信息”页查看文件的尺寸分别是100*100像素和400*400像素。

工作中,在转换图片尺寸时还可以指定宽度,然后根据比例计算出高度;或者指定高度,然后根据比例计算出宽度;如下面的代码(cfx/image/tImage.cs)。

C#
using System;
using System.IO;
using System.Drawing;
using System.Drawing.Imaging;

namespace cfx.image
{
    public static class tImage
    {
        // 其它代码
        // 设置宽度,高度自动按比例设置
        public static bool ResizeByWidth(string source, 
            string target, int width)
        {
            try
            {
                using (Image img = Image.FromFile(source))
                {
                    // 计算比例
                    float scale = (float)img.Width / width;
                    //
                    using (Bitmap newImg = new Bitmap(img,
                        width, (int)(img.Height / scale)))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        // 设置宽度,高度自动按比例设置
        public static bool ResizeByWidth(Image img, 
            string target, int width)
        {
            try
            {
                // 计算比例
                float scale = (float)img.Width / width;
                //
                using (Bitmap newImg = new Bitmap(img,
                    width, (int)(img.Height / scale)))
                {
                    newImg.Save(target, GetImageFormat(target));
                    return true;
                }
            }
            catch { return false; }
        }
        //
        // 设置宽度,高度自动按比例设置
        public static bool ResizeByWidth(Stream s, 
            string target, int width)
        {
            try
            {
                using (Image img = Image.FromStream(s))
                {
                    // 计算比例
                    float scale = (float)img.Width / width;
                    //
                    using (Bitmap newImg = new Bitmap(img,
                        width, (int)(img.Height / scale)))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        // 设置高度,宽度自动按比例设置
        public static bool ResizeByHeight(string source, 
            string target, int height)
        {
            try
            {
                using (Image img = Image.FromFile(source))
                {
                    // 计算比例
                    float scale = (float)img.Height / height;
                    //
                    using (Bitmap newImg = new Bitmap(img,
                        (int)(img.Width / scale), height))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        //
        public static bool ResizeByHeight(Image img, 
            string target, int height)
        {
            try
            {
                // 计算比例
                float scale = (float)img.Height / height;
                //
                using (Bitmap newImg = new Bitmap(img,
                    (int)(img.Width / scale), height))
                {
                    newImg.Save(target, GetImageFormat(target));
                    return true;
                }
            }
            catch { return false; }
        }
        //
        public static bool ResizeByHeight(Stream s, 
            string target, int height)
        {
            try
            {
                using (Image img = Image.FromStream(s))
                {
                    // 计算比例
                    float scale = (float)img.Height / height;
                    //
                    using (Bitmap newImg = new Bitmap(img,
                        (int)(img.Width / scale), height))
                    {
                        newImg.Save(target, GetImageFormat(target));
                        return true;
                    }
                }
            }
            catch { return false; }
        }
        //
    }
}

代码中,ResizeByWidth()和ResizeByHeight()方法分别按指定的宽度和高度缩放图片,并保持宽度和高度的原始比例。

图片旋转与翻转

图片按角度旋转(rotate)和翻转(flip)操作中,如果旋转90度、180度、270度,可以直接使用Bitmap对象的RotateFlip()方法,同时可以执行水平翻转和垂直翻转操作。RotateFlip()方法的参数只有一个,定义为RotateFlipType枚举类型,用于指定旋转和翻转的方式。下面的代码演示了RotateFlip()方法的基本应用。

C#
using System.Drawing;
using System.Drawing.Imaging;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            using(Bitmap bmp = new Bitmap(@"d:\block.bmp"))
            {
                bmp.RotateFlip(RotateFlipType.Rotate90FlipNone);
                bmp.Save(@"d:\4.jpg", ImageFormat.Jpeg);
            }
        }
    }
}

代码会将d:\block.bmp图片旋转90度,并保存到d:\4.jpg文件中,效果如下图所示。

旋转90度

下面的代码会对图片进行水平翻转操作。

C#
using System.Drawing;
using System.Drawing.Imaging;

namespace csfx_demo
{
    class Program
    {
        static void Main(string[] args)
        {
            using(Bitmap bmp = new Bitmap(@"d:\block.bmp"))
            {
                bmp.RotateFlip(RotateFlipType.RotateNoneFlipX);
                bmp.Save(@"d:\5.jpg", ImageFormat.Jpeg);
            }
        }
    }
}

代码会将d:\block.bmp图片水平翻转,并保存到d:\5.jpg文件中,效果如下图所示。

水平翻转

RotateFlipType枚举值有很多,前半部分使用Rotate定义了旋转的角度,不执行旋转操作时使用RotateNone,而Rotate90、Rotate180、Rotate270分别是顺时针旋转90度、180度和270度;枚举值的后半部分使用Flip定义了翻转方式,不执行翻转操作时使用FlipNone,水平翻转使用FlipX,垂直翻转使用FlipY,同时执行水平翻转和垂直翻转则使用FlipXY。实际应用中,可以使用Rotate和Flip组合的枚举值指定旋转和翻转操作方式。