using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Runtime.InteropServices; using System.Linq; using System.Text; using System.Threading.Tasks; using PaintDotNet.Camera.TUC; using OpenCvSharp; namespace PaintDotNet.Camera { /// /// Bitmap转化 /// public static class Tools { static Bitmap currBitmap; public static Bitmap ToBitmap(this byte[] rawValues, int width, int height, PixelFormat pixelFormat) { //// 申请目标位图的变量,并将其内存区域锁定 try { if (currBitmap == null || width != currBitmap.Width || height != currBitmap.Height || pixelFormat != currBitmap.PixelFormat) { currBitmap = new Bitmap(width, height, pixelFormat); } var rect = new Rectangle(0, 0, width, height); var bitmapData = currBitmap.LockBits(rect, ImageLockMode.WriteOnly, pixelFormat); IntPtr iptr = bitmapData.Scan0; // 获取bmpData的内存起始位置 int size = width * height; if (pixelFormat == PixelFormat.Format24bppRgb) size *= 3; Marshal.Copy(rawValues, 0, iptr, size); currBitmap.UnlockBits(bitmapData); return currBitmap; } catch { return null; } } public static byte[] ToByteArray(this Bitmap bitmap) { BitmapData bmpdata = null; try { bmpdata = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, bitmap.PixelFormat); int numbytes = bmpdata.Stride * bitmap.Height; byte[] bytedata = new byte[numbytes]; IntPtr ptr = bmpdata.Scan0; Marshal.Copy(ptr, bytedata, 0, numbytes); return bytedata; } finally { if (bmpdata != null) bitmap.UnlockBits(bmpdata); } } public static Bitmap ToBitmap(this TUCAM_FRAME frame) { var width = (int)(frame.usWidth); var height = (int)(frame.usHeight); int nSize = (int)(frame.uiImgSize + frame.usHeader); var buff = new byte[nSize]; Marshal.Copy(frame.pBuffer, buff, 0, nSize); Buffer.BlockCopy(buff, (int)(frame.usHeader), buff, 0, (int)(frame.uiImgSize)); Bitmap bitmap; if (frame.ucChannels == 1) { bitmap = buff.ToBitmap(width, height, PixelFormat.Format8bppIndexed); if (bitmap == null) { return null; } // 定义灰度调色板 System.Drawing.Imaging.ColorPalette GreyColorPalette = bitmap.Palette; for (int Index = 0; Index <= byte.MaxValue; Index++) { GreyColorPalette.Entries[Index] = Color.FromArgb(byte.MaxValue, Index, Index, Index); } bitmap.Palette = GreyColorPalette; //bitmap.Palette.Flags = 4; } else { bitmap = buff.ToBitmap(width, height, PixelFormat.Format24bppRgb); ; } return bitmap; } public static Bitmap CreatBitmap(this TUCAM_FRAME frame) { var width = (int)(frame.usWidth); var height = (int)(frame.usHeight); int nSize = (int)(frame.uiImgSize + frame.usHeader); var buff = new byte[nSize]; Marshal.Copy(frame.pBuffer, buff, 0, nSize); Buffer.BlockCopy(buff, (int)(frame.usHeader), buff, 0, (int)(frame.uiImgSize)); Bitmap bitmap; PixelFormat pixelFormat; if (frame.ucChannels == 1) { pixelFormat = PixelFormat.Format8bppIndexed; } else { pixelFormat = PixelFormat.Format24bppRgb; } bitmap = new Bitmap(width, height, pixelFormat); var rect = new Rectangle(0, 0, width, height); var bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, pixelFormat); IntPtr iptr = bitmapData.Scan0; // 获取bmpData的内存起始位置 int size = width * height; if (pixelFormat == PixelFormat.Format24bppRgb) size *= 3; Marshal.Copy(buff, 0, iptr, size); bitmap.UnlockBits(bitmapData); if (frame.ucChannels == 1) { // 定义灰度调色板 System.Drawing.Imaging.ColorPalette GreyColorPalette = bitmap.Palette; for (int Index = 0; Index <= byte.MaxValue; Index++) { GreyColorPalette.Entries[Index] = Color.FromArgb(byte.MaxValue, Index, Index, Index); } bitmap.Palette = GreyColorPalette; } return bitmap; } #region ToMat /// /// Converts System.Drawing.Bitmap to Mat /// /// System.Drawing.Bitmap object to be converted /// A Mat object which is converted from System.Drawing.Bitmap public static Mat ToMat(this Bitmap src) { if (src == null) throw new ArgumentNullException(nameof(src)); int w = src.Width; int h = src.Height; int channels; switch (src.PixelFormat) { case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppRgb: channels = 3; break; case PixelFormat.Format32bppArgb: case PixelFormat.Format32bppPArgb: channels = 4; break; case PixelFormat.Format8bppIndexed: case PixelFormat.Format1bppIndexed: channels = 1; break; default: throw new NotImplementedException(); } Mat dst = new Mat(h, w, MatType.CV_8UC(channels)); //return OpenCvSharp.Extensions.BitmapConverter.ToMat(src); ToMat(src, dst); return dst; } /// /// Converts System.Drawing.Bitmap to Mat /// /// System.Drawing.Bitmap object to be converted /// A Mat object which is converted from System.Drawing.Bitmap public static unsafe void ToMat(this Bitmap src, Mat dst) { if (src == null) throw new ArgumentNullException(nameof(src)); if (dst == null) throw new ArgumentNullException(nameof(dst)); if (dst.IsDisposed) throw new ArgumentException("The specified dst is disposed.", nameof(dst)); if (dst.Depth() != MatType.CV_8U) throw new NotSupportedException("Mat depth != CV_8U"); if (dst.Dims != 2) throw new NotSupportedException("Mat dims != 2"); if (src.Width != dst.Width || src.Height != dst.Height) throw new ArgumentException("src.Size != dst.Size"); int w = src.Width; int h = src.Height; Rectangle rect = new Rectangle(0, 0, w, h); BitmapData bd = null; try { bd = src.LockBits(rect, ImageLockMode.ReadOnly, src.PixelFormat); switch (src.PixelFormat) { case PixelFormat.Format1bppIndexed: Format1bppIndexed(); break; case PixelFormat.Format8bppIndexed: Format8bppIndexed(); break; case PixelFormat.Format24bppRgb: Format24bppRgb(); break; case PixelFormat.Format32bppRgb: case PixelFormat.Format32bppArgb: case PixelFormat.Format32bppPArgb: Format32bppRgb(); break; } } finally { if (bd != null) src.UnlockBits(bd); } // ReSharper disable once InconsistentNaming void Format1bppIndexed() { if (dst.Channels() != 1) throw new ArgumentException("Invalid nChannels"); if (dst.IsSubmatrix()) throw new NotImplementedException("submatrix not supported"); if (bd == null) throw new NotSupportedException("BitmapData == null (Format1bppIndexed)"); byte* srcPtr = (byte*)bd.Scan0.ToPointer(); byte* dstPtr = dst.DataPointer; int srcStep = bd.Stride; uint dstStep = (uint)dst.Step(); int x = 0; for (int y = 0; y < h; y++) { // 横は必ず4byte幅に切り上げられる。 // この行の各バイトを調べていく for (int bytePos = 0; bytePos < srcStep; bytePos++) { if (x < w) { // 現在の位置のバイトからそれぞれのビット8つを取り出す byte b = srcPtr[bytePos]; for (int i = 0; i < 8; i++) { if (x >= w) { break; } // IplImageは8bit/pixel dstPtr[dstStep * y + x] = ((b & 0x80) == 0x80) ? (byte)255 : (byte)0; b <<= 1; x++; } } } // 次の行へ x = 0; srcPtr += srcStep; } } void Ch1(Mat dst1, int height, int srcStep, uint dstStep, IntPtr srcData, byte[] palette) { if (dstStep == srcStep && !dst1.IsSubmatrix() && dst1.IsContinuous()) { // Read Bitmap pixel data to managed array long length = dst1.DataEnd.ToInt64() - dst1.Data.ToInt64(); if (length > int.MaxValue) throw new NotSupportedException("Too big dst Mat"); var buffer = new byte[length]; Marshal.Copy(srcData, buffer, 0, buffer.Length); // Apply conversion by palette buffer = buffer.Select(b => palette[b]).ToArray(); // Write to dst Mat Marshal.Copy(buffer, 0, dst1.Data, buffer.Length); } else { // Copy line bytes from src to dst for each line byte* sp = (byte*)srcData; byte* dp = (byte*)dst1.Data; var buffer = new byte[srcStep]; for (int y = 0; y < height; y++) { // Read Bitmap pixel data to managed array Marshal.Copy(new IntPtr(sp), buffer, 0, buffer.Length); // Apply conversion by palette buffer = buffer.Select(b => palette[b]).ToArray(); // Write to dst Mat Marshal.Copy(buffer, 0, new IntPtr(dp), buffer.Length); sp += srcStep; dp += dstStep; } } } // ReSharper disable once InconsistentNaming void Format8bppIndexed() { int srcStep = bd.Stride; uint dstStep = (uint)dst.Step(); int channels = dst.Channels(); if (channels == 1) { var palette = new byte[256]; var paletteLength = Math.Min(256, src.Palette.Entries.Length); for (int i = 0; i < paletteLength; i++) { // TODO src.Palette.Flags & 2 == 2 // https://docs.microsoft.com/ja-jp/dotnet/api/system.drawing.imaging.colorpalette.flags?view=netframework-4.8 palette[i] = src.Palette.Entries[i].R; } Ch1(dst, h, srcStep, dstStep, bd.Scan0, palette); } else if (channels == 3) { // Palette var paletteR = new byte[256]; var paletteG = new byte[256]; var paletteB = new byte[256]; var paletteLength = Math.Min(256, src.Palette.Entries.Length); for (int i = 0; i < paletteLength; i++) { var c = src.Palette.Entries[i]; paletteR[i] = c.R; paletteG[i] = c.G; paletteB[i] = c.B; } using (var dstR = new Mat(h, w, MatType.CV_8UC1)) using (var dstG = new Mat(h, w, MatType.CV_8UC1)) using (var dstB = new Mat(h, w, MatType.CV_8UC1)) { Ch1(dstR, h, srcStep, (uint)dstR.Step(), bd.Scan0, paletteR); Ch1(dstG, h, srcStep, (uint)dstG.Step(), bd.Scan0, paletteG); Ch1(dstB, h, srcStep, (uint)dstB.Step(), bd.Scan0, paletteB); Cv2.Merge(new[] { dstB, dstG, dstR }, dst); } } else { throw new ArgumentException($"Invalid channels of dst Mat ({channels})"); } } // ReSharper disable once InconsistentNaming void Format24bppRgb() { if (dst.Channels() != 3) throw new ArgumentException("Invalid nChannels"); if (dst.Depth() != MatType.CV_8U && dst.Depth() != MatType.CV_8S) throw new ArgumentException("Invalid depth of dst Mat"); int srcStep = bd.Stride; long dstStep = dst.Step(); if (dstStep == srcStep && !dst.IsSubmatrix() && dst.IsContinuous()) { IntPtr dstData = dst.Data; long bytesToCopy = dst.DataEnd.ToInt64() - dstData.ToInt64(); Buffer.MemoryCopy(bd.Scan0.ToPointer(), dstData.ToPointer(), bytesToCopy, bytesToCopy); } else { // Copy line bytes from src to dst for each line byte* sp = (byte*)bd.Scan0; byte* dp = (byte*)dst.Data; for (int y = 0; y < h; y++) { Buffer.MemoryCopy(sp, dp, dstStep, dstStep); sp += srcStep; dp += dstStep; } } } // ReSharper disable once InconsistentNaming void Format32bppRgb() { int srcStep = bd.Stride; long dstStep = dst.Step(); switch (dst.Channels()) { case 4: if (!dst.IsSubmatrix() && dst.IsContinuous()) { IntPtr dstData = dst.Data; long bytesToCopy = dst.DataEnd.ToInt64() - dstData.ToInt64(); Buffer.MemoryCopy(bd.Scan0.ToPointer(), dstData.ToPointer(), bytesToCopy, bytesToCopy); } else { byte* sp = (byte*)bd.Scan0; byte* dp = (byte*)dst.Data; for (int y = 0; y < h; y++) { Buffer.MemoryCopy(sp, dp, dstStep, dstStep); sp += srcStep; dp += dstStep; } } break; case 3: byte* srcPtr = (byte*)bd.Scan0.ToPointer(); byte* dstPtr = (byte*)dst.Data.ToPointer(); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { dstPtr[y * dstStep + x * 3 + 0] = srcPtr[y * srcStep + x * 4 + 0]; dstPtr[y * dstStep + x * 3 + 1] = srcPtr[y * srcStep + x * 4 + 1]; dstPtr[y * dstStep + x * 3 + 2] = srcPtr[y * srcStep + x * 4 + 2]; } } break; default: throw new ArgumentException("Invalid nChannels"); } } } #endregion #region ToBitmap /* /// /// Converts Mat to System.Drawing.Bitmap /// /// Mat /// public static Bitmap ToBitmap(this Mat src) { if (src == null) { throw new ArgumentNullException(nameof(src)); } PixelFormat pf; switch (src.Channels()) { case 1: pf = PixelFormat.Format8bppIndexed; break; case 3: pf = PixelFormat.Format24bppRgb; break; case 4: pf = PixelFormat.Format32bppArgb; break; default: throw new ArgumentException("Number of channels must be 1, 3 or 4.", nameof(src)); } return ToBitmap(src, pf); } /// /// Converts Mat to System.Drawing.Bitmap /// /// Mat /// Pixel Depth /// public static Bitmap ToBitmap(this Mat src, PixelFormat pf) { if (src == null) throw new ArgumentNullException(nameof(src)); src.ThrowIfDisposed(); Bitmap bitmap = new Bitmap(src.Width, src.Height, pf); ToBitmap(src, bitmap); return bitmap; } /// /// Converts Mat to System.Drawing.Bitmap /// /// Mat /// Mat /// Author: shimat, Gummo (ROI support) public static unsafe void ToBitmap(this Mat src, Bitmap dst) { if (src == null) throw new ArgumentNullException(nameof(src)); if (dst == null) throw new ArgumentNullException(nameof(dst)); if (src.IsDisposed) throw new ArgumentException("The image is disposed.", nameof(src)); if (src.Depth() != MatType.CV_8U) throw new ArgumentException("Depth of the image must be CV_8U"); //if (src.IsSubmatrix()) // throw new ArgumentException("Submatrix is not supported"); if (src.Width != dst.Width || src.Height != dst.Height) throw new ArgumentException(""); PixelFormat pf = dst.PixelFormat; // 1プレーン用の場合、グレースケールのパレット情報を生成する if (pf == PixelFormat.Format8bppIndexed) { ColorPalette plt = dst.Palette; for (int x = 0; x < 256; x++) { plt.Entries[x] = Color.FromArgb(x, x, x); } dst.Palette = plt; } int w = src.Width; int h = src.Height; Rectangle rect = new Rectangle(0, 0, w, h); BitmapData bd = null; bool submat = src.IsSubmatrix(); bool continuous = src.IsContinuous(); try { bd = dst.LockBits(rect, ImageLockMode.WriteOnly, pf); IntPtr srcData = src.Data; byte* pSrc = (byte*)(srcData.ToPointer()); byte* pDst = (byte*)(bd.Scan0.ToPointer()); int ch = src.Channels(); int srcStep = (int)src.Step(); int dstStep = ((src.Width * ch) + 3) / 4 * 4; // 4の倍数に揃える int stride = bd.Stride; switch (pf) { case PixelFormat.Format1bppIndexed: { if (submat) throw new NotImplementedException("submatrix not supported"); // BitmapDataは4byte幅だが、IplImageは1byte幅 // 手作業で移し替える //int offset = stride - (w / 8); int x = 0; byte b = 0; for (int y = 0; y < h; y++) { for (int bytePos = 0; bytePos < stride; bytePos++) { if (x < w) { for (int i = 0; i < 8; i++) { var mask = (byte)(0x80 >> i); if (x < w && pSrc[srcStep * y + x] == 0) b &= (byte)(mask ^ 0xff); else b |= mask; x++; } pDst[bytePos] = b; } } x = 0; pDst += stride; } break; } case PixelFormat.Format8bppIndexed: case PixelFormat.Format24bppRgb: case PixelFormat.Format32bppArgb: if (srcStep == dstStep && !submat && continuous) { long bytesToCopy = src.DataEnd.ToInt64() - src.Data.ToInt64(); Buffer.MemoryCopy(pSrc, pDst, bytesToCopy, bytesToCopy); } else { for (int y = 0; y < h; y++) { long offsetSrc = (y * srcStep); long offsetDst = (y * dstStep); long bytesToCopy = w * ch; // 一列ごとにコピー Buffer.MemoryCopy(pSrc + offsetSrc, pDst + offsetDst, bytesToCopy, bytesToCopy); } } break; default: throw new NotImplementedException(); } } finally { if (bd != null) dst.UnlockBits(bd); } }*/ #endregion } }