464 lines
19 KiB
C#
464 lines
19 KiB
C#
|
using OpenCvSharp;
|
|||
|
using System;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.Linq;
|
|||
|
using System.Text;
|
|||
|
using System.Threading.Tasks;
|
|||
|
|
|||
|
namespace GeBoShi.SysCtrl
|
|||
|
{
|
|||
|
public static class OpencvUtils
|
|||
|
{
|
|||
|
public static int image_width = 2048;
|
|||
|
public static int image_height = 2048;
|
|||
|
#region 图像预处理
|
|||
|
public static Mat Resize(Mat mat, int width, int height, out int xw)
|
|||
|
{
|
|||
|
OpenCvSharp.Size dsize = new OpenCvSharp.Size(width, height);
|
|||
|
Mat mat2 = new Mat();
|
|||
|
//Cv2.Resize(mat, mat2, dsize);
|
|||
|
ResizeUniform(mat, dsize, out mat2, out xw);
|
|||
|
return mat2;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// 等比例缩放
|
|||
|
/// </summary>
|
|||
|
/// <param name="src"></param>
|
|||
|
/// <param name="dst_size"></param>
|
|||
|
/// <param name="dst"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static int ResizeUniform(Mat src, Size dst_size, out Mat dst, out int xw)
|
|||
|
{
|
|||
|
xw = 0;
|
|||
|
int w = src.Cols;
|
|||
|
int h = src.Rows;
|
|||
|
int dst_w = dst_size.Width;
|
|||
|
int dst_h = dst_size.Height;
|
|||
|
//std::cout << "src: (" << h << ", " << w << ")" << std::endl;
|
|||
|
dst = new Mat(dst_h, dst_w, MatType.CV_8UC3, new Scalar(114, 114, 114));
|
|||
|
|
|||
|
float[] ratio = new float[2];
|
|||
|
float ratio_src = w * 1.0f / h;
|
|||
|
float ratio_dst = dst_w * 1.0f / dst_h;
|
|||
|
|
|||
|
int tmp_w = 0;
|
|||
|
int tmp_h = 0;
|
|||
|
if (ratio_src > ratio_dst)
|
|||
|
{
|
|||
|
tmp_w = dst_w;
|
|||
|
tmp_h = (int)(dst_w * 1.0f / w) * h;
|
|||
|
|
|||
|
ratio[0] = (float)w / (float)tmp_w;
|
|||
|
ratio[1] = (float)h / (float)tmp_h;
|
|||
|
}
|
|||
|
else if (ratio_src < ratio_dst)
|
|||
|
{
|
|||
|
tmp_h = dst_h;
|
|||
|
tmp_w = (int)((dst_h * 1.0f / h) * w);
|
|||
|
|
|||
|
ratio[0] = (float)w / (float)tmp_w;
|
|||
|
ratio[1] = (float)h / (float)tmp_h;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Cv2.Resize(src, dst, dst_size);
|
|||
|
|
|||
|
ratio[0] = (float)w / (float)tmp_w;
|
|||
|
ratio[1] = (float)h / (float)tmp_h;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//std::cout << "tmp: (" << tmp_h << ", " << tmp_w << ")" << std::endl;
|
|||
|
Mat tmp = new Mat();
|
|||
|
Cv2.Resize(src, tmp, new Size(tmp_w, tmp_h));
|
|||
|
|
|||
|
unsafe
|
|||
|
{
|
|||
|
if (tmp_w != dst_w)
|
|||
|
{ //高对齐,宽没对齐
|
|||
|
int index_w = (int)((dst_w - tmp_w) / 2.0);
|
|||
|
xw = index_w;
|
|||
|
//std::cout << "index_w: " << index_w << std::endl;
|
|||
|
for (int i = 0; i < dst_h; i++)
|
|||
|
{
|
|||
|
Buffer.MemoryCopy(IntPtr.Add(tmp.Data, i * tmp_w * 3).ToPointer(), IntPtr.Add(dst.Data, i * dst_w * 3 + index_w * 3).ToPointer(), tmp_w * 3, tmp_w * 3);
|
|||
|
}
|
|||
|
}
|
|||
|
else if (tmp_h != dst_h)
|
|||
|
{ //宽对齐, 高没有对齐
|
|||
|
int index_h = (int)((dst_h - tmp_h) / 2.0);
|
|||
|
//std::cout << "index_h: " << index_h << std::endl;
|
|||
|
Buffer.MemoryCopy(tmp.Data.ToPointer(), IntPtr.Add(dst.Data, index_h * dst_w * 3).ToPointer(), tmp_w * tmp_h * 3, tmp_w * tmp_h * 3);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
}
|
|||
|
}
|
|||
|
return 0;
|
|||
|
}
|
|||
|
public static Mat ResizeMat(Mat mat, int width, int height)
|
|||
|
{
|
|||
|
OpenCvSharp.Size dsize = new OpenCvSharp.Size(width, height);
|
|||
|
Mat mat2 = new Mat();
|
|||
|
Cv2.Resize(mat, mat2, dsize);
|
|||
|
return mat2;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 计算合理宽幅
|
|||
|
/// </summary>
|
|||
|
/// <param name="sumWidth">多个相机图像总宽(外部去除重合部分)</param>
|
|||
|
/// <returns></returns>
|
|||
|
public static int GetWidthForResize(int sumWidth)
|
|||
|
{
|
|||
|
//保证计算8x2 16个小图
|
|||
|
int count = (int)Math.Round(sumWidth * 1.0f / image_width, 0);
|
|||
|
count = 8;
|
|||
|
return count * image_width;
|
|||
|
|
|||
|
//int count = sumWidth / image_width;
|
|||
|
////int remainder = sumWidth % image_width;
|
|||
|
//if (count % 2 == 0)
|
|||
|
// return count * image_width;
|
|||
|
//else
|
|||
|
// return count * image_width+ image_width;
|
|||
|
}
|
|||
|
/// <summary>
|
|||
|
/// 裁切指定区域
|
|||
|
/// </summary>
|
|||
|
/// <param name="mat"></param>
|
|||
|
/// <param name="x"></param>
|
|||
|
/// <param name="y"></param>
|
|||
|
/// <param name="width"></param>
|
|||
|
/// <param name="height"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static Mat CutImage(Mat mat, int x, int y, int width, int height)
|
|||
|
{
|
|||
|
Rect roi = new Rect(x, y, width, height);
|
|||
|
return new Mat(mat, roi).Clone();
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 裁边
|
|||
|
/// <summary>
|
|||
|
/// 裁边
|
|||
|
/// </summary>
|
|||
|
/// <param name="mat_rgb"></param>
|
|||
|
/// <param name="isLeft"></param>
|
|||
|
/// <param name="marginHoleWidth"></param>
|
|||
|
/// <param name="marginWidth"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static Mat getMaxInsetRect2(Mat mat_rgb, bool isLeft, int marginHoleWidth, out int marginWidth)
|
|||
|
{
|
|||
|
int bian = 3500;
|
|||
|
Rect Roi;
|
|||
|
if (!isLeft)
|
|||
|
Roi = new Rect(mat_rgb.Width - bian, 0, bian, mat_rgb.Height);
|
|||
|
else
|
|||
|
Roi = new Rect(0, 0, bian, mat_rgb.Height);
|
|||
|
int type = isLeft ? 1 : 0;
|
|||
|
int len = EdgeClipping2(mat_rgb, type, Roi, isLeft);
|
|||
|
#if false
|
|||
|
//Mat mat_rgb = new Mat("E:\\CPL\\测试代码\\边缘检测\\test\\test\\test\\img\\19.bmp");
|
|||
|
Mat image_gray = new Mat();
|
|||
|
Cv2.CvtColor(mat_rgb, image_gray, ColorConversionCodes.BGR2GRAY);
|
|||
|
//cvtColor(image_RGB, image, COLOR_RGB2GRAY);
|
|||
|
int height = image_gray.Rows;
|
|||
|
int width = image_gray.Cols;
|
|||
|
|
|||
|
// 算法定义:取均分5段图片的五条横线,经过一系列处理之后,二值化,找到沿边位置,然后取均值作为直边,在缩进一段有针眼的位置
|
|||
|
// 定义每段的行数
|
|||
|
int num_rows = 5;
|
|||
|
int segment_height = height / num_rows - 1;
|
|||
|
|
|||
|
// 定义空数组保存结果
|
|||
|
int[] total = new int[num_rows];
|
|||
|
|
|||
|
// 平均截取5行数据并处理图像
|
|||
|
for (int i = 0; i < num_rows; i++)
|
|||
|
{
|
|||
|
// 截取当前行的图像
|
|||
|
int start_row = i * segment_height;
|
|||
|
Rect roi = new Rect(0, start_row, width, 1);
|
|||
|
Mat current_segment = image_gray.Clone(roi);
|
|||
|
|
|||
|
// 对当前行的图像进行平滑处理
|
|||
|
Mat smoothed_image = new Mat();
|
|||
|
Cv2.GaussianBlur(current_segment, smoothed_image, new Size(5, 1), 0);
|
|||
|
|
|||
|
// 计算当前行的灰度直方图
|
|||
|
Mat absolute_histo = new Mat();
|
|||
|
Cv2.CalcHist(new Mat[] { smoothed_image }, new int[] { 0 }, new Mat(), absolute_histo, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) });
|
|||
|
Cv2.GaussianBlur(current_segment, smoothed_image, new Size(19, 1), 0);
|
|||
|
|
|||
|
// 对图片进行分割i+1
|
|||
|
//double otsu_threshold;
|
|||
|
//threshold(smoothed_image, smoothed_image, 0, 255, THRESH_BINARY + THRESH_OTSU, &otsu_threshold);
|
|||
|
Cv2.Threshold(smoothed_image, smoothed_image, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
|
|||
|
|
|||
|
// 使用形态学操作进行孔洞填充
|
|||
|
Mat kernel = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(25, 1));
|
|||
|
Mat filled_image = new Mat();
|
|||
|
Cv2.MorphologyEx(smoothed_image, filled_image, MorphTypes.Close, kernel);
|
|||
|
|
|||
|
// 取较长的一个值作为皮革的宽度
|
|||
|
int num_255 = Cv2.CountNonZero(filled_image);
|
|||
|
int length_t = (num_255 > width / 2) ? num_255 : width - num_255;
|
|||
|
total[i] = (length_t);
|
|||
|
API.OutputDebugString($"getMaxInsetRect2: 【{i + 1}】{length_t}={num_255}|{width}");
|
|||
|
}
|
|||
|
// 取平均值作为宽度
|
|||
|
int length = (int)total.Average();
|
|||
|
marginWidth = width-length;
|
|||
|
#endif
|
|||
|
int length = (len > mat_rgb.Width / 2) ? len : mat_rgb.Width - len;
|
|||
|
marginWidth = mat_rgb.Width - length;
|
|||
|
// 判断数据是否异常,判断当前线段的宽度是否大于设定像素的偏差
|
|||
|
//int abnormal_pxl = 200;
|
|||
|
//for (int i = 0; i < num_rows; i++)
|
|||
|
//{
|
|||
|
// if (Math.Abs(total[i] - length) > abnormal_pxl)
|
|||
|
// throw new Exception("数据异常,当段图片的宽度有问题!");
|
|||
|
//}
|
|||
|
|
|||
|
//右侧相机,拍摄产品,边缘位于右侧判断,缩进100像素,去点针眼
|
|||
|
//Cv2.Line(mat_rgb, new Point(length - 100, 0), new Point(length - 100, height), new Scalar(255, 0, 0), 20);
|
|||
|
////左侧相机,拍摄产品,边缘位于左侧判断,缩进100像素,去点针眼
|
|||
|
//Cv2.Line(mat_rgb, new Point(width - length + 100, 0), new Point(width - length + 100, height), new Scalar(0, 255, 0), 20);
|
|||
|
|
|||
|
//int decWidth = width - length + marginHoleWidth;
|
|||
|
//if (isLeft)
|
|||
|
// return cutImage(mat_rgb, decWidth, 0, width- decWidth, height);
|
|||
|
//else
|
|||
|
// return cutImage(mat_rgb, 0, 0, width - decWidth, height);
|
|||
|
|
|||
|
//API.OutputDebugString($"getMaxInsetRect2:margin={marginWidth},length={length}({marginHoleWidth}),isLeft={isLeft},mat_rgb={mat_rgb.Width}*{mat_rgb.Height},w={length - marginHoleWidth},h={mat_rgb.Height}");
|
|||
|
if (isLeft)
|
|||
|
return CutImage(mat_rgb, mat_rgb.Width - length + marginHoleWidth, 0, length - marginHoleWidth, mat_rgb.Height);
|
|||
|
else
|
|||
|
return CutImage(mat_rgb, 0, 0, length - marginHoleWidth, mat_rgb.Height);
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// 寻边算法
|
|||
|
/// </summary>
|
|||
|
/// <param name="image"></param>
|
|||
|
/// <param name="FindType"></param>
|
|||
|
/// <param name="Roi"></param>
|
|||
|
/// <param name="IsLeft"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static int EdgeClipping2(Mat image, int FindType, Rect Roi, bool IsLeft)
|
|||
|
{
|
|||
|
DateTimeOffset startTime = DateTimeOffset.Now;
|
|||
|
Mat mat_rgb = image.Clone(Roi);
|
|||
|
int height = mat_rgb.Rows;
|
|||
|
int width = mat_rgb.Cols;
|
|||
|
int sf = 10; //缩放比例
|
|||
|
int pix = 5; //获取均值区域长宽像素
|
|||
|
int pointNum = 15; //获取找遍点数
|
|||
|
int offsetGray = 5; //二值化偏差
|
|||
|
|
|||
|
//按比例缩放
|
|||
|
int sf_height = height / sf;
|
|||
|
int sf_width = width / sf;
|
|||
|
Cv2.Resize(mat_rgb, mat_rgb, new Size(sf_width, sf_height), 0, 0, InterpolationFlags.Linear);
|
|||
|
Mat himg = new Mat();
|
|||
|
himg = mat_rgb.Clone();
|
|||
|
DateTimeOffset endTime = DateTimeOffset.Now;
|
|||
|
//Console.WriteLine("图片缩小(ms): " + (endTime - startTime).TotalMilliseconds.ToString("0.000"));
|
|||
|
startTime = DateTimeOffset.Now;
|
|||
|
|
|||
|
//滤过去除多余噪声
|
|||
|
//Cv2.EdgePreservingFilter(himg, himg, EdgePreservingMethods.NormconvFilter);
|
|||
|
//Cv2.PyrMeanShiftFiltering(himg, himg, 1, 2, 1);
|
|||
|
Cv2.PyrMeanShiftFiltering(himg, himg, 10, 17, 2);
|
|||
|
//himg.ImWrite("himg.jpg");
|
|||
|
endTime = DateTimeOffset.Now;
|
|||
|
//Console.WriteLine("滤过去除多余噪声(ms): " + (endTime - startTime).TotalMilliseconds.ToString("0.000"));
|
|||
|
|
|||
|
startTime = DateTimeOffset.Now;
|
|||
|
//转灰度图
|
|||
|
Mat image_gray = new Mat();
|
|||
|
Cv2.CvtColor(himg, image_gray, ColorConversionCodes.BGR2GRAY);
|
|||
|
//image_gray.ImWrite("image_gray.jpg");
|
|||
|
|
|||
|
Mat image_Canny = new Mat();
|
|||
|
Cv2.Canny(image_gray, image_Canny, 32, 64);
|
|||
|
//image_Canny.ImWrite("image_Canny.jpg");
|
|||
|
|
|||
|
|
|||
|
//二值化
|
|||
|
Mat image_Otsu = new Mat();
|
|||
|
int hDis = sf_height / (pointNum + 2); //去除边缘两点
|
|||
|
#if false //二值算法
|
|||
|
List<double> LeftAvg = new List<double>();
|
|||
|
List<double> RightAvg = new List<double>();
|
|||
|
//double thb = Cv2.Threshold(image_gray, image_Otsu, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
|
|||
|
#region 多点获取二值化均值
|
|||
|
for (int i = 0; i < pointNum; i++)
|
|||
|
{
|
|||
|
Rect roiLeft = new Rect(0, hDis + hDis * i, pix, pix);
|
|||
|
Mat current_segmentL = image_gray.Clone(roiLeft);
|
|||
|
//Scalar ttr = current_segmentL.Mean();
|
|||
|
LeftAvg.Add(current_segmentL.Mean().Val0);
|
|||
|
|
|||
|
Rect roiRight = new Rect(sf_width - pix, hDis + hDis * i, pix, pix);
|
|||
|
Mat current_segmentR = image_gray.Clone(roiRight);
|
|||
|
RightAvg.Add(current_segmentR.Mean().Val0);
|
|||
|
}
|
|||
|
double thres = 0;
|
|||
|
if (IsLeft)
|
|||
|
{
|
|||
|
if (LeftAvg.Average() > RightAvg.Average())
|
|||
|
thres = RightAvg.Max() + offsetGray;
|
|||
|
else
|
|||
|
thres = RightAvg.Min() - offsetGray;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
if (LeftAvg.Average() > RightAvg.Average())
|
|||
|
thres = LeftAvg.Min() - offsetGray;
|
|||
|
else
|
|||
|
thres = LeftAvg.Max() + offsetGray;
|
|||
|
}
|
|||
|
//double thres = (RightAvg.Average() + )/2;
|
|||
|
#endregion
|
|||
|
#endif
|
|||
|
#if false
|
|||
|
double min, max;
|
|||
|
|
|||
|
image_gray.MinMaxLoc(out min, out max);
|
|||
|
double thres = (min + max) / 2;
|
|||
|
#endif
|
|||
|
|
|||
|
#if false //二值化图片
|
|||
|
//Cv2.Threshold(image_gray, image_Otsu, 0, 255, ThresholdTypes.Otsu);
|
|||
|
double thb = Cv2.Threshold(image_gray, image_Otsu, thres, 255, ThresholdTypes.Binary);
|
|||
|
image_Otsu.ImWrite("Otsu1.jpg");
|
|||
|
Cv2.MedianBlur(image_Otsu, image_Otsu, 21);
|
|||
|
image_Otsu.ImWrite("Otsu2.jpg");
|
|||
|
endTime = DateTimeOffset.Now;
|
|||
|
Console.WriteLine("灰度图二值化(ms): " + (endTime - startTime).TotalMilliseconds.ToString("0.000"));
|
|||
|
startTime = DateTimeOffset.Now;
|
|||
|
#else
|
|||
|
image_Otsu = image_Canny;
|
|||
|
#endif
|
|||
|
// 定义空数组保存结果
|
|||
|
int[] total = new int[pointNum];
|
|||
|
List<int> total_t = new List<int>();
|
|||
|
bool isLeft = FindType == 0 ? true : false;
|
|||
|
// 平均截取pointNum行数据并处理图像
|
|||
|
for (int i = 0; i < pointNum; i++)
|
|||
|
{
|
|||
|
// 截取当前行的图像
|
|||
|
Rect roi = new Rect(0, hDis + hDis * i, sf_width, 1);
|
|||
|
Mat current_segment = image_Otsu.Clone(roi);
|
|||
|
|
|||
|
#if false
|
|||
|
#region 预处理
|
|||
|
// 对当前行的图像进行平滑处理
|
|||
|
Mat smoothed_image2 = new Mat();
|
|||
|
Cv2.GaussianBlur(current_segment, smoothed_image2, new Size(5, 1), 0);
|
|||
|
|
|||
|
// 计算当前行的灰度直方图
|
|||
|
Mat absolute_histo2 = new Mat();
|
|||
|
|
|||
|
Cv2.CalcHist(new Mat[] { smoothed_image2 }, new int[] { 0 }, new Mat(), absolute_histo2, 1, new int[] { 256 }, new Rangef[] { new Rangef(0, 256) });
|
|||
|
Cv2.GaussianBlur(current_segment, smoothed_image2, new Size(9, 1), 0);
|
|||
|
|
|||
|
// 对图片进行分割
|
|||
|
//double otsu_threshold;
|
|||
|
//threshold(smoothed_image, smoothed_image, 0, 255, THRESH_BINARY + THRESH_OTSU, &otsu_threshold);
|
|||
|
double otsu_threshold2 = Cv2.Threshold(smoothed_image2, smoothed_image2, 0, 255, ThresholdTypes.Binary | ThresholdTypes.Otsu);
|
|||
|
|
|||
|
// 使用形态学操作进行孔洞填充
|
|||
|
Mat kernel3 = Cv2.GetStructuringElement(MorphShapes.Rect, new Size(5, 1));
|
|||
|
Mat filled_image3 = new Mat();
|
|||
|
Cv2.MorphologyEx(smoothed_image2, filled_image3, MorphTypes.Close, kernel3);
|
|||
|
#endregion
|
|||
|
#else
|
|||
|
//Mat filled_image3 = current_segment.Clone();
|
|||
|
Mat filled_image3 = current_segment;
|
|||
|
#endif
|
|||
|
#if true
|
|||
|
//从左到右判断边和从右到左判断边
|
|||
|
int numX = 0;
|
|||
|
byte tempVal = 0;
|
|||
|
if (isLeft)
|
|||
|
{
|
|||
|
tempVal = filled_image3.At<byte>(0, 0);
|
|||
|
for (int j = 0; j < filled_image3.Cols; j++)
|
|||
|
{
|
|||
|
if (filled_image3.At<byte>(0, j) != tempVal)
|
|||
|
{
|
|||
|
numX = j;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
tempVal = filled_image3.At<byte>(0, filled_image3.Cols - 1);
|
|||
|
for (int j = filled_image3.Cols - 1; j >= 0; j--)
|
|||
|
{
|
|||
|
if (filled_image3.At<byte>(0, j) != tempVal)
|
|||
|
{
|
|||
|
numX = j;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#else
|
|||
|
int numX = Cv2.CountNonZero(filled_image3);
|
|||
|
#endif
|
|||
|
//int length_t = (numX > (sf_width / 2)) ? numX :sf_width - numX;
|
|||
|
int length_t = numX;
|
|||
|
total[i] = (length_t);
|
|||
|
if (length_t > 0)
|
|||
|
total_t.Add(length_t);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 取平均值作为宽度
|
|||
|
int length = 0;
|
|||
|
if(total_t.Count> 0)
|
|||
|
length = (int)total_t.Average();
|
|||
|
|
|||
|
endTime = DateTimeOffset.Now;
|
|||
|
//Console.WriteLine("计算边(ms): " + (endTime - startTime).TotalMilliseconds.ToString("0.000"));
|
|||
|
|
|||
|
// 判断数据是否异常,判断当前线段的宽度是否大于设定像素的偏差
|
|||
|
//int abnormal_pxl = 100 / 4;
|
|||
|
//for (int i = 0; i < pointNum; i++)
|
|||
|
//{
|
|||
|
// if (Math.Abs(total[i] - length) > abnormal_pxl)
|
|||
|
// Console.WriteLine("数据异常!");
|
|||
|
// //出现数据异常,当段图片的宽度有问题
|
|||
|
//}
|
|||
|
|
|||
|
//乘上换算系数还原
|
|||
|
length = length * sf + Roi.X;
|
|||
|
return length;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
|
|||
|
#region 合并
|
|||
|
/// <summary>
|
|||
|
/// 合并MAT(宽高必需一致)
|
|||
|
/// </summary>
|
|||
|
/// <param name="mats"></param>
|
|||
|
/// <param name="isHorizontal"></param>
|
|||
|
/// <returns></returns>
|
|||
|
public static Mat MergeImage_sameSize(Mat[] mats, bool isHorizontal = true)
|
|||
|
{
|
|||
|
Mat matOut = new Mat();
|
|||
|
if (isHorizontal)
|
|||
|
Cv2.HConcat(mats, matOut);//横向拼接
|
|||
|
else
|
|||
|
Cv2.VConcat(mats, matOut);//纵向拼接
|
|||
|
return matOut;
|
|||
|
}
|
|||
|
#endregion
|
|||
|
}
|
|||
|
}
|