Image帮助类

快速读取图像文件的高度和宽度

WPF

引用库System.Windows.Media,常用于WPF程序。

using PresentationCore;
using System.Windows.Media;
/// <summary>
/// 快速读取图像文件的高度和宽度
/// </summary>
/// <param name="fileName"></param>
/// <returns></returns>
public static string GetImageSize(string fileName)
{
    try
    {
        using (var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
        {
            var frame = System.Windows.Media.Imaging.BitmapFrame.Create(stream, System.Windows.Media.Imaging.BitmapCreateOptions.DelayCreation, System.Windows.Media.Imaging.BitmapCacheOption.None);
            var width = frame.PixelWidth;
            var height = frame.PixelHeight;
            return $"{width}*{height}";
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("图片格式异常:" + ex.Message);
        return string.Empty;
    }
}

Winform

Bitmap.FromFile

using (var image = Bitmap.FromFile(filePath))
{
    int width = image.Width;
    int height = image.Height;
}

三方库 MetadataExtractor

GuGet引入第三方库 MetadataExtractor

var metadata = ImageMetadataReader.ReadMetadata(filePath);
foreach (var directory in metadata)
{
    foreach (var tag in directory.Tags)
    {
        Console.WriteLine($"[{directory.Name}]{tag.Name} = {tag.Description}");
    }
}

获取宽与高

var meta = ImageMetadataReader.ReadMetadata(filePath);
// JPG
var jpg = meta.OfType<MetadataExtractor.Formats.Jpeg.JpegDirectory>().FirstOrDefault();
if (jpg != null)
{
    var width = jpg.GetImageWidth();
    var height = jpg.GetImageHeight();
    Console.WriteLine($"{i} {width} * {height}");
}
// PNG
var png = meta.OfType<MetadataExtractor.Formats.Png.PngDirectory>().FirstOrDefault();
if (png != null)
{
    var width = png.GetString(BmpHeaderDirectory.TagImageWidth);
    var height = png.GetString(BmpHeaderDirectory.TagImageHeight);
    Console.WriteLine($"{i} {width} * {height}");
}
// BMP
var bmp = meta.OfType<MetadataExtractor.Formats.Bmp.BmpHeaderDirectory>().FirstOrDefault();
if (bmp != null)
{
    var width = bmp.GetString(BmpHeaderDirectory.TagImageWidth);
    var height = bmp.GetString(BmpHeaderDirectory.TagImageHeight);
    Console.WriteLine($"{i} {width} * {height}");
}

其他格式的获取方式类似,Tiff、WebP等。

自定义

一般文件的开头存储有相关信息,可以把图片当成文件进行处理,根据相关协议进行解析。

/// <summary>
/// 快速获取图片的大小
/// </summary>
/// <param name="filePath">图片路径</param>
/// <returns></returns>
public static Size GetImageSize(string filePath)
{
    Size rslt = new Size();
    string type = "UNKNOWN";
    if (!File.Exists(filePath))
    {
        return rslt;
    }
    using (var stream = File.OpenRead(filePath))
    {
        using (var reader = new BinaryReader(stream, Encoding.UTF7, false))
        {
            var c1 = reader.ReadChar();
            var c2 = reader.ReadChar();
            var c3 = reader.ReadChar();
            if (c1 == 0xFF && c2 == 0xD8)
            {
                type = "JPG";
                while (c3 == 0xFF)
                {
                    int marker = reader.Read();
                    var data = reader.ReadBytes(2);
                    int len = BigEndianByte2Int(data);
                    if (marker == 192 || marker == 193 || marker == 194)
                    {
                        reader.BaseStream.Position += 1;
                        data = reader.ReadBytes(2);
                        rslt.Height = BigEndianByte2Int(data);
                        data = reader.ReadBytes(2);
                        rslt.Width = BigEndianByte2Int(data);
                        break;
                    }
                    reader.BaseStream.Position += len - 2;
                    c3 = reader.ReadChar();
                }
            }
            else if (c2 == 'P' && c3 == 'N')
            {
                type = "PNG";
                reader.BaseStream.Position += 15;
                var data = reader.ReadBytes(2);
                rslt.Width = BigEndianByte2Int(data);
                reader.BaseStream.Position += 2;
                data = reader.ReadBytes(2);
                rslt.Height = BigEndianByte2Int(data);
            }
            else if (c1 == 'B' && c2 == 'M')
            {
                type = "BMP";
                reader.BaseStream.Position += 15;
                var data = reader.ReadBytes(2);
                rslt.Width = LittleEndianByte2Int(data);
                reader.BaseStream.Position += 2;
                data = reader.ReadBytes(2);
                rslt.Height = LittleEndianByte2Int(data);
            }
            else if (c1 == 'G' && c2 == 'I' && c3 == 'F')
            {
                type = "GIF";
                reader.BaseStream.Position += 3;
                var data = reader.ReadBytes(2);
                rslt.Width = LittleEndianByte2Int(data);
                data = reader.ReadBytes(2);
                rslt.Height = LittleEndianByte2Int(data);
            }
            else if (c1 == 'R' && c2 == 'I' && c3 == 'F')
            {
                type = "WebP";
                reader.BaseStream.Position += 23;
                var data = reader.ReadBytes(2);
                rslt.Width = LittleEndianByte2Int(data);
                data = reader.ReadBytes(2);
                rslt.Height = LittleEndianByte2Int(data);
            }
            else
            {
                var data = File2Bytes(filePath);
                Console.WriteLine(Byte2HexStringX2(true, data));
            }
        }
    }
    Console.WriteLine($"{type} {rslt.Width}(Width) * {rslt.Height}(Height)");
    return rslt;
}

其中,字节转换可以参考其他博客的BigEndianByte2IntByte2HexStringX2

耗时对比

循环运行100次,各自所需时间如图所示,自定义时间是最短的,毫秒级别。

图片生成指定质量的流

/// <summary>
/// 图片生成指定质量的流
/// </summary>
/// <param name="encoder">图片编码</param>
/// <param name="quality">图片质量</param>
/// <returns></returns>
public Stream ToStream(ImageCodecInfo encoder, int quality)
{
    var myEncoder = Encoder.Quality;
    var myEncoderParameters = new EncoderParameters(1);
    var myEncoderParam = new EncoderParameter(myEncoder, quality);
    myEncoderParameters.Param[0] = myEncoderParam;
    var rslt = new MemoryStream();
    _bitmap.Save(rslt, encoder, myEncoderParameters);
    return rslt;
}

图片一般帮助类

/// <summary>
/// 图片一般帮助类
/// </summary>
public partial class ImageHelper : IDisposable
{
    readonly string _fileName;
    Bitmap _bitmap;

    /// <summary>
    /// 图片所指位图
    /// </summary>
    public Bitmap SourceBitmap
    {
        get { return _bitmap; }
    }

    public ImageHelper(string fileName)
    {
        if (string.IsNullOrEmpty(fileName))
        {
            throw new ArgumentNullException(fileName, "fileName is NULL!");
        }
        if (!File.Exists(fileName))
        {
            throw new NotImplementedException("the image is not exist!!");
        }
        _fileName = fileName;
        _bitmap = new Bitmap(fileName);
    }
    public ImageHelper(Image img)
    {
        _bitmap = (Bitmap)img;
        _fileName = string.Empty;
    }
    public ImageHelper(Stream stream)
    {
        _bitmap = (Bitmap)Image.FromStream(stream);
        _fileName = string.Empty;
    }

    public byte[] ToBytes(string extension)
    {
        return ToBytes(GetFormat(extension));
    }
    public byte[] ToBytes(ImageFormat imageFormat)
    {
        using var ms = new MemoryStream();
        _bitmap.Save(ms, imageFormat);
        return ms.ToArray();
    }

    public void DrawRectangles(Color color, RectangleF[] rects)
    {
        if (rects == null)
        {
            return;
        }
        var pen = new Pen(color, 5);
        using Graphics g = Graphics.FromImage(_bitmap);
        g.DrawRectangles(pen, rects);
    }
    public void DrawRectangle(Color color, Rectangle rect)
    {
        var pen = new Pen(color, 5);
        using Graphics g = Graphics.FromImage(_bitmap);
        g.DrawRectangle(pen, rect);
    }

    public Color StringToColor(string colorName)
    {
        if (colorName.StartsWith("#"))
        {
            colorName = colorName.Replace("#", string.Empty);
        }
        if (int.TryParse(colorName, System.Globalization.NumberStyles.HexNumber, null, out int value))
        {
            return Color.FromArgb(
                //Convert.ToByte((value >> 24) & 255),  // a
                Convert.ToByte((value >> 16) & 255),    // R
                Convert.ToByte((value >> 8) & 255),     // G
                Convert.ToByte((value >> 0) & 255)      // B
                );

        }
        return Color.Red;
    }

    /// <summary>
    /// 取文件名后缀(小写)
    /// </summary>
    /// <param name="name">文件名</param>
    /// <returns>返回小写后缀,不带“.”</returns>
    public string GetFileExt(string name)
    {
        return name.Split('.').Last().ToLower();
    }

    #region Dispose
    // 是否释放
    private bool _isDisposed = false;
    // 锁对象
    private static readonly object _clockObj = new object();
    ~ImageHelper()
    {
        Dispose(false);
    }
    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    // 释放相关资源
    protected virtual void Dispose(bool disposing)
    {
        //不需要多次释放
        if (_isDisposed) return;
        //由于Dispose方法可能被多线程调用,所以加锁以确保线程安全
        lock (_clockObj)
        {
            if (disposing)
            {
                if (!string.IsNullOrWhiteSpace(_fileName) && File.Exists(_fileName))
                {
                    File.Delete(_fileName);
                }
            }
            if (_bitmap != null)
            {
                _bitmap.Dispose();
                _bitmap = null;
            }
            _isDisposed = true;
        }
    }
    #endregion
}

图片缩略

/// <summary>
/// 图片伸缩(百分比)
/// </summary>
/// <param name="stretchWidth">伸缩的宽度(%)</param>
/// <param name="stretchHeight">伸缩的高度(%)</param>
public void StretchImage(int stretchWidth, int stretchHeight)
{
    if (stretchHeight == _bitmap.Height && stretchWidth == _bitmap.Width)
    {
        return;
    }
    int width = _bitmap.Width;
    int height = _bitmap.Height;
    Bitmap bitmap = new Bitmap(width * stretchWidth / 100, height * stretchHeight / 100);
    Graphics gSave = Graphics.FromImage(bitmap);
    Point[] p = new Point[3];
    p[0].X = 0;
    p[1].X = width * stretchWidth / 100;
    p[2].X = 0;

    p[0].Y = 0;
    p[1].Y = 0;
    p[2].Y = height * stretchHeight / 100;
    gSave.Clear(Color.White);
    gSave.DrawImage(_bitmap, p);
    gSave.Dispose();

    _bitmap.Dispose();
    _bitmap = bitmap;

}
/// <summary>
/// 图片缩略(像素)
/// </summary>
/// <param name="thumbWidth">伸缩的宽度(像素)</param>
/// <param name="thumbHeight">伸缩的高度(像素)</param>
public void ThumbImage(int thumbWidth, int thumbHeight)
{
    if (thumbHeight == _bitmap.Height && thumbWidth == _bitmap.Width)
    {
        return;
    }
    Bitmap bitmap = new Bitmap(thumbWidth, thumbHeight);
    Graphics gSave = Graphics.FromImage(bitmap);
    Point[] p = new Point[3];
    p[0].X = 0;
    p[1].X = thumbWidth;
    p[2].X = 0;

    p[0].Y = 0;
    p[1].Y = 0;
    p[2].Y = thumbHeight;
    gSave.Clear(Color.White);
    gSave.DrawImage(_bitmap, p);
    gSave.Dispose();

    _bitmap.Dispose();
    _bitmap = bitmap;

}

调整图片质量

/// <summary>
/// 调整图片质量
/// </summary>
/// <param name="encoder">图片编码</param>
/// <param name="quality">图片质量</param>
/// <returns></returns>
public void QualityImage(ImageCodecInfo encoder, int quality)
{
    var myEncoder = Encoder.Quality;
    var myEncoderParameters = new EncoderParameters(1);
    var myEncoderParam = new EncoderParameter(myEncoder, quality);
    myEncoderParameters.Param[0] = myEncoderParam;
    var stream = new MemoryStream();
    _bitmap.Save(stream, encoder, myEncoderParameters);

    var bitmap = (Bitmap)Image.FromStream(stream);
    _bitmap.Dispose();
    _bitmap = bitmap;
}

图片转换

/// <summary>
/// 图片转换(后拉伸)
/// </summary>
/// <param name="rotateAngle">旋转度数</param>
/// <param name="stretchWidth">水平伸缩</param>
/// <param name="stretchHeight">竖直伸缩</param>
/// <param name="tiltWidth">水平倾斜</param>
/// <param name="tiltHeight">竖直倾斜</param>
/// <returns></returns>
public void DistortImage1(int rotateAngle, int stretchWidth, int stretchHeight, int tiltWidth, int tiltHeight)
{
    TiltWidthImage(tiltWidth);
    TiltHeightImage(tiltHeight);
    RotateAngle(rotateAngle);
    StretchImage(stretchWidth, stretchHeight);
}
/// <summary>
/// 图片转换(先缩小)
/// </summary>
/// <param name="rotateAngle">旋转度数</param>
/// <param name="stretchWidth">水平伸缩</param>
/// <param name="stretchHeight">竖直伸缩</param>
/// <param name="tiltWidth">水平倾斜</param>
/// <param name="tiltHeight">竖直倾斜</param>
/// <returns></returns>
public void DistortImage2(int rotateAngle, int stretchWidth, int stretchHeight, int tiltWidth, int tiltHeight)
{
    StretchImage(stretchWidth, stretchHeight);
    TiltWidthImage(tiltWidth);
    TiltHeightImage(tiltHeight);
    RotateAngle(rotateAngle);
}
/// <summary>
/// 图片水平倾斜
/// </summary>
/// <param name="tiltWidth">水平偏移长度</param>
public void TiltWidthImage(int tiltWidth)
{
    if (tiltWidth == 0)
    {
        return;
    }
    int width = _bitmap.Width;
    int height = _bitmap.Height;
    Bitmap bitmap = new Bitmap(width + Math.Abs(tiltWidth), height);
    Graphics gSave = Graphics.FromImage(bitmap);

    Point[] p = new Point[3];
    if (tiltWidth > 0)
    {
        p[0].X = Math.Abs(tiltWidth);
        p[1].X = width + Math.Abs(tiltWidth);
        p[2].X = 0;
    }
    else
    {
        p[0].X = 0;
        p[1].X = _bitmap.Width;
        p[2].X = Math.Abs(tiltWidth);
    }
    p[0].Y = 0;
    p[1].Y = 0;
    p[2].Y = height;
    gSave.Clear(Color.White);
    gSave.DrawImage(_bitmap, p);
    gSave.Dispose();

    _bitmap.Dispose();
    _bitmap = bitmap;
}
/// <summary>
/// 图片竖直倾斜
/// </summary>
/// <param name="tiltHeight">竖直偏移长度</param>
public void TiltHeightImage(int tiltHeight)
{
    if (tiltHeight == 0)
    {
        return;
    }
    int width = _bitmap.Width;
    int height = _bitmap.Height;

    Bitmap bitmap = new Bitmap(width, height + Math.Abs(tiltHeight));
    Graphics gSave = Graphics.FromImage(bitmap);

    Point[] p = new Point[3];
    p[0].X = 0;
    p[1].X = width;
    p[2].X = 0;
    if (tiltHeight > 0)
    {
        p[0].Y = Math.Abs(tiltHeight);
        p[1].Y = 0;
        p[2].Y = height + Math.Abs(tiltHeight);
    }
    else
    {
        p[0].Y = 0;
        p[1].Y = Math.Abs(tiltHeight);
        p[2].Y = height;
    }

    gSave.Clear(Color.White);
    gSave.DrawImage(_bitmap, p);
    gSave.Dispose();
    _bitmap.Dispose();
    _bitmap = bitmap;
}

图片裁切

/// <summary>  
/// 图片裁切
/// </summary>  
/// <param name="StartX">开始坐标X</param>  
/// <param name="StartY">开始坐标Y</param>  
/// <param name="iWidth">宽度</param>  
/// <param name="iHeight">高度</param>  
/// <returns>剪裁后的Bitmap</returns>  
public void CutImg(int StartX, int StartY, int iWidth, int iHeight)
{
    if (_bitmap == null) return;
    if (iWidth == 0 || iHeight == 0) return;
    int w = _bitmap.Width;
    int h = _bitmap.Height;
    if (StartX >= w || StartY >= h) return;
    if (StartX + iWidth > w) { iWidth = w - StartX; }
    if (StartY + iHeight > h) { iHeight = h - StartY; }

    Bitmap bmpOut = new Bitmap(iWidth, iHeight, PixelFormat.Format24bppRgb);
    using (Graphics g = Graphics.FromImage(bmpOut))
    {
        g.DrawImage(_bitmap, new Rectangle(0, 0, iWidth, iHeight), new Rectangle(StartX, StartY, iWidth, iHeight), GraphicsUnit.Pixel);
    }
    _bitmap.Dispose();
    _bitmap = bmpOut;
}

若不限制 iWidth,iHeight 的大小,可以任意放缩指定大小。

图片旋转

/// <summary>
/// 图片旋转
/// </summary>
/// <param name="angle">旋转度数</param>
public void RotateAngle(int angle)
{
    angle = angle % 360;
    if (angle == 0)
    {
        return;
    }
    //原图的宽和高
    int srcWidth = _bitmap.Width;
    int srcHeight = _bitmap.Height;
    //图像旋转之后所占区域宽和高
    Rectangle rotateRec = GetRotateRectangle(srcWidth, srcHeight, angle);
    int rotateWidth = rotateRec.Width;
    int rotateHeight = rotateRec.Height;

    //目标位图
    Bitmap destImage = null;
    Graphics graphics = null;
    try
    {
        //定义画布,宽高为图像旋转后的宽高
        destImage = new Bitmap(rotateWidth, rotateHeight);
        //graphics根据destImage创建,因此其原点此时在destImage左上角
        graphics = Graphics.FromImage(destImage);
        //要让graphics围绕某矩形中心点旋转N度,分三步
        //第一步,将graphics坐标原点移到矩形中心点,假设其中点坐标(x,y)
        //第二步,graphics旋转相应的角度(沿当前原点)
        //第三步,移回(-x,-y)
        //获取画布中心点
        Point centerPoint = new Point(rotateWidth / 2, rotateHeight / 2);
        //将graphics坐标原点移到中心点
        graphics.TranslateTransform(centerPoint.X, centerPoint.Y);
        //graphics旋转相应的角度(绕当前原点)
        graphics.RotateTransform(angle);
        //恢复graphics在水平和垂直方向的平移(沿当前原点)
        graphics.TranslateTransform(-centerPoint.X, -centerPoint.Y);
        //此时已经完成了graphics的旋转

        graphics.Clear(Color.White);
        //计算:如果要将源图像画到画布上且中心与画布中心重合,需要的偏移量
        Point Offset = new Point((rotateWidth - srcWidth) / 2, (rotateHeight - srcHeight) / 2);
        //将源图片画到rect里(rotateRec的中心)
        graphics.DrawImage(_bitmap, new Rectangle(Offset.X, Offset.Y, srcWidth, srcHeight));
        //重至绘图的所有变换
        graphics.ResetTransform();
        graphics.Save();
    }
    catch (Exception ex)
    {
        string err = ex.Message;
    }
    finally
    {
        if (graphics != null)
            graphics.Dispose();
    }
    _bitmap.Dispose();
    _bitmap = destImage;
}
private Rectangle GetRotateRectangle(int width, int height, float angle)
{
    double radian = angle * Math.PI / 180; ;
    double cos = Math.Cos(radian);
    double sin = Math.Sin(radian);
    //只需要考虑到第四象限和第三象限的情况取大值(中间用绝对值就可以包括第一和第二象限)
    int newWidth = (int)(Math.Max(Math.Abs(width * cos - height * sin), Math.Abs(width * cos + height * sin)));
    int newHeight = (int)(Math.Max(Math.Abs(width * sin - height * cos), Math.Abs(width * sin + height * cos)));
    return new Rectangle(0, 0, newWidth, newHeight);
}

得到图片格式

/// <summary>
/// 得到图片格式
/// </summary>
/// <param name="name">文件名称</param>
/// <returns>ImageFormat</returns>
public ImageFormat GetFormat(string name)
{
    string ext = GetFileExt(name);
    switch (ext)
    {
        case "ico":
            return ImageFormat.Icon;
        case "bmp":
            return ImageFormat.Bmp;
        case "png":
            return ImageFormat.Png;
        case "gif":
            return ImageFormat.Gif;
        default:
            return ImageFormat.Jpeg;
    }
}

获取图片编码信息

/// <summary>
/// 获取图片编码信息
/// </summary>
/// <param name="imageFormat">图片格式</param>
/// <returns></returns>
public ImageCodecInfo GetEncoder(ImageFormat imageFormat)
{
    var codecs = ImageCodecInfo.GetImageEncoders();
    foreach (var item in codecs)
    {
        if (item.FormatID == imageFormat.Guid)
        {
            return item;
        }
    }
    return null;
}

图片新尺寸调整

/// <summary>
/// 计算新尺寸(保持纵横比)
/// </summary>
/// <param name="srcWidth">原始宽度</param>
/// <param name="srcHeight">原始高度</param>
/// <param name="dstWidth">目标宽度</param>
/// <param name="dstHeight">目标高度</param>
/// <returns></returns>
public Size ResizeImage(int srcWidth, int srcHeight, int dstWidth, int dstHeight)
{
    if (dstWidth <= 0)
        dstWidth = srcWidth;
    if (dstHeight <= 0)
        dstHeight = srcHeight;
    decimal MAX_WIDTH = dstWidth;
    decimal MAX_HEIGHT = dstHeight;
    decimal ASPECT_RATIO = MAX_WIDTH / MAX_HEIGHT;

    int newWidth, newHeight;
    decimal originalWidth = srcWidth;
    decimal originalHeight = srcHeight;

    if (originalWidth > MAX_WIDTH || originalHeight > MAX_HEIGHT)
    {
        decimal factor;
        if (originalWidth / originalHeight > ASPECT_RATIO)
        {
            factor = originalWidth / MAX_WIDTH;
            newWidth = Convert.ToInt32(originalWidth / factor);
            newHeight = Convert.ToInt32(originalHeight / factor);
        }
        else
        {
            factor = originalHeight / MAX_HEIGHT;
            newWidth = Convert.ToInt32(originalWidth / factor);
            newHeight = Convert.ToInt32(originalHeight / factor);
        }
    }
    else
    {
        newWidth = srcWidth;
        newHeight = srcHeight;
    }
    return new Size(newWidth, newHeight);
}

图片保存

/// <summary>
/// BitmapImage保存为png
/// </summary>
/// <param name="bitmapImage">BitmapImage</param>
/// <param name="filePath">保存绝对路径</param>
private void SaveBitmapIamge(BitmapImage bitmapImage, string filePath)
{
    var encoder = new PngBitmapEncoder();
    encoder.Frames.Add(BitmapFrame.Create(bitmapImage));
    using (var fs = new FileStream(filePath, FileMode.OpenOrCreate))
    {
        encoder.Save(fs);
    }
}

创建位图

/// <summary>
/// 创建可显示的位图
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
public static BitmapImage GenerateBitmapImage(byte[] data)
{
    if (data == null || data.Length == 0)
    {
        return NoPicture;
    }
    BitmapImage bitmap = new BitmapImage();
    bitmap.BeginInit();
    bitmap.CacheOption = BitmapCacheOption.OnLoad;
    //bitmap.CreateOptions = BitmapCreateOptions.DelayCreation;
    using (MemoryStream ms = new MemoryStream(data))
    {
        bitmap.StreamSource = ms;
        bitmap.EndInit();
        bitmap.Freeze();
    }
    return bitmap;
}
posted @ 2020-12-18 18:02  wesson2019  阅读(145)  评论(0编辑  收藏  举报