C#景深融合
最近因为一个项目要求,将matlab写的一些关于图像的算法转成C#。这个挺坑爹的,C#处理图像还是挺少的,opencv关于这方面的资源挺少的。我用的是opencvsharp这个第三方库,GitHub上有资源。
这个景深融合其实是一个显微镜扫描成像的问题,一共有数张图像,都是局部清晰,其它部分模糊。比如下面这样:
实际处理思路是:图像边缘检测,然后做了一个平滑处理,根据灰度图的亮度进行筛选,形成一个分区。最后根据分区处理像素的填充。
下面贴代码:
读取图片的操作就略去了,边缘提取用的是sobel,注意一下,opencvsharp中的图像处理注意mat数据的溢出。及时调整mat的像素深度比如16s,通常原始图像读取是8u。像素深度关系到具体坐标的数据格式。
1 class Oper 2 { 3 public static Mat Deal(Mat ImIn) 4 { 5 Mat mat_gray = new Mat(); 6 // 7 //Mat smooth_img = new Mat(); 8 //Cv2.GaussianBlur(ImIn, smooth_img, new OpenCvSharp.Size(3, 3), 0.96, 0.96, BorderTypes.Default); 9 //灰度图 10 11 12 Cv2.CvtColor(ImIn, mat_gray, ColorConversionCodes.BGR2GRAY); 13 //GetMatData.Arr(mat_gray); 14 15 Mat X = new Mat(); Mat Y = new Mat(); 16 //InputArray arr = InputArray.Create<float>(new float[3, 3] { { -1, -2, -1 }, { 0, 0, 0 }, { 1, 2, 1 } }); 17 //Cv2.Filter2D(mat_gray,X , -1, arr, new Point(-1, -1), 0); 18 //InputArray arr_convert = InputArray.Create<float>(new float[3, 3] { { -1, 0, -1 }, { -2, 0, 2 }, { -1, 0, 1 } }); 19 //Cv2.Filter2D(mat_gray, Y, -1, arr, new Point(-1, -1), 0); 20 21 22 //sobel边缘 23 //-----------这里开始转变为16位运算------------- 24 Cv2.Sobel(mat_gray, X, MatType.CV_16S, 1, 0, 3,1,0,BorderTypes.Replicate); 25 Cv2.Sobel(mat_gray, Y, MatType.CV_16S, 0, 1, 3,1,0,BorderTypes.Replicate); 26 //GetMatData.getData(X); 27 //GetMatData.getData(Y); 28 29 int width = X.Cols; int heigth = Y.Rows; 30 //-------double型数据的对应像素深度是64F--------- 31 var output = new Mat(X.Size(),MatType.CV_64F); 32 33 var indexer_x = X.GetGenericIndexer<Int16>(); 34 var indexer_y = Y.GetGenericIndexer<Int16>(); 35 var indexer_op = output.GetGenericIndexer<double>(); 36 //算术平均 37 for (int index = 0; index < heigth ; index++) 38 { 39 for (int index_y = 0; index_y < width; index_y++) 40 { 41 Int16 Gx = indexer_x[index,index_y]; 42 Int16 Gy = indexer_y[index,index_y]; 43 44 double v1 = Math.Pow(Gx, 2); 45 double v2 = Math.Pow(Gy, 2); 46 double val = Math.Sqrt(v1 + v2); 47 48 indexer_op[index, index_y] = val; 49 } 50 } 51 //var ss =indexer_op[0, 0]; 52 53 Cv2.GaussianBlur(output,output,new OpenCvSharp.Size(31,31),11); 54 //Cv2.ImShow("te", output); Cv2.WaitKey(0); 55 return output; 56 } 57 58 }
到这里基本完成了图像的局部提取,下面进行一下分区的对比提取。
这部分我是这样理解的,比如十张局部扫描图,根据像素的梯度变化可以将清晰处与模糊处区分开来,sobel可以很好的将边缘提取出来,经过平滑处理形成分区。
之后十张图相当于MN10 的矩阵,根据像素的梯度变化,每个像素取最大值可以准确提取这部分图像的清晰部分。(这里一个理解是实际上清晰部分的梯度变化较模糊处大,梯度值实际上可以表现为亮度的大小)
像素对比处代码如下:
1 public static Mat id_Mat (Mat[]coll) 2 { 3 int heigth = coll[0].Rows; 4 int width = coll[0].Cols; 5 6 Mat max_id = new Mat(coll[0].Size(),MatType.CV_8U); 7 var indexer_id = max_id.GetGenericIndexer<byte>(); 8 //int ss = max_id.At<double>(10, 10); 9 // 10 11 12 //注意像素深度的设置,数据格式的统一 13 Mat mat1 = coll[0]; 14 var indexer_pre=mat1.GetGenericIndexer<double>(); 15 for (int num = 1; num < coll.Length; num++) 16 { 17 var mat2 = coll[num]; 18 var indexer = mat2.GetGenericIndexer<double>(); 19 for (int index = 0; index < heigth ; index++) 20 { 21 for (int index_y = 0; index_y < width; index_y++) 22 { 23 double G1 = indexer_pre[index,index_y]; 24 double G2 = indexer[index, index_y]; 25 if (G1 <= G2) 26 { 27 double G_double = (double)G2; 28 indexer_pre[index, index_y] = G_double; 29 indexer_id[index, index_y] = (byte)num; 30 } 31 } 32 } 33 } 34 return max_id; 35 }
一点说明,这里为了后续图像合成的方便,输出mat max_id实际是每个像素选取的最大值所属的图像编号。
比如这样的:
1 | 2 |
---|---|
1 | 2 |
左上的像素取图片1中对应像素的值。
最后就是填像素值合成了,如下:
1 class Merge 2 { //ID点阵选择相应ID图像填色 3 public static Mat ImMerge(Mat IDIm,Mat[]Coll_Ori) 4 { 5 Mat ImMer = new Mat(Coll_Ori[0].Size(), Coll_Ori[0].Type()); 6 var indexer_mer=ImMer.GetGenericIndexer<Vec3b>(); 7 var indexer_id = IDIm.GetGenericIndexer<byte>(); 8 9 int heigth = IDIm.Rows; int width = IDIm.Cols; 10 for (int index=0;index<Coll_Ori.Length;index++) 11 { 12 for (int x = 0; x < heigth; x++) 13 { 14 for (int y = 0; y < width; y++) 15 { 16 int val_ID = indexer_id[x, y]; 17 if (val_ID == index) 18 { 19 var Ori_indexer = Coll_Ori[index].GetGenericIndexer<Vec3b>(); 20 indexer_mer[x, y] = Ori_indexer[x, y]; 21 } 22 else { continue; } 23 } 24 } 25 } 26 return ImMer; 27 } 28 }
最后的输出图像如下:
例子1,图有点大,截个屏吧!
例子2
总的来说算法的处理效果是不错的,速度上也可以,毕竟C#还是不太适合做算法。我看网上基于C#的这方面代码还没有,希望对大家有一定的价值!