海康VM脚本中使用opencvsharp和halcon
最近海康VM用的有点多,但是VM有时候处理复杂图像还是比较难受的,拖拉拽不一定就好,有时候需要加入别的或者已经用opencvsharp写好的一些功能想直接放进VM中。
于是需要在VM脚本中转换halcon opencvsharp VM脚本图像 图像类型之间互相转换。
VM脚本实际上就是Csharp,只要Csharp支持的几乎都可以在VM脚本实现。
我这是一个例子。拿出来。环境配置在注释中体现。
using System.Text;
using System.Windows.Forms;
using Script.Methods;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using HalconDotNet;
using Ha=HalconDotNet;
using ha=HalconDotNet.HOperatorSet;
using OpenCvSharp;
using OpenCvSharp.Extensions;
using System.Drawing; //添加引用System.Drawing.dll
using System.Drawing.Imaging;
using HslCommunication;
using HslCommunication.LogNet;
using CSINIRW;
//ini 文件操作类
//using HslCommunication.Profinet.Melsec;
// 提示:关闭脚本窗口后:在VM流程中 鼠标选中脚本模块,按Ctrl+M快捷键,可以直接跳转到脚本模块目录。
//C:\Program Files\VisionMaster4.3.0\Applications\Module(sp)\x64\Logic\ShellModule\DLL
//复制 halcon的dll 包括依赖dll
//复制 OpenCvSharp dll 包括依赖dll
//复制 hslcomm dll
//复制 csini
// vm自带 OpenCvSharp路径
//C:\Program Files\VisionMaster4.3.0\Applications\3rdLib\OpenCv
//编辑程序集> 引用需要的dll
/*****************************************************************
输入变量:
int0 (IMAGE) 1 图象源1.图像[]
输出变量:
out0 (IMAGE)
out0 (IMAGE)
************************************************************************/
public partial class UserScript:ScriptMethods,IProcessMethods
{
//执行次数计数
int processCount ;
public void Init()
{
processCount = 0;
}
public bool Process()
{
//--------------------------------------------------------------------------------------------------------------------------------------------
//VM:脚本输入图像(图像类型是ImageData);
ImageData img = new ImageData(); //实例化ImageData类型图像. 海康ImageData;Cv.Mat;
ImageData imgOut = new ImageData(); //输入图像无数据会出现调用失败的情况>>( Line:0 -- Error:编译错误:方法执行失败 Init(),调用的目标发生了异常。); //输入图像需要订阅才会有数据;;
// ImageData imgOutC = new ImageData();
GetImageValue("in0",ref img); //读取输入图像;
Mat matimg = ImageDataToMat(img); // 脚本图像转 Mat;
double maxValue=239;
Mat dst = new Mat();
Mat dst1= new Mat();
//Mat src = new Mat(@"lena2.jpg", ImreadModes.Grayscale);//exe同目录图片
Cv2.Canny(matimg, dst, 15, 200);
// opencv 自适应阈值
Cv2.AdaptiveThreshold(matimg, dst1, 255,OpenCvSharp.AdaptiveThresholdTypes.MeanC, OpenCvSharp.ThresholdTypes.Binary, 11, 2);
/*******
Cv2.AdaptiveThreshold(Mat src, Mat dst, double maxValue,
AdaptiveThresholdTypes adaptiveMethod,
ThresholdTypes thresholdType, int blockSize, double C);
Mat src:输入图像。
Mat dst:输出图像,与输入图像 src 具有相同的大小和类型。
double maxValue:当像素值大于阈值时,将其设置为的值。
AdaptiveThresholdTypes adaptiveMethod:自适应方法的类型,决定如何计算局部阈值。
AdaptiveThresholdTypes.MeanC:计算邻域块的均值,阈值为均值减去常量 C。
AdaptiveThresholdTypes.GaussianC:计算邻域块的加权和,权重由高斯窗口确定,阈值为和减去常量 C。
*******/
// 测试临时可以快速看到图 实际过程中 会导致
//*******/
// //这里演示VM脚本>OpencvSharp 窗口显示
Task.Run(() =>{
using (new Window("dst1_Image", WindowMode.Normal, dst1))
{
Cv2.WaitKey(0);
}
});
//*******/
//*********
//这里演示VM脚本>halcon 窗口显示
Task.Run(() =>{
HObject ho_Image;
HTuple Heigh,Width;
HTuple hv_WindowHandle = new HTuple();
Mat imageROI = new Mat(matimg,new Rect(0,0,260,260));//设置Roi
imgOut = MatToImageData(matimg);//Mat 转脚本图像
HObject himg = ImageDataToHalconImage(img);
ha.ReadImage(out ho_Image, "printer_chip/printer_chip_01");
ho_Image=himg;
ha.GetImageSize(ho_Image,out Heigh,out Width);
ha.SetSystem("use_window_thread","true"); // 不开这个 vm运行脚本后ha窗口会卡死
ha.OpenWindow(0,0,512,512,0,"visible","",out hv_WindowHandle); //开这个窗口就会一闪而过
ha.SetPart(hv_WindowHandle,0,0,Width,Heigh);
ha.DispObj(ho_Image,hv_WindowHandle);
Sleep(3000);// 时间越长 halcon窗口关闭越慢 测试用 不能在VM脚本瞎搞
});
//*******/
// Mat dstImage = Mat.Zeros(imageROI.Width,imageROI.Width,MatType.CV_8UC1);//调用OpenCV接口进行图像处理
// Mat src = Cv2.ImRead(@"C:/Users/Administrator/Desktop/lenapic/lena2.jpg", ImreadModes.Grayscale);
// Cv2.Threshold(imageROI,dstImage,10,120,ThresholdTypes.Otsu);//二值化
//画线
// Point p1 = new Point(0, 0);
// Point p2;
// p2.X = 260;
// p2.Y = 260;
// Scalar color = new Scalar(255, 0, 255);
//Cv2.Line(dstImage, p1, p2, color, 2, LineTypes.Link8);
//画框
// Rect rect1 = new Rect(0, 0, 260, 260);
// Scalar color1 = new Scalar(255, 0, 0);
//Cv2.Rectangle(dstImage, rect1, color1, 2, LineTypes.AntiAlias);//LineTypes.AntiAlias:反锯齿效果
//写字
// string str001="OpenCV:";
//Cv2.PutText(dstImage,str001, new Point(10,30), HersheyFonts.HersheySimplex, 1,Scalar.All(255),1,LineTypes.Link4);
//Cv2.PutText(srcImage,"srcImage:", new Point(300,300), HersheyFonts.HersheySimplex, 1,Scalar.All(255),1,LineTypes.Link4);
// using (src = new Mat(@"C:\Users\whx\Desktop\opcvImage\s1.jpg", ImreadModes.AnyColor | ImreadModes.AnyDepth))
// Mat dst = new Mat(src.Size(),src.Type());
//src.CopyTo(dst);
// using (new Window("DST Image", WindowMode.Normal, matimg))
// using (new Window("SRC Image", WindowMode.Normal, src))
//将处理后的图像拷贝到原图ROI区域
SetImageValue("out0",imgOut);//输出图像
//--------------------------------------------------------------------------------------------------------------------------------------------
/*****************************************************************************************************************************************/
return true;
}
//--------------------------------------------------------------------------------------------------------------------------------------------
/*****************************************************************************************************************************************/
// ImageDataToMat
// MatToImageData
// HalconImageToImageData
// ImageDataToHalconImage
public Mat ImageDataToMat(ImageData img)
{
Mat matImage = new Mat();
if(ImagePixelFormate.MONO8 == img.PixelFormat)
{
matImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC1);
IntPtr grayPtr = Marshal.AllocHGlobal(img.Width * img.Heigth);
Marshal.Copy(img.Buffer, 0, matImage.Ptr(0), img.Buffer.Length);
//用完记得释放指针
Marshal.FreeHGlobal(grayPtr);
}
else if (ImagePixelFormate.RGB24 == img.PixelFormat)
{
matImage = Mat.Zeros(img.Heigth, img.Width, MatType.CV_8UC3);
IntPtr rgbPtr = Marshal.AllocHGlobal(img.Width * img.Heigth * 3);
Marshal.Copy(img.Buffer, 0, matImage.Ptr(0), img.Buffer.Length);
Cv2.CvtColor(matImage, matImage, ColorConversionCodes.RGB2BGR);
//用完记得释放指针
Marshal.FreeHGlobal(rgbPtr);
}
return matImage;
}
public ImageData MatToImageData(Mat matImage)
{
ImageData imgOut = new ImageData();
byte[] buffer = new Byte[matImage.Width * matImage.Height * matImage.Channels()];
Marshal.Copy(matImage.Ptr(0), buffer, 0, buffer.Length);
if (1 == matImage.Channels())
{
imgOut.Buffer = buffer;
imgOut.Width = matImage.Width;
imgOut.Heigth = matImage.Height;
imgOut.PixelFormat = ImagePixelFormate.MONO8;
}
else if (3 == matImage.Channels())
{
//交换R与B通道
for (int i = 0; i < buffer.Length - 2; i += 3)
{
byte temp = buffer[i];
buffer[i] = buffer[i + 2];
buffer[i + 2] = temp;
}
imgOut.Buffer = buffer;
imgOut.Width = matImage.Width;
imgOut.Heigth = matImage.Height;
imgOut.PixelFormat = ImagePixelFormate.RGB24;
}
return imgOut;
}
//halcon图像与脚本图像(ImageData)互转
//HalconImageToImageData
//ImageDataToHalconImage
public static ImageData HalconImageToImageData(HObject hImageObj)
{
try
{
ImageData imageData = new ImageData();
HTuple imageWidth = 0;
HTuple imageHeight = 0;
HTuple objClass = hImageObj.GetObjClass();
if (objClass.S.Equals("image"))
{
HTuple imageType;
HOperatorSet.GetImageType(hImageObj, out imageType);
if (imageType.S.Equals("byte"))
{
//获取图像通道数
HTuple channels = 0;
HOperatorSet.CountChannels(hImageObj, out channels);
//如果是单通道
if (channels.I == 1)
{
HTuple imagePointer;
HOperatorSet.GetImagePointer1(hImageObj, out imagePointer, out imageType, out imageWidth, out imageHeight);
imageData.Width = imageWidth.I;
imageData.Heigth = imageHeight.I;
imageData.PixelFormat = ImagePixelFormate.MONO8;
int stride = imageWidth.I;
if (stride % 4 != 0)
{
stride += 4 - stride % 4;
}
imageData.Buffer = new byte[stride * imageHeight.I];
Marshal.Copy(imagePointer, imageData.Buffer, 0, stride * imageHeight.I);
}
//如果是三通道
else if (channels.I == 3)
{
HTuple redChannel;
HTuple greenChannel;
HTuple blueChannel;
HOperatorSet.GetImagePointer3(hImageObj, out redChannel, out greenChannel, out blueChannel, out imageType, out imageWidth, out imageHeight);
imageData.Width = imageWidth.I;
imageData.Heigth = imageHeight.I;
imageData.PixelFormat = ImagePixelFormate.RGB24;
int stride = imageWidth.I;
if (stride % 4 != 0)
{
stride += 4 - stride % 4;
}
imageData.Buffer = new byte[stride * imageHeight.I * 3];
byte[] imageRedBuffer = new byte[stride * imageHeight.I];
byte[] imageGreenBuffer = new byte[stride * imageHeight.I];
byte[] imageBlueBuffer = new byte[stride * imageHeight.I];
Marshal.Copy(redChannel.IP, imageRedBuffer, 0, imageRedBuffer.Length);
Marshal.Copy(greenChannel.IP, imageGreenBuffer, 0, imageGreenBuffer.Length);
Marshal.Copy(blueChannel.IP, imageBlueBuffer, 0, imageBlueBuffer.Length);
for (int row = 0; row < imageHeight.I; row++)
{
for (int col = 0, index = 0; col < imageWidth.I; col++, index += 3)
{
imageData.Buffer[index] = imageRedBuffer[row * imageWidth + col];
imageData.Buffer[index + 1] = imageGreenBuffer[row * imageWidth + col];
imageData.Buffer[index + 2] = imageBlueBuffer[row * imageWidth + col];
}
}
}
else
{
hImageObj.Dispose();
throw new Exception("不支持单通道,三通道以外的图像");
}
}
else
{
hImageObj.Dispose();
throw new Exception("不支持8bit以外的位深度图像");
}
}
else
{
hImageObj.Dispose();
throw new Exception("HObject非图像类型对象");
}
return imageData;
}
catch (Exception ex)
{
hImageObj.Dispose();
throw new Exception(ex.Message);
}
}
public static HObject ImageDataToHalconImage(ImageData image)
{
IntPtr imagePointer = IntPtr.Zero;
IntPtr redChannel = IntPtr.Zero;
IntPtr greenChannel = IntPtr.Zero;
IntPtr blueChannel = IntPtr.Zero;
try
{
HObject imageObj = new HObject();
HTuple width = image.Width;
HTuple height = image.Heigth;
if (image.PixelFormat == ImagePixelFormate.MONO8)
{
imagePointer = Marshal.AllocHGlobal(image.Buffer.Length);
Marshal.Copy(image.Buffer, 0, imagePointer, image.Buffer.Length);
HOperatorSet.GenImage1(out imageObj, "byte", width, height, imagePointer);
}
else if (image.PixelFormat == ImagePixelFormate.RGB24)
{
byte[] imageRedBuffer = new byte[image.Buffer.Length / 3];
byte[] imageGreBuffer = new byte[image.Buffer.Length / 3];
byte[] imageBluBuffer = new byte[image.Buffer.Length / 3];
int index = 0;
for (int i = 0; i < image.Buffer.Length; index++, i += 3)
{
imageRedBuffer[index] = image.Buffer[i];
imageGreBuffer[index] = image.Buffer[i + 1];
imageBluBuffer[index] = image.Buffer[i + 2];
}
redChannel = Marshal.AllocHGlobal(imageRedBuffer.Length);
greenChannel = Marshal.AllocHGlobal(imageGreBuffer.Length);
blueChannel = Marshal.AllocHGlobal(imageBluBuffer.Length);
Marshal.Copy(imageRedBuffer, 0, redChannel, imageRedBuffer.Length);
Marshal.Copy(imageGreBuffer, 0, greenChannel, imageGreBuffer.Length);
Marshal.Copy(imageBluBuffer, 0, blueChannel, imageBluBuffer.Length);
HOperatorSet.GenImage3(out imageObj, "byte", width, height, redChannel, greenChannel, blueChannel);
}
return imageObj;
}
catch (Exception ex)
{
Marshal.FreeHGlobal(imagePointer);
Marshal.FreeHGlobal(redChannel);
Marshal.FreeHGlobal(greenChannel);
Marshal.FreeHGlobal(blueChannel);
throw new Exception(ex.Message);
}
}
//
///
///
public void Mat2HObjectBpp8_u(Mat mat, out HObject image)
{
int ImageWidth = mat.Width;
int ImageHeight = mat.Height;
int channel = mat.Channels();
long size = ImageWidth * ImageHeight * channel;
int col_byte_num = ImageWidth * channel;
byte[] rgbValues = new byte[size];
//IntPtr imgptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(rgbValues.Length);
unsafe
{
for (int i = 0; i < mat.Height; i++)
{
IntPtr c = mat.Ptr(i);
//byte* c1 = (byte*)c;
System.Runtime.InteropServices.Marshal.Copy(c, rgbValues, i * col_byte_num, col_byte_num); // 一行一行将mat 像素复制到byte[],
}
void* p;
IntPtr ptr;
fixed (byte* pc = rgbValues)
{
p = (void*)pc;
ptr = new IntPtr(p);
}
HOperatorSet.GenImage1(out image, "byte", ImageWidth, ImageHeight, ptr);
}
}
public void Mat2HObjectBpp24_u(Mat mat, out HObject image)
{
int ImageWidth = mat.Width;
int ImageHeight = mat.Height;
int channel = mat.Channels();
long size = ImageWidth * ImageHeight * channel;
int col_byte_num = ImageWidth * channel;
byte[] rgbValues = new byte[size];
//IntPtr imgptr = System.Runtime.InteropServices.Marshal.AllocHGlobal(rgbValues.Length);
unsafe
{
for (int i = 0; i < mat.Height; i++)
{
IntPtr c = mat.Ptr(i);
//byte* c1 = (byte*)c;
System.Runtime.InteropServices.Marshal.Copy(c, rgbValues, i * col_byte_num, col_byte_num);
}
void* p;
IntPtr ptr;
fixed (byte* pc = rgbValues)
{
p = (void*)pc;
ptr = new IntPtr(p);
}
HOperatorSet.GenImageInterleaved(out image, ptr, "bgr", ImageWidth, ImageHeight, 0, "byte", 0, 0, 0, 0, -1, 0);
}
}
/********************************************************************/
//两个方向4个函数 m2h h2m
/// <summary>
/// /
/// </summary>
/// <param name="mat"></param>
/// <param name="image"></param>
static void Mat2HObjectBpp8(Mat mat, out HObject image)
{
try
{
Bitmap bmp = mat.ToBitmap();
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect,ImageLockMode.ReadOnly,PixelFormat.Format8bppIndexed);
HOperatorSet.GenImage1(out image,"byte",bmp.Width,bmp.Height,srcBmpData.Scan0);
bmp.UnlockBits(srcBmpData);
}
catch(Exception ex)
{
image = null;
}
}
static void Mat2HObjectBpp24(Mat mat,out HObject image)
{
Bitmap bmp = mat.ToBitmap();
Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height);
BitmapData srcBmpData = bmp.LockBits(rect, ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);
// HOperatorSet.GenImageInterleaved(out image,srcBmpData.Scan0, "bgr", bmp.Width, bmp.Height,"byte" ,0,0,0,0,-1,0,0);
// HOperatorSet.GenImageInterleaved(out image, srcBmpData.Scan0, "bgr", bmp.Width, bmp.Height, 0, "byte", 0,0, 0, 0, -1, 2);
HOperatorSet.GenImageInterleaved(out image, srcBmpData.Scan0, "bgr", bmp.Width, bmp.Height, 0, "byte", 0, 0, 0, 0, -1, 0);
bmp.UnlockBits(srcBmpData);
}
static void HObject2Mat8(HObject image ,out Mat res)
{//ok
HTuple hpoint, type, width, height;
HOperatorSet.GetImagePointer1(image ,out hpoint,out type,out width,out height);
IntPtr ptr2 = hpoint;
int bytes = width * height;
byte[] rgbvalues = new byte[bytes];
System.Runtime.InteropServices.Marshal.Copy(ptr2,rgbvalues,0,bytes);
res = new Mat(height,width,MatType.CV_8UC1,rgbvalues);
}
static void HObject2Mat24(HObject image,out Mat res)
{
// ok
HTuple hred,hgreen,hblue , type, width, height;
HOperatorSet.GetImagePointer3
(image,out hred,out hgreen,out hblue ,out type,out width,out height);
int bytes = width * height;
byte[] rvalues = new byte[bytes];
byte[] gvalues = new byte[bytes];
byte[] bvalues = new byte[bytes];
IntPtr ptrr = hred;
IntPtr ptrg = hgreen;
IntPtr ptrb = hblue;
Mat resr, resg, resb;
System.Runtime.InteropServices.Marshal.Copy(ptrr, rvalues, 0, bytes);
resr = new Mat(height, width, MatType.CV_8UC1, rvalues);
System.Runtime.InteropServices.Marshal.Copy(ptrg, gvalues, 0, bytes);
resg = new Mat(height, width, MatType.CV_8UC1, gvalues);
System.Runtime.InteropServices.Marshal.Copy(ptrb, bvalues, 0, bytes);
resb = new Mat(height, width, MatType.CV_8UC1, bvalues);
Mat[] mv = new Mat[3] { resb,resg,resr };
res = new Mat();
Cv2.Merge(mv, res);
}
}
看函数名就知道是干嘛用的了,填参数就行。
当然转换专题还没有完全结束 后面心情好的时候继续更新。
下一步打算把python的numpy图像塞进VM中,至此VM就可以拥有python图像处理的一切功能,也可以重复利用手上现有的python程序。