725 lines
30 KiB
C#
725 lines
30 KiB
C#
using HalconDotNet;
|
||
using OpenCvSharp;
|
||
using OpenCvSharp.Extensions;
|
||
using System;
|
||
using System.Drawing;
|
||
using System.Drawing.Imaging;
|
||
using System.IO;
|
||
using System.Linq;
|
||
using System.Runtime.InteropServices;
|
||
using System.Threading.Tasks;
|
||
|
||
namespace DH.Commons.Enums
|
||
{
|
||
public class OpenCVEngineTool : IDisposable
|
||
{
|
||
public void Dispose()
|
||
{
|
||
throw new NotImplementedException();
|
||
}
|
||
|
||
|
||
}
|
||
|
||
|
||
public static class OpenCVHelper
|
||
{
|
||
[DllImport("kernel32.dll", EntryPoint = "RtlMoveMemory", SetLastError = false)]
|
||
public static extern void CopyMemory(IntPtr dest, IntPtr src, uint count);
|
||
|
||
|
||
public static byte[] MatToBytes(this Mat image)
|
||
{
|
||
if (image != null && image.Data != IntPtr.Zero)
|
||
{
|
||
int size = (int)(image.Total() * image.ElemSize());
|
||
byte[] bytes = new byte[size];
|
||
Marshal.Copy(image.Data, bytes, 0, size);
|
||
return bytes;
|
||
}
|
||
else
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取水平拼接图片
|
||
/// </summary>
|
||
/// <param name="map1"></param>
|
||
/// <param name="map2"></param>
|
||
/// <returns></returns>
|
||
public static Bitmap GetHConcatImage(Bitmap map1, Bitmap map2)
|
||
{
|
||
var mat1 = map1.ToMat();
|
||
var mat2 = map2.ToMat();
|
||
//var maxChannel = Math.Max(mat1.Channels(), mat2.Channels());
|
||
//var maxType = Math.Max(mat1.Type(), mat2.Type());
|
||
|
||
//转通道数
|
||
mat1 = mat1.CvtColor(ColorConversionCodes.GRAY2BGRA);
|
||
//转位深
|
||
mat1.ConvertTo(mat1, mat2.Type());
|
||
Mat resMat = new Mat(mat1.Height, mat1.Width, mat2.Type(), new Scalar(0));
|
||
Cv2.HConcat(mat1, mat2, resMat);
|
||
mat1.Dispose();
|
||
mat2.Dispose();
|
||
return resMat.ToBitmap();
|
||
}
|
||
|
||
public static Mat To3Channels(this Mat img)
|
||
{
|
||
if (img == null)
|
||
return null;
|
||
Mat resMat = new Mat(img.Rows, img.Cols, MatType.CV_8UC3);
|
||
Mat[] channels = new Mat[3]; ;
|
||
for (int i = 0; i < 3; i++)
|
||
{
|
||
channels[i] = img;
|
||
}
|
||
Cv2.Merge(channels, resMat);
|
||
img.Dispose();
|
||
img = null;
|
||
return resMat;
|
||
}
|
||
/// <summary>
|
||
/// 把OpenCV图像转换到Halcon图像
|
||
/// </summary>
|
||
/// <param name="mImage">OpenCV图像_Mat</param>
|
||
/// <returns>Halcon图像_HObject</returns>
|
||
public static HObject MatToHImage(Mat mImage)
|
||
{
|
||
try
|
||
{
|
||
HObject hImage;
|
||
int matChannels = 0; // 通道数
|
||
Type matType = null;
|
||
uint width, height; // 宽,高
|
||
width = height = 0; // 宽,高初始化
|
||
|
||
// 获取通道数
|
||
matChannels = mImage.Channels();
|
||
if (matChannels == 0)
|
||
{
|
||
return null;
|
||
}
|
||
if (matChannels == 1) // 单通道
|
||
//if (true) // 单通道
|
||
{
|
||
IntPtr ptr; // 灰度图通道
|
||
Mat[] mats = mImage.Split();
|
||
|
||
// 改自:Mat.GetImagePointer1(mImage, out ptr, out matType, out width, out height); // ptr=2157902018096 cType=byte width=830 height=822
|
||
ptr = mats[0].Data; // 取灰度图值
|
||
matType = mImage.GetType(); // byte
|
||
height = (uint)mImage.Rows; // 高
|
||
width = (uint)mImage.Cols; // 宽
|
||
|
||
// 改自:hImage = new HObject(new OpenCvSharp.Size(width, height), MatType.CV_8UC1, newScalar(0));
|
||
byte[] dataGrayScaleImage = new byte[width * height]; //Mat dataGrayScaleImage = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
|
||
|
||
unsafe
|
||
{
|
||
fixed (byte* ptrdata = dataGrayScaleImage)
|
||
{
|
||
|
||
#region 按行复制
|
||
//for (int i = 0; i < height; i++)
|
||
//{
|
||
// CopyMemory((IntPtr)(ptrdata + width * i), new IntPtr((long)ptr + width *i), width);
|
||
//}
|
||
|
||
#endregion
|
||
|
||
CopyMemory((IntPtr)ptrdata, new IntPtr((long)ptr), width * height);
|
||
HOperatorSet.GenImage1(out hImage, "byte", width, height, (IntPtr)ptrdata);
|
||
|
||
}
|
||
}
|
||
return hImage;
|
||
}
|
||
else if (matChannels == 3) // 三通道
|
||
{
|
||
IntPtr ptrRed; // R通道图
|
||
IntPtr ptrGreen; // G通道图
|
||
IntPtr ptrBlue; // B通道图
|
||
Mat[] mats = mImage.Split();
|
||
|
||
ptrRed = mats[0].Data; // 取R通道值
|
||
ptrGreen = mats[1].Data; // 取G通道值
|
||
ptrBlue = mats[2].Data; // 取B通道值
|
||
matType = mImage.GetType(); // 类型
|
||
height = (uint)mImage.Rows; // 高
|
||
width = (uint)mImage.Cols; // 宽
|
||
|
||
// 改自:hImage = new HObject(new OpenCvSharp.Size(width, height), MatType.CV_8UC1, new Scalar(0));
|
||
byte[] dataRed = new byte[width * height]; //Mat dataGrayScaleImage = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
|
||
byte[] dataGreen = new byte[width * height];
|
||
byte[] dataBlue = new byte[width * height];
|
||
|
||
unsafe
|
||
{
|
||
fixed (byte* ptrdataRed = dataRed, ptrdataGreen = dataGreen, ptrdataBlue = dataBlue)
|
||
{
|
||
|
||
#region 按行复制
|
||
//HImage himg = new HImage("byte", width, height, (IntPtr)ptrdataRed);
|
||
//for (int i = 0; i < height; i++)
|
||
//{
|
||
// CopyMemory((IntPtr)(ptrdataRed + width * i), new IntPtr((long)ptrRed +width * i), width);
|
||
// CopyMemory((IntPtr)(ptrdataGreen + width * i), new IntPtr((long)ptrGreen+ width * i), width);
|
||
// CopyMemory((IntPtr)(ptrdataBlue + width * i), new IntPtr((long)ptrBlue +width * i), width);
|
||
//}
|
||
|
||
#endregion
|
||
|
||
CopyMemory((IntPtr)ptrdataRed, new IntPtr((long)ptrRed), width* height); // 复制R通道
|
||
CopyMemory((IntPtr)ptrdataGreen, new IntPtr((long)ptrGreen), width * height); // 复制G通道
|
||
CopyMemory((IntPtr)ptrdataBlue, new IntPtr((long)ptrBlue), width * height); // 复制B通道
|
||
HOperatorSet.GenImage3(out hImage, "byte", width, height, (IntPtr)ptrdataRed, (IntPtr)ptrdataGreen, (IntPtr)ptrdataBlue); // 合成
|
||
}
|
||
}
|
||
return hImage;
|
||
}
|
||
else
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw ex;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
public static Mat HImageToMat(this HObject hobj)
|
||
{
|
||
try
|
||
{
|
||
Mat mImage;
|
||
HTuple htChannels;
|
||
HTuple cType = null;
|
||
HTuple width, height;
|
||
width = height = 0;
|
||
|
||
HOperatorSet.CountChannels(hobj, out htChannels);
|
||
|
||
if (htChannels.Length == 0)
|
||
{
|
||
return null;
|
||
}
|
||
if (htChannels[0].I == 1)
|
||
{
|
||
HTuple ptr;
|
||
HOperatorSet.GetImagePointer1(hobj, out ptr, out cType, out width, out height);
|
||
mImage = new Mat(height, width, MatType.CV_8UC1, new Scalar(0));
|
||
|
||
unsafe
|
||
{
|
||
CopyMemory(mImage.Data, new IntPtr((byte*)ptr.IP), (uint)(width * height));// CopyMemory(要复制到的地址,复制源的地址,复制的长度)
|
||
}
|
||
|
||
return mImage;
|
||
}
|
||
else if (htChannels[0].I == 3)
|
||
{
|
||
HTuple ptrRed;
|
||
HTuple ptrGreen;
|
||
HTuple ptrBlue;
|
||
|
||
HOperatorSet.GetImagePointer3(hobj, out ptrRed, out ptrGreen, out ptrBlue, out cType, out width, out height);
|
||
Mat pImageRed = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
|
||
Mat pImageGreen = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
|
||
Mat pImageBlue = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC1);
|
||
mImage = new Mat(new OpenCvSharp.Size(width, height), MatType.CV_8UC3, new Scalar(0, 0, 0));
|
||
unsafe
|
||
{
|
||
CopyMemory(pImageRed.Data, new IntPtr((byte*)ptrRed.IP), (uint)(width * height)); // CopyMemory(要复制到的地址,复制源的地址,复制的长度)_Red
|
||
CopyMemory(pImageGreen.Data, new IntPtr((byte*)ptrGreen.IP), (uint)(width * height)); // CopyMemory(要复制到的地址,复制源的地址,复制的长度)_Green
|
||
CopyMemory(pImageBlue.Data, new IntPtr((byte*)ptrBlue.IP), (uint)(width * height)); // CopyMemory(要复制到的地址,复制源的地址,复制的长度)_Blue
|
||
|
||
}
|
||
Mat[] multi = new Mat[] { pImageBlue, pImageGreen, pImageRed };
|
||
Cv2.Merge(multi, mImage);
|
||
pImageRed.Dispose();
|
||
pImageRed = null;
|
||
pImageGreen.Dispose();
|
||
pImageGreen = null;
|
||
pImageBlue.Dispose();
|
||
pImageBlue = null;
|
||
return mImage;
|
||
}
|
||
else
|
||
{
|
||
return null;
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
throw ex;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 从内存流中指定位置,读取数据
|
||
/// </summary>
|
||
/// <param name="curStream"></param>
|
||
/// <param name="startPosition"></param>
|
||
/// <param name="length"></param>
|
||
/// <returns></returns>
|
||
public static int ReadData(MemoryStream curStream, int startPosition, int length)
|
||
{
|
||
int result = -1;
|
||
|
||
byte[] tempData = new byte[length];
|
||
curStream.Position = startPosition;
|
||
curStream.Read(tempData, 0, length);
|
||
result = BitConverter.ToInt32(tempData, 0);
|
||
|
||
return result;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 使用byte[]数据,生成三通道 BMP 位图
|
||
/// </summary>
|
||
/// <param name="originalImageData"></param>
|
||
/// <param name="originalWidth"></param>
|
||
/// <param name="originalHeight"></param>
|
||
/// <returns></returns>
|
||
public static Bitmap CreateColorBitmap(byte[] originalImageData, int originalWidth, int originalHeight, byte[] color_map)
|
||
{
|
||
// 指定8位格式,即256色
|
||
Bitmap resultBitmap = new Bitmap(originalWidth, originalHeight, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
|
||
|
||
// 将该位图存入内存中
|
||
MemoryStream curImageStream = new MemoryStream();
|
||
resultBitmap.Save(curImageStream, System.Drawing.Imaging.ImageFormat.Bmp);
|
||
curImageStream.Flush();
|
||
|
||
// 由于位图数据需要DWORD对齐(4byte倍数),计算需要补位的个数
|
||
int curPadNum = ((originalWidth * 8 + 31) / 32 * 4) - originalWidth;
|
||
|
||
// 最终生成的位图数据大小
|
||
int bitmapDataSize = ((originalWidth * 8 + 31) / 32 * 4) * originalHeight;
|
||
|
||
// 数据部分相对文件开始偏移,具体可以参考位图文件格式
|
||
int dataOffset = ReadData(curImageStream, 10, 4);
|
||
|
||
|
||
// 改变调色板,因为默认的调色板是32位彩色的,需要修改为256色的调色板
|
||
int paletteStart = 54;
|
||
int paletteEnd = dataOffset;
|
||
int color = 0;
|
||
|
||
for (int i = paletteStart; i < paletteEnd; i += 4)
|
||
{
|
||
byte[] tempColor = new byte[4];
|
||
tempColor[0] = (byte)color;
|
||
tempColor[1] = (byte)color;
|
||
tempColor[2] = (byte)color;
|
||
tempColor[3] = (byte)0;
|
||
color++;
|
||
|
||
curImageStream.Position = i;
|
||
curImageStream.Write(tempColor, 0, 4);
|
||
}
|
||
|
||
// 最终生成的位图数据,以及大小,高度没有变,宽度需要调整
|
||
byte[] destImageData = new byte[bitmapDataSize];
|
||
int destWidth = originalWidth + curPadNum;
|
||
|
||
// 生成最终的位图数据,注意的是,位图数据 从左到右,从下到上,所以需要颠倒
|
||
for (int originalRowIndex = originalHeight - 1; originalRowIndex >= 0; originalRowIndex--)
|
||
{
|
||
int destRowIndex = originalHeight - originalRowIndex - 1;
|
||
|
||
for (int dataIndex = 0; dataIndex < originalWidth; dataIndex++)
|
||
{
|
||
// 同时还要注意,新的位图数据的宽度已经变化destWidth,否则会产生错位
|
||
destImageData[destRowIndex * destWidth + dataIndex] = originalImageData[originalRowIndex * originalWidth + dataIndex];
|
||
}
|
||
}
|
||
|
||
// 将流的Position移到数据段
|
||
curImageStream.Position = dataOffset;
|
||
|
||
// 将新位图数据写入内存中
|
||
curImageStream.Write(destImageData, 0, bitmapDataSize);
|
||
|
||
curImageStream.Flush();
|
||
|
||
// 将内存中的位图写入Bitmap对象
|
||
resultBitmap = new Bitmap(curImageStream);
|
||
|
||
resultBitmap = Convert8to24(resultBitmap, color_map); // 转为3通道图像
|
||
|
||
return resultBitmap;
|
||
}
|
||
|
||
// 实现单通道到多通道
|
||
public static Bitmap Convert8to24(this Bitmap bmp, byte[] color_map)
|
||
{
|
||
|
||
Rectangle rect = new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height);
|
||
BitmapData bitmapData = bmp.LockBits(rect, ImageLockMode.ReadOnly, bmp.PixelFormat);
|
||
|
||
//计算实际8位图容量
|
||
int size8 = bitmapData.Stride * bmp.Height;
|
||
byte[] grayValues = new byte[size8];
|
||
|
||
//// 申请目标位图的变量,并将其内存区域锁定
|
||
Bitmap TempBmp = new Bitmap(bmp.Width, bmp.Height, PixelFormat.Format24bppRgb);
|
||
BitmapData TempBmpData = TempBmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb);
|
||
|
||
|
||
//// 获取图像参数以及设置24位图信息
|
||
int stride = TempBmpData.Stride; // 扫描线的宽度
|
||
int offset = stride - TempBmp.Width; // 显示宽度与扫描线宽度的间隙
|
||
IntPtr iptr = TempBmpData.Scan0; // 获取bmpData的内存起始位置
|
||
int scanBytes = stride * TempBmp.Height;// 用stride宽度,表示这是内存区域的大小
|
||
|
||
// 下面把原始的显示大小字节数组转换为内存中实际存放的字节数组
|
||
byte[] pixelValues = new byte[scanBytes]; //为目标数组分配内存
|
||
System.Runtime.InteropServices.Marshal.Copy(bitmapData.Scan0, grayValues, 0, size8);
|
||
|
||
for (int i = 0; i < bmp.Height; i++)
|
||
{
|
||
|
||
for (int j = 0; j < bitmapData.Stride; j++)
|
||
{
|
||
|
||
if (j >= bmp.Width)
|
||
continue;
|
||
|
||
int indexSrc = i * bitmapData.Stride + j;
|
||
int realIndex = i * TempBmpData.Stride + j * 3;
|
||
|
||
// color_id:就是预测出来的结果
|
||
int color_id = (int)grayValues[indexSrc] % 256;
|
||
|
||
if (color_id == 0) // 分割中类别1对应值1,而背景往往为0,因此这里就将背景置为[0, 0, 0]
|
||
{
|
||
// 空白
|
||
pixelValues[realIndex] = 0;
|
||
pixelValues[realIndex + 1] = 0;
|
||
pixelValues[realIndex + 2] = 0;
|
||
}
|
||
else
|
||
{
|
||
// 替换为color_map中的颜色值
|
||
pixelValues[realIndex] = color_map[color_id * 3];
|
||
pixelValues[realIndex + 1] = color_map[color_id * 3 + 1];
|
||
pixelValues[realIndex + 2] = color_map[color_id * 3 + 2];
|
||
}
|
||
}
|
||
}
|
||
|
||
//Parallel.For(0, width * height, i =>
|
||
// {
|
||
// int index = (i + 1) % width + widthIn4 * ((i + 1) / width) - 1;
|
||
|
||
// showBitmapBuffer[index * 6] = bitmapBuffer[index * 6] = data[i * 2];
|
||
// showBitmapBuffer[index * 6 + 1] = bitmapBuffer[index * 6 + 1] = data[i * 2 + 1];
|
||
// });
|
||
|
||
// 用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
|
||
Marshal.Copy(pixelValues, 0, iptr, scanBytes);
|
||
TempBmp.UnlockBits(TempBmpData); // 解锁内存区域
|
||
bmp.UnlockBits(bitmapData);
|
||
return TempBmp;
|
||
}
|
||
|
||
|
||
// 获取伪彩色图的RGB值 -- 同时也是适用于检测框分类颜色
|
||
public static byte[] GetColorMap(int num_classes = 256)
|
||
{
|
||
num_classes += 1;
|
||
byte[] color_map = new byte[num_classes * 3];
|
||
for (int i = 0; i < num_classes; i++)
|
||
{
|
||
int j = 0;
|
||
int lab = i;
|
||
while (lab != 0)
|
||
{
|
||
color_map[i * 3] |= (byte)(((lab >> 0) & 1) << (7 - j));
|
||
color_map[i * 3 + 1] |= (byte)(((lab >> 1) & 1) << (7 - j));
|
||
color_map[i * 3 + 2] |= (byte)(((lab >> 2) & 1) << (7 - j));
|
||
|
||
j += 1;
|
||
lab >>= 3;
|
||
}
|
||
}
|
||
|
||
// 去掉底色
|
||
color_map = color_map.Skip(3).ToArray();
|
||
return color_map;
|
||
}
|
||
|
||
// GetGrayMap
|
||
public static byte[] GetGrayMap(int num_classes = 256)
|
||
{
|
||
byte[] color_map = new byte[num_classes];
|
||
for (int i = 0; i < num_classes; i++)
|
||
{
|
||
if (i <= 100)
|
||
color_map[i] = 0;
|
||
if (i > 100 && i <= 200)
|
||
color_map[i] = 100;
|
||
if (i > 200)
|
||
color_map[i] = 255;
|
||
|
||
}
|
||
return color_map;
|
||
}
|
||
/// <summary>
|
||
/// 像素点阵转换为bitmap 二值化图
|
||
/// </summary>
|
||
/// <param name="rawValues">byte[]数组</param>
|
||
/// <param name="width">图片的宽度</param>
|
||
/// <param name="height">图片的高度</param>
|
||
/// <returns>bitmap图片</returns>
|
||
public static Bitmap CreateBinaryBitmap(byte[] rawValues, int width, int height)
|
||
{
|
||
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
|
||
BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
|
||
//获取图像参数
|
||
int stride = bmpData.Stride; // 扫描线的宽度
|
||
int offset = stride - width; // 显示宽度与扫描线宽度的间隙
|
||
IntPtr iptr = bmpData.Scan0; // 获取bmpData的内存起始位置
|
||
int scanBytes = stride * height;// 用stride宽度,表示这是内存区域的大小
|
||
//下面把原始的显示大小字节数组转换为内存中实际存放的字节数组
|
||
int posScan = 0, posReal = 0;// 分别设置两个位置指针,指向源数组和目标数组
|
||
byte[] pixelValues = new byte[scanBytes]; //为目标数组分配内存
|
||
for (int x = 0; x < height; x++)
|
||
{
|
||
//下面的循环节是模拟行扫描
|
||
for (int y = 0; y < width; y++)
|
||
{
|
||
pixelValues[posScan++] = rawValues[posReal++] == 0 ? (byte)0 : (byte)255;
|
||
}
|
||
posScan += offset; //行扫描结束,要将目标位置指针移过那段“间隙”
|
||
}
|
||
//用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
|
||
Marshal.Copy(pixelValues, 0, iptr, scanBytes);
|
||
bmp.UnlockBits(bmpData); // 解锁内存区域
|
||
//下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度
|
||
ColorPalette tempPalette;
|
||
using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed))
|
||
{
|
||
tempPalette = tempBmp.Palette;
|
||
}
|
||
for (int i = 0; i < 256; i++)
|
||
{
|
||
tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
|
||
}
|
||
|
||
bmp.Palette = tempPalette;
|
||
|
||
return bmp;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 像素点阵转换为bitmap灰度图
|
||
/// </summary>
|
||
/// <param name="rawValues">byte[]数组</param>
|
||
/// <param name="width">图片的宽度</param>
|
||
/// <param name="height">图片的高度</param>
|
||
/// <returns>bitmap图片</returns>
|
||
public static Bitmap CreateGrayBitmap(byte[] rawValues, int width, int height)
|
||
{
|
||
Bitmap bmp = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
|
||
BitmapData bmpData = bmp.LockBits(new System.Drawing.Rectangle(0, 0, width, height), ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
|
||
//获取图像参数
|
||
int stride = bmpData.Stride; // 扫描线的宽度
|
||
int offset = stride - width; // 显示宽度与扫描线宽度的间隙
|
||
IntPtr iptr = bmpData.Scan0; // 获取bmpData的内存起始位置
|
||
int scanBytes = stride * height;// 用stride宽度,表示这是内存区域的大小
|
||
//下面把原始的显示大小字节数组转换为内存中实际存放的字节数组
|
||
int posScan = 0, posReal = 0;// 分别设置两个位置指针,指向源数组和目标数组
|
||
byte[] pixelValues = new byte[scanBytes]; //为目标数组分配内存
|
||
for (int x = 0; x < height; x++)
|
||
{
|
||
//下面的循环节是模拟行扫描
|
||
for (int y = 0; y < width; y++)
|
||
{
|
||
pixelValues[posScan++] = rawValues[posReal++];
|
||
}
|
||
posScan += offset; //行扫描结束,要将目标位置指针移过那段“间隙”
|
||
}
|
||
//用Marshal的Copy方法,将刚才得到的内存字节数组复制到BitmapData中
|
||
Marshal.Copy(pixelValues, 0, iptr, scanBytes);
|
||
bmp.UnlockBits(bmpData); // 解锁内存区域
|
||
//下面的代码是为了修改生成位图的索引表,从伪彩修改为灰度
|
||
ColorPalette tempPalette;
|
||
using (Bitmap tempBmp = new Bitmap(1, 1, System.Drawing.Imaging.PixelFormat.Format8bppIndexed))
|
||
{
|
||
tempPalette = tempBmp.Palette;
|
||
}
|
||
for (int i = 0; i < 256; i++)
|
||
{
|
||
tempPalette.Entries[i] = System.Drawing.Color.FromArgb(i, i, i);
|
||
}
|
||
|
||
bmp.Palette = tempPalette;
|
||
|
||
return bmp;
|
||
}
|
||
|
||
|
||
//分别基于像素(GetPixel和SetPixel)、基于内存、基于指针这三种方法增强图片对比度
|
||
// 第一种方法:像素提取法 速度慢 基于像素:400-600ms
|
||
public static Bitmap MethodBaseOnPixel(Bitmap bitmap, int degree)
|
||
{
|
||
Color curColor;
|
||
int grayR, grayG, grayB;
|
||
|
||
double Deg = (100.0 + degree) / 100.0;
|
||
for (int i = 0; i < bitmap.Width; i++)
|
||
{
|
||
for (int j = 0; j < bitmap.Height; j++)
|
||
{
|
||
curColor = bitmap.GetPixel(i, j);
|
||
grayR = Convert.ToInt32((((curColor.R / 255.0 - 0.5) * Deg + 0.5)) * 255);
|
||
grayG = Convert.ToInt32((((curColor.G / 255.0 - 0.5) * Deg + 0.5)) * 255);
|
||
grayB = Convert.ToInt32((((curColor.B / 255.0 - 0.5) * Deg + 0.5)) * 255);
|
||
if (grayR < 0)
|
||
grayR = 0;
|
||
else if (grayR > 255)
|
||
grayR = 255;
|
||
|
||
if (grayB < 0)
|
||
grayB = 0;
|
||
else if (grayB > 255)
|
||
grayB = 255;
|
||
|
||
if (grayG < 0)
|
||
grayG = 0;
|
||
else if (grayG > 255)
|
||
grayG = 255;
|
||
|
||
bitmap.SetPixel(i, j, Color.FromArgb(grayR, grayG, grayB));
|
||
}
|
||
}
|
||
|
||
return bitmap;
|
||
}
|
||
|
||
// 第二种方法:基于内存 17-18ms
|
||
public static unsafe Bitmap MethodBaseOnMemory(Bitmap bitmap, int degree)
|
||
{
|
||
if (bitmap == null)
|
||
{
|
||
return null;
|
||
}
|
||
double Deg = (100.0 + degree) / 100.0;
|
||
|
||
int width = bitmap.Width;
|
||
int height = bitmap.Height;
|
||
|
||
int length = height * 3 * width;
|
||
byte[] RGB = new byte[length];
|
||
|
||
BitmapData data = bitmap.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
|
||
|
||
System.IntPtr Scan0 = data.Scan0;
|
||
System.Runtime.InteropServices.Marshal.Copy(Scan0, RGB, 0, length);
|
||
|
||
double gray = 0;
|
||
for (int i = 0; i < RGB.Length; i += 3)
|
||
{
|
||
for (int j = 0; j < 3; j++)
|
||
{
|
||
gray = (((RGB[i + j] / 255.0 - 0.5) * Deg + 0.5)) * 255.0;
|
||
if (gray > 255)
|
||
gray = 255;
|
||
|
||
if (gray < 0)
|
||
gray = 0;
|
||
RGB[i + j] = (byte)gray;
|
||
}
|
||
}
|
||
|
||
System.Runtime.InteropServices.Marshal.Copy(RGB, 0, Scan0, length);// 此处Copy是之前Copy的逆操作
|
||
bitmap.UnlockBits(data);
|
||
return bitmap;
|
||
}
|
||
|
||
//第三种方法:基于指针 20-23ms
|
||
public static unsafe Bitmap MethodBaseOnPtr(Bitmap b, int degree)
|
||
{
|
||
if (b == null)
|
||
{
|
||
return null;
|
||
}
|
||
try
|
||
{
|
||
double num = 0.0;
|
||
double num2 = (100.0 + degree) / 100.0;
|
||
num2 *= num2;
|
||
int width = b.Width;
|
||
int height = b.Height;
|
||
BitmapData bitmapdata = b.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
|
||
byte* numPtr = (byte*)bitmapdata.Scan0;
|
||
|
||
int offset = bitmapdata.Stride - (width * 3);
|
||
for (int i = 0; i < height; i++)
|
||
{
|
||
for (int j = 0; j < width; j++)
|
||
{
|
||
for (int k = 0; k < 3; k++)
|
||
{
|
||
num = ((((((double)numPtr[k]) / 255.0) - 0.5) * num2) + 0.5) * 255.0;
|
||
if (num < 0.0)
|
||
{
|
||
num = 0.0;
|
||
}
|
||
if (num > 255.0)
|
||
{
|
||
num = 255.0;
|
||
}
|
||
numPtr[k] = (byte)num;
|
||
}
|
||
numPtr += 3;
|
||
|
||
}
|
||
numPtr += offset;
|
||
}
|
||
b.UnlockBits(bitmapdata);
|
||
return b;
|
||
}
|
||
catch
|
||
{
|
||
return b;
|
||
}
|
||
}
|
||
|
||
public static double GetRoiArae(Mat src, Rect rect)
|
||
{
|
||
//初始面积为0
|
||
double Area = 0;
|
||
//获取感兴趣区域
|
||
src = src[rect];
|
||
//转为单通道
|
||
Mat gray = src.CvtColor(ColorConversionCodes.BGR2GRAY);
|
||
//二值化
|
||
Mat binary = gray.Threshold(0, 255, ThresholdTypes.Otsu | ThresholdTypes.Binary);
|
||
|
||
//求轮廓 不连通会有多个闭合区域
|
||
OpenCvSharp.Point[][] contours;
|
||
HierarchyIndex[] hierarchy;
|
||
Cv2.FindContours(binary, out contours, out hierarchy, RetrievalModes.List, ContourApproximationModes.ApproxSimple);
|
||
for (int i = 0; i < contours.Count(); i++)
|
||
{
|
||
//所有面积相加
|
||
Area += Cv2.ContourArea(contours[i], false);
|
||
}
|
||
return Area;
|
||
}
|
||
}
|
||
}
|