C# Bitmap相关操作和转换

 好久更新了,随便找点东西填一填。

主要功能

部分函数假定输入的bitmap是24bpprgb格式。

GetColorEntropy

获取图片的色彩熵,一定程度的反映了图片的质量。

从一段c++代码翻译过来,代码风格有点怪。


GetRgbHistogram

获取图片的RGB颜色的直方图统计。


CropRGBValues

从一个RGB数组里获取一个矩形里的RGB数组。


CropRGBValuesFromBitmapFrame

从一个BitmapFrame里获取一个矩形里的RGB数组,这个函数其实是先转成Bitmap然后重画一个bitmap。


GetRGBValuesFromBitmap

获取一个Bitmap的RGB数组。


GetRGBValuesFromBitmapFrame

获取一个BitmapFrame的RGB数组。


WriteRGBValuesToBitmap

把一个RGB数组写到一个Bitmap中,方便保存出来看看模样。


GetBitmapFromBitmapSource

利用BitmapData把RGB数据从BitmapSource拷贝到Bitmap。

也可以利用BmpBitmapEncoder,先把数据拷到MemoryStream,然后再从MemoryStream构造Bitmap。

BitmapFrame继承了BitmapSource,所以这个函数也可以传BitmapFrame。


GetBitmapFromBitmapFrame

同上,不过这里用的BmpBitmapEncoder。


GetBitmapSourceFromBitmap

从Bitmap里获取BitmapSource。


GetBitmapFrameFromBitmap

先获取BitmapSource,然后Create BitmapFrame。


代码

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media.Imaging;

namespace MyTools
{
    class BitmapHelper
    {
        public static double GetColorEntropy(byte[] rgbValues, int dwWidth, int dwHeight, int lStride)
        {
            int dwRSize = 16, dwGSize = 16, dwBSize = 16;
            int dwNumBins = dwRSize * dwGSize * dwBSize;
            var pdHistogramForColorEntropy = GetRgbHistogram(rgbValues, dwWidth, dwHeight, lStride);
            // Compute the entropy based on the histogram.
            double dEntropy = 0.0;
            for (int n = 0; n < dwNumBins; n++)
            {
                if (pdHistogramForColorEntropy[n] > 0.0)
                {
                    dEntropy += -pdHistogramForColorEntropy[n] * Math.Log(pdHistogramForColorEntropy[n]);
                }
            }

            dEntropy /= Math.Log(2.0);
            if (dEntropy < 0.0)
            {
                dEntropy = 0.0;
            }

            return dEntropy;
        }
        
        public static double[] GetRgbHistogram(byte[] rgbValues, int dwWidth, int dwHeight, int lStride)
        {
            int dwRSize = 16, dwGSize = 16, dwBSize = 16;
            int c_dwProcessImageWidth = 80;
            int c_dwProcessImageHeight = 60;
            int dwNumBins = dwRSize * dwGSize * dwBSize;
            double[] pdHistogram = new double[dwNumBins];
            Array.Clear(pdHistogram, 0, pdHistogram.Length);
            int dwSubsampleRatioX = Math.Max(dwWidth / c_dwProcessImageWidth, 1);
            int dwSubsampleRatioY = Math.Max(dwHeight / c_dwProcessImageHeight, 1);
            int dwNumSamples = (dwWidth / dwSubsampleRatioX) * (dwHeight / dwSubsampleRatioY);

            float fRQtzRatio = (dwRSize - 1) / 255.0f;
            float fGQtzRatio = (dwGSize - 1) / 255.0f;
            float fBQtzRatio = (dwBSize - 1) / 255.0f;

            long lInputXOffset = dwSubsampleRatioX * lStride;
            long lInputYOffset = dwSubsampleRatioY * lStride;
            for (int y = 0; y < dwHeight; y += dwSubsampleRatioY)
            {
                for (int x = 0; x < dwWidth; x += dwSubsampleRatioX)
                {
                    int idx = (y * dwWidth + x) * 3;
                    int dwB = (int)(rgbValues[idx] * fRQtzRatio);
                    int dwG = (int)(rgbValues[idx + 1] * fGQtzRatio);
                    int dwR = (int)(rgbValues[idx + 2] * fBQtzRatio);

                    pdHistogram[dwB * dwGSize * dwRSize + dwG * dwRSize + dwR] += 1.0;
                }
            }

            for (int n = 0; n < dwNumBins; n++)
            {
                pdHistogram[n] /= dwNumSamples;
            }

            return pdHistogram;
        }
        public static byte[] CropRGBValues(byte[] rgbValues, int width, int height, Rectangle rect)
        {
            int descStride = rect.Width * 3;
            int srcStride = width * 3;
            int destOffset = 0;
            int srcOffset = (rect.Y * rect.Width + rect.X) * 3;
            byte[] cropValues = new byte[rect.Width * rect.Height * 3];
            for (int i = 0; i < rect.Height; i++)
            {
                Array.Copy(rgbValues, srcOffset, cropValues, destOffset, descStride);
                srcOffset += srcStride;
                destOffset += descStride;
            }

            return cropValues;
        }

        public static byte[] CropRGBValuesFromBitmapFrame(BitmapFrame bitmapFrame, Rectangle rect)
        {
            Bitmap target = new Bitmap(rect.Width, rect.Height, PixelFormat.Format24bppRgb);

            BmpBitmapEncoder encoder = new BmpBitmapEncoder();
            encoder.Frames.Add(bitmapFrame);
            using (MemoryStream ms1 = new MemoryStream())
            {
                encoder.Save(ms1);
                Bitmap src = new Bitmap(ms1);
                using (Graphics g = Graphics.FromImage(target))
                {
                    g.DrawImage(src, new Rectangle(0, 0, target.Width, target.Height),
                                     rect,
                                     GraphicsUnit.Pixel);
                }
            }

            return GetRGBValuesFromBitmap(target);
        }
        public static byte[] GetRGBValuesFromBitmap(Bitmap bmp)
        {
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            System.Drawing.Imaging.BitmapData bmpData =
                bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadOnly,
                bmp.PixelFormat);

            int st = bmpData.Stride;
            IntPtr ptr = bmpData.Scan0;
            // Declare an array to hold the bytes of the bitmap.
            int stride = 3;
            if (bmp.PixelFormat != PixelFormat.Format24bppRgb)
            {
                throw new Exception("doesn't support this format");
            }

            int bytes = stride * rect.Width * rect.Height;
            byte[] rgbValues = new byte[bytes];
            // Copy the RGB values into the array.
            System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes);
            bmp.UnlockBits(bmpData);
            return rgbValues;
        }

        public static byte[] GetRGBValuesFromBitmapFrame(BitmapFrame bitmapFrame)
        {
            int width = bitmapFrame.PixelWidth;
            int height = bitmapFrame.PixelHeight;
            int bitsPerPixel = bitmapFrame.Format.BitsPerPixel;
            int stride = (bitsPerPixel >> 3) * width;
            if (bitsPerPixel != 24)
            {
                throw new IOException("expected 24 BPP JXR, but got " + bitsPerPixel);
            }

            byte[] rgbValues = new byte[width * height * 3];
            bitmapFrame.CopyPixels(rgbValues, stride, 0);
            return rgbValues;
        }

        public static Bitmap WriteRGBValuesToBitmap(byte[] rgbValues, int width, int height)
        {
            if (rgbValues.Length != width * height * 3)
            {
                throw new Exception("invalid data");
            }

            Bitmap bmp = new Bitmap(width, height, PixelFormat.Format24bppRgb);
            Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
            System.Drawing.Imaging.BitmapData bmpData =
               bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.WriteOnly,
               bmp.PixelFormat);

            int bytes = Math.Abs(bmpData.Stride) * bmp.Height;
            IntPtr ptr = bmpData.Scan0;
            System.Runtime.InteropServices.Marshal.Copy(rgbValues, 0, ptr, bytes);
            bmp.UnlockBits(bmpData);
            return bmp;
        }

        //https://stackoverflow.com/questions/2284353/is-there-a-good-way-to-convert-between-bitmapsource-and-bitmap
        public static Bitmap GetBitmapFromBitmapSource(BitmapSource source)
        {
            Bitmap bmp = new Bitmap(
              source.PixelWidth,
              source.PixelHeight,
              PixelFormat.Format24bppRgb);
            BitmapData data = bmp.LockBits(
              new Rectangle(Point.Empty, bmp.Size),
              ImageLockMode.WriteOnly,
              PixelFormat.Format24bppRgb);
            source.CopyPixels(
              System.Windows.Int32Rect.Empty,
              data.Scan0,
              data.Height * data.Stride,
              data.Stride);
            bmp.UnlockBits(data);
            return bmp;
        }

        public static Bitmap GetBitmapFromBitmapFrame(BitmapFrame source)
        {
            // method 1.
            //GetBitmapFromBitmapSource(source);
            // method 2.
            Bitmap bitmap;
            using (var outStream = new MemoryStream())
            {
                BitmapEncoder enc = new BmpBitmapEncoder();
                enc.Frames.Add(source);
                enc.Save(outStream);
                bitmap = new Bitmap(outStream);
            }
            return bitmap;
        }

        public static BitmapSource GetBitmapSourceFromBitmap(Bitmap bmp)
        {
            return System.Windows.Interop.Imaging.CreateBitmapSourceFromHBitmap(
                          bmp.GetHbitmap(),
                          IntPtr.Zero,
                          System.Windows.Int32Rect.Empty,
                          BitmapSizeOptions.FromEmptyOptions());
        }

        public static BitmapFrame GetBitmapFrameFromBitmap(Bitmap bmp)
        {
            return BitmapFrame.Create(GetBitmapSourceFromBitmap(bmp));
        }
    }
}


留言: