ImageSharp一個專注於NetCore平臺影象處理的開源專案

神牛003發表於2017-10-12

今天大家分享的是一個專注於NetCore平臺影象處理的開源專案,老實說為這篇文章取名字想了5分鐘,可能是詞窮亦或是想更好的表達出這款開源專案的作用;這個專案在影象處理方面有很多功能,如:縮放,裁剪,繪畫,組合圖片等;今天主要講的是用她怎麼來繪圖和生成驗證碼的實際例子。

  • 簡單介紹ImageSharp
  • 試試畫兩條線(實線和虛線)
  • 生成個縮圖
  • 在圖片上畫字
  • 製作一個驗證碼圖片
  • 結合RazorPage模板,展示驗證碼圖片

簡單介紹ImageSharp

ImageSharp是對NetCore平臺擴充套件的一個影象處理方案,在寫下本文為止它最新的nuget下載量為4,034次,作者團隊最近一個月剛更新的包;沒錯這裡說最新是因為她前身和之前的版本都很受歡迎下載量也超高;她的git專案地址:https://github.com/SixLabors/ImageSharp。如果您的專案和我一樣是2.0版本(2.0以前的略過),那麼直接可以通過vs的nuget控制檯下載對應的包,注意繪圖的話需要分別下載如下兩個包:

 Install-Package SixLabors.ImageSharp -Version 1.0.0-beta0001 

 Install-Package SixLabors.ImageSharp.Drawing -Version 1.0.0-beta0001 

ImageSharp用法有朋友之前寫過,不過都主要針對於之前的版本,本章主要用到的都是最新的,有部分寫法可能不相同。

試試畫兩條線(實線和虛線)

這裡將用她來畫兩條直線並儲存成圖片,主要起到一個介紹作用,先來看實線如下程式碼:

var path = @"D:\F\學習\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\images";
            //預設實線
            using (Image<Rgba32> image = new Image<Rgba32>(500, 500))   //畫布大小
            {
                image.Mutate(x => x.
                        BackgroundColor(Rgba32.WhiteSmoke).   //畫布背景
                            DrawLines(
                            Rgba32.HotPink, //字型顏色
                            5,   //字型大小
                            new SixLabors.Primitives.PointF[]{
                                    new Vector2(10, 10),
                                    new Vector2(200, 150),
                                    new Vector2(50, 300)
                            } //兩點一線座標
                        )
                    );

                image.Save($"{path}/1.png"); //儲存
            }

 總要步驟我都備註上文字了,這裡主要通過兩點一線來繪製圖形,Vector2物件值得注意就是C#二維座標(x,y)物件,其實除了Vector2還有Vector3(三維座標)等,這對於做u3d的朋友來說不會陌生,老實說這個也是我在接觸u3d時候才知道有這個類的。下面來看效果圖:

由兩個兩點一線構造的一個角,下面來看下虛線繪製:

//虛線
            using (Image<Rgba32> image = new Image<Rgba32>(500, 500))   //畫布大小
            {
                image.Mutate(x => x.
                        BackgroundColor(Rgba32.WhiteSmoke).   //畫布背景
                            DrawLines(
                            Pens.Dash(Rgba32.HotPink, 5),   //字型大小
                            new SixLabors.Primitives.PointF[]{
                                    new Vector2(10, 10),
                                    new Vector2(200, 150),
                                    new Vector2(50, 300)
                            } //兩點一線座標
                        )
                    );

                image.Save($"{path}/2.png"); //儲存
            }

步驟都差不多,只是呼叫了DrawLines的擴充套件方法而已,其他線條例子就不多說了各位自行實驗。

生成個縮圖和在圖片上畫字

對於圖片型別的網站來說縮圖是常見的,這裡用ImageSharp生成縮圖很簡單,本例項用8.png做樣本來生成縮圖8-1.png,直接看例子如下是netstandard 1.3+的例子:

 //縮圖
            using (Image<Rgba32> image = Image.Load($"{path}/8.png"))
            {
                image.Mutate(x => x
                     .Resize(image.Width / 2, image.Height / 2)
                     );
                image.Save($"{path}/8-1.png");
            }

為了更好的對比縮圖和原圖的區別這裡對接拿兩圖的屬性做對比如:

能很好的看出縮圖檔案大小和畫素都減半了,實際縮略的時候不一定減半,這全由引數控制Resize(width,height);

畫字:在圖片上畫我們想要的字,其實類似於水印的一種需求,下面是在圖片上畫字的程式碼:

//畫字 
            var install_Family = new FontCollection().Install(
                System.IO.Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/bak", "STKAITI.TTF")
                //@"C:\Windows\Fonts\STKAITI.TTF"   //字型檔案
                );
            var font = new Font(install_Family, 50);  //字型
            using (Image<Rgba32> image = Image.Load($"{path}/8.png"))
            {
                image.Mutate(x => x
                     .DrawText(
                        "你們好,我是神牛",   //文字內容
                         font,
                         Rgba32.HotPink,
                         new Vector2(50, 150),
                         TextGraphicsOptions.Default)
                     );
                image.Save($"{path}/8-2.png");
            }

這裡用ImageSharp在圖片上畫字的時候需要注意:字型,因為windows系統自帶了字型問題這裡以STKAITI.TTF字型檔案為例,它儲存於 C:\Windows\Fonts\STKAITI.TTF 目錄,當然您可以直接把它拷貝到我們專案中如下我這裡的例子一樣做法(這裡只測試了windows下可用,尚未測試linux下直接使用該字型檔案是否可行);

製作一個驗證碼圖片

下面我們將用她來畫一個驗證碼型別的圖片,通常驗證碼都有一些點和線來干擾,上面已經有畫線例子了,這裡展示怎麼畫點:

//畫點(規則的點,其他的各位自行寫演算法) 
            var dianWith = 1; //點寬度
            var xx = 300;  //圖片寬度
            var yy = 200;  //圖片高度

            var xx_space = 10;  //點與點之間x座標間隔
            var yy_space = 5;    //y座標間隔

            var listPath = new List<IPath>();
            for (int i = 0; i < xx / xx_space; i++)
            {
                for (int j = 0; j < yy / yy_space; j++)
                {
                    var position = new Vector2(i * xx_space, j * yy_space);
                    var linerLine = new LinearLineSegment(position, position);
                    var shapesPath = new SixLabors.Shapes.Path(linerLine);
                    listPath.Add(shapesPath);
                }
            }

            using (Image<Rgba32> image = new Image<Rgba32>(xx, yy))   //畫布大小
            {
                image.Mutate(x => x.
                        BackgroundColor(Rgba32.WhiteSmoke).   //畫布背景
                            Draw(
                            Pens.Dot(Rgba32.HotPink, dianWith),   //大小
                            new SixLabors.Shapes.PathCollection(listPath)  //座標集合
                        )
                    );
                image.Save($"{path}/9.png"); //儲存
            }

這裡直接利用IImageProcessingContext<TPixel>擴充套件方法Draw來繪製有規則的點,如圖所示:

比較單調,或許您們能做的更好看些;下面來做驗證碼圖片,主要由:畫點+畫字=驗證碼圖片,這裡我封裝了一個方法直接生成驗證碼圖片:

/// <summary>
        /// 畫點+畫字=驗證碼圖片  
        /// </summary>
        /// <param name="content">驗證碼</param>
        /// <param name="outImgPath">輸出圖片路徑</param>
        /// <param name="fontFilePath">字型檔案</param>
        /// <param name="x">圖片寬度</param>
        /// <param name="y">圖片高度</param>
        public void GetValidCode(
                    string content = "我是神牛",
                    string outImgPath = "D:/F/學習/vs2017/netcore/Study.AspNetCore/WebApp02-1/wwwroot/images/10.png",
                    string fontFilePath = @"D:\F\學習\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\bak\STKAITI.TTF",
                    int xx = 150, int yy = 25)
        {
            var dianWith = 1; //點寬度
            var xx_space = 10;  //點與點之間x座標間隔
            var yy_space = 5;    //y座標間隔
            var wenZiLen = content.Length;  //文字長度
            var maxX = xx / wenZiLen; //每個文字最大x寬度
            var prevWenZiX = 0; //前面一個文字的x座標
            var size = 16;//字型大小

            //字型
            var install_Family = new FontCollection().Install(
              fontFilePath
              //@"C:\Windows\Fonts\STKAITI.TTF"   //windows系統下字型檔案
              );
            var font = new Font(install_Family, size);  //字型

            //點座標
            var listPath = new List<IPath>();
            for (int i = 0; i < xx / xx_space; i++)
            {
                for (int j = 0; j < yy / yy_space; j++)
                {
                    var position = new Vector2(i * xx_space, j * yy_space);
                    var linerLine = new LinearLineSegment(position, position);
                    var shapesPath = new SixLabors.Shapes.Path(linerLine);
                    listPath.Add(shapesPath);
                }
            }

            //畫圖
            using (Image<Rgba32> image = new Image<Rgba32>(xx, yy))   //畫布大小
            {
                image.Mutate(x =>
                {
                    //畫點
                    var imgProc = x.BackgroundColor(Rgba32.WhiteSmoke).   //畫布背景
                              Draw(
                              Pens.Dot(Rgba32.HotPink, dianWith),   //大小
                              new SixLabors.Shapes.PathCollection(listPath)  //座標集合
                          );

                    //逐個畫字
                    for (int i = 0; i < wenZiLen; i++)
                    {
                        //當前的要輸出的字
                        var nowWenZi = content.Substring(i, 1);

                        //文字座標
                        var wenXY = new Vector2();
                        var maxXX = prevWenZiX + (maxX - size);
                        wenXY.X = new Random().Next(prevWenZiX, maxXX);
                        wenXY.Y = new Random().Next(0, yy - size);

                        prevWenZiX = Convert.ToInt32(Math.Floor(wenXY.X)) + size;

                        //畫字
                        imgProc.DrawText(
                           nowWenZi,   //文字內容
                           font,
                           i % 2 > 0 ? Rgba32.HotPink : Rgba32.Red,
                           wenXY,
                           TextGraphicsOptions.Default);
                    }
                });
                //儲存到圖片
                image.Save(outImgPath);
            }
        }

通過簡單的呼叫 GetValidCode("我是神牛");return Page(); 能得到如圖驗證碼圖片的效果:

文字看起來好像在點的前面,不過沒關係只需要把畫點和畫字的先後順序修改下就行了,這裡不貼圖了;

結合RazorPage模板,展示驗證碼圖片

上面一節是生成了驗證碼圖片,當然實際場景中我們是不需要生成驗證碼物理圖片的,只需要返回一個流或base64等方式輸出到web介面上就行了,我們可以來看看 Image<TPixel> 儲存時候的擴充套件方法:

        //
        // 摘要:
        //     Saves the image to the given stream using the currently loaded image format.
        //
        // 引數:
        //   source:
        //     The source image
        //
        //   filePath:
        //     The file path to save the image to.
        //
        // 型別引數:
        //   TPixel:
        //     The Pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void Save<TPixel>(this Image<TPixel> source, string filePath) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream using the currently loaded image format.
        //
        // 引數:
        //   source:
        //     The source image
        //
        //   filePath:
        //     The file path to save the image to.
        //
        //   encoder:
        //     The encoder to save the image with.
        //
        // 型別引數:
        //   TPixel:
        //     The Pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the encoder is null.
        public static void Save<TPixel>(this Image<TPixel> source, string filePath, IImageEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream using the currently loaded image format.
        //
        // 引數:
        //   source:
        //     The source image
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   format:
        //     The format to save the image to.
        //
        // 型別引數:
        //   TPixel:
        //     The Pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void Save<TPixel>(this Image<TPixel> source, Stream stream, IImageFormat format) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the bmp format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the bmp format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The encoder to save the image with.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsBmp<TPixel>(this Image<TPixel> source, Stream stream, BmpEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the gif format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The options for the encoder.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream, GifEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the gif format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsGif<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the jpeg format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The options for the encoder.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream, JpegEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the jpeg format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsJpeg<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the png format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the image to the given stream with the png format.
        //
        // 引數:
        //   source:
        //     The image this method extends.
        //
        //   stream:
        //     The stream to save the image to.
        //
        //   encoder:
        //     The options for the encoder.
        //
        // 型別引數:
        //   TPixel:
        //     The pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SaveAsPng<TPixel>(this Image<TPixel> source, Stream stream, PngEncoder encoder) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the raw image to the given bytes.
        //
        // 引數:
        //   source:
        //     The source image
        //
        //   buffer:
        //     The buffer to save the raw pixel data to.
        //
        // 型別引數:
        //   TPixel:
        //     The Pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SavePixelData<TPixel>(this ImageFrame<TPixel> source, byte[] buffer) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the raw image to the given bytes.
        //
        // 引數:
        //   source:
        //     The source image
        //
        // 型別引數:
        //   TPixel:
        //     The Pixel format.
        //
        // 返回結果:
        //     A copy of the pixel data as bytes from this frame.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static byte[] SavePixelData<TPixel>(this ImageFrame<TPixel> source) where TPixel : struct, IPixel<TPixel>;
        //
        // 摘要:
        //     Saves the raw image to the given bytes.
        //
        // 引數:
        //   source:
        //     The source image
        //
        //   buffer:
        //     The buffer to save the raw pixel data to.
        //
        // 型別引數:
        //   TPixel:
        //     The Pixel format.
        //
        // 異常:
        //   T:System.ArgumentNullException:
        //     Thrown if the stream is null.
        public static void SavePixelData<TPixel>(this Image<TPixel> source, byte[] buffer) where TPixel : struct, IPixel<TPixel>;
View Code

好吧有點多,我們只需要明白她能轉base64,stream,儲存為圖片等就行了;這裡我們將用到 SaveAsPng(Stream) 方法,然後獲取他的byte[],如下程式碼:

/// <summary>
        /// 畫點+畫字=驗證碼byte[]
        /// </summary>
        /// <param name="content">驗證碼</param>
        /// <param name="outImgPath">輸出圖片路徑</param>
        /// <param name="fontFilePath">字型檔案</param>
        /// <param name="x">圖片寬度</param>
        /// <param name="y">圖片高度</param>
        public byte[] GetValidCodeByte(
                    string content = "我是神牛",
                    string fontFilePath = @"D:\F\學習\vs2017\netcore\Study.AspNetCore\WebApp02-1\wwwroot\bak\STKAITI.TTF",
                    int xx = 150, int yy = 25)
        {
            var bb = default(byte[]);
            try
            {
                var dianWith = 1; //點寬度
                var xx_space = 10;  //點與點之間x座標間隔
                var yy_space = 5;    //y座標間隔
                var wenZiLen = content.Length;  //文字長度
                var maxX = xx / wenZiLen; //每個文字最大x寬度
                var prevWenZiX = 0; //前面一個文字的x座標
                var size = 16;//字型大小

                //字型
                var install_Family = new FontCollection().Install(
                  fontFilePath
                  //@"C:\Windows\Fonts\STKAITI.TTF"   //windows系統下字型檔案
                  );
                var font = new Font(install_Family, size);  //字型

                //點座標
                var listPath = new List<IPath>();
                for (int i = 0; i < xx / xx_space; i++)
                {
                    for (int j = 0; j < yy / yy_space; j++)
                    {
                        var position = new Vector2(i * xx_space, j * yy_space);
                        var linerLine = new LinearLineSegment(position, position);
                        var shapesPath = new SixLabors.Shapes.Path(linerLine);
                        listPath.Add(shapesPath);
                    }
                }

                //畫圖
                using (Image<Rgba32> image = new Image<Rgba32>(xx, yy))   //畫布大小
                {
                    image.Mutate(x =>
                    {
                        var imgProc = x;

                        //逐個畫字
                        for (int i = 0; i < wenZiLen; i++)
                        {
                            //當前的要輸出的字
                            var nowWenZi = content.Substring(i, 1);

                            //文字座標
                            var wenXY = new Vector2();
                            var maxXX = prevWenZiX + (maxX - size);
                            wenXY.X = new Random().Next(prevWenZiX, maxXX);
                            wenXY.Y = new Random().Next(0, yy - size);

                            prevWenZiX = Convert.ToInt32(Math.Floor(wenXY.X)) + size;

                            //畫字
                            imgProc.DrawText(
                                   nowWenZi,   //文字內容
                                   font,
                                   i % 2 > 0 ? Rgba32.HotPink : Rgba32.Red,
                                   wenXY,
                                   TextGraphicsOptions.Default);
                        }

                        //畫點 
                        imgProc.BackgroundColor(Rgba32.WhiteSmoke).   //畫布背景
                                     Draw(
                                     Pens.Dot(Rgba32.HotPink, dianWith),   //大小
                                     new SixLabors.Shapes.PathCollection(listPath)  //座標集合
                                 );
                    });
                    using (MemoryStream stream = new MemoryStream())
                    {
                        image.SaveAsPng(stream);
                        bb = stream.GetBuffer();
                    }
                }
            }
            catch (Exception ex)
            {
            }
            return bb;
        }

該方法返回了一個byte[]陣列,然後通過HttpGet方式請求Razor介面,前端就能夠獲取到這個驗證碼圖片byte[]了;

/// <summary>
        /// Get獲取驗證碼圖片byte[]
        /// </summary>
        /// <returns></returns>
        public FileResult OnGetValidCode()
        {
            var codebb = GetValidCodeByte(DateTime.Now.ToString("mmssfff"));
            return File(codebb, "image/png");
        }

我們通過get請求獲取驗證碼: http://localhost:1120/login?handler=ValidCode ,然後得到如圖效果:

本篇內容到此就結束了,如果對您有好的幫助,不妨點個“贊”;一起努力推動NetCore發展吧,謝謝。

相關文章