这篇文章偏向于利用Emgu CV解决实际项目问题,干货满满,因为它涉及到了一个很重要的图形处理环节---Mat类型元素的访问和修改。

对于OpenCV来说,不论是使用环境是C++还是python,都可以直接按照Row、Col坐标点直接读取和修改Mat元素值,而把OpenCV进行C#封装后,Emgu CV就不允许这么操作了。为什么?????我也不知道,但是它还有其它间接的方法,就是与Emgu CV中的Image类来相互转换,共同配合完成像素值的读、写操作。具体演示如下:

1、直接通过Image的索引,三通道彩色图像像素访问及修改

还以大名鼎鼎的lena.jpg举例,将其赋给srcMat。通过Image的索引,完成三通道彩色图像像素访问及修改,具体方法就是:

  • 定义一个与srcMat尺寸相同的三通道图像,Image类型变量,Bgr颜色空间,取名dstImage
  • 将srcMat转换成另一个Image类型变量,Bgr颜色空间,取名tempImage
  • 根据Cols、Rows遍历dstImage
  • 读取指定像素点的方法就是tempImage[i, j].Blue, tempImage[i, j].Green, tempImage[i, j].Red
  • 赋值的方法就是dstImage[i, j] = new Bgr(255, 255, 255);
  • 最终dstImage通过ConvertTo函数转换成8位无符号整型的Mat进行显示。

代码如下:

Mat dstMat = srcMat.Clone();
int width = srcMat.Cols;
int height = srcMat.Rows;
Image<Bgr, int> dstImage = new Image<Bgr, int>(width, height);
Image<Bgr, int> tempImage = dstMat.ToImage<Bgr, int>();
for (int i = 0; i < height; i++)
{
    for (int j = 0; j < width; ++j)
    {
        if (i % 20 < 10)
        {
            dstImage[i, j] = new Bgr(255, 255, 255); ;
        }
        else
        {
            dstImage[i, j] = new Bgr(tempImage[i, j].Blue, tempImage[i, j].Green, tempImage[i, j].Red);
        }
    }
}

dstMat = dstImage.Mat;
dstMat.ConvertTo(dstMat, DepthType.Cv8U);
CvInvoke.Imshow("Result Mat, " + dstMat.Size.ToString(), dstMat);

代码执行后,输出图像如下: 

上述代码执行完成,在我这里大概用了0.04秒左右。 

2、直接通过Image的索引,单通道灰度图像像素访问及修改

除了三通道的图像,还有一种常用的灰度图,它是单通道的。这里就需要增加一个步骤,将原始图篇通过CvtColor()函数转换成灰度图(CvtColor()具体用法和各个参数含义今后会详细介绍),其余步骤和刚刚的代码大同小异。

代码如下:

Mat gray = new Mat();
int width = srcMat.Cols;
int height = srcMat.Rows;
CvInvoke.CvtColor(srcMat, gray, ColorConversion.Bgr2Gray);
Image<Gray, int> dstImage = new Image<Gray, int>(width, height);
Image<Gray, int> tempImage = gray.ToImage<Gray, int>();
for (int i = 0; i < height; i++)
{
    for (int j = 0; j < width; ++j)
    {
        if (i % 20 < 10)
        {
            dstImage[i, j] = new Gray(0);
        }
        else
        {
            dstImage[i, j] = tempImage[i, j];
        }
    }
}

gray = dstImage.Mat;
gray.ConvertTo(gray, DepthType.Cv8U);
CvInvoke.Imshow("Result Mat, " + gray.Size.ToString(), gray);

这段代码执行用时0.015秒左右,效果如下: 

3、Image的Data属性遍历图片,三通道彩色图像像素访问及修改

 上面的两段图像处理代码,对于一副512 * 512大小的图片,处理时间都在百分之几秒左右。看上去是不是速度还可以,但是,对于真正的高速图像处理(比如摄像机指挥机器臂的操作),这个时间还是太慢了。有没有更快一些的办法呢????

那是必须的,Image元素除了直接使用索引以外,它还有一个Data属性,通过这个属性,可以加快图像读写速度。比如第一节介绍的对三通道彩色图像的访问及修改,修改完代码后,执行时间大概在0.005秒,快了十倍。代码如下:

Mat dstMat = srcMat.Clone();
int width = srcMat.Cols;
int height = srcMat.Rows;
Image<Bgr, int> dstImage = new Image<Bgr, int>(width, height);
Image<Bgr, int> tempImage = dstMat.ToImage<Bgr, int>();
for (int i = 0; i < height; i++)
{
    for (int j = 0; j < width; ++j)
    {
        if (i % 20 < 10)
        {
            dstImage.Data[i, j, 0] = 255;
            dstImage.Data[i, j, 1] = 255;
            dstImage.Data[i, j, 2] = 255;
        }
        else
        {
            dstImage.Data[i, j, 0] = tempImage.Data[i, j, 0];
            dstImage.Data[i, j, 1] = tempImage.Data[i, j, 1];
            dstImage.Data[i, j, 2] = tempImage.Data[i, j, 2];
        }
    }
}

dstMat = dstImage.Mat;
dstMat.ConvertTo(dstMat, DepthType.Cv8U);
CvInvoke.Imshow("Result Mat, " + dstMat.Size.ToString(), dstMat);

4、Image的Data属性遍历图片,单通道灰度图像像素访问及修改 

Image的Data属性读写单通道灰度图呢,执行时间大概在0.002秒,大概也是十倍的速度提升。具体代码如下:

Mat gray = new Mat();
int width = srcMat.Cols;
int height = srcMat.Rows;
CvInvoke.CvtColor(srcMat, gray, ColorConversion.Bgr2Gray);
Image<Gray, int> dstImage = new Image<Gray, int>(width, height);
Image<Gray, int> tempImage = gray.ToImage<Gray, int>();
for (int i = 0; i < height; i++)
{
    for (int j = 0; j < width; ++j)
    {
        if (i % 20 < 10)
        {
            dstImage.Data[i, j, 0] = 0;
        }
        else
        {
            dstImage.Data[i, j, 0] = tempImage.Data[i, j, 0];
        }
    }
}

gray = dstImage.Mat;
gray.ConvertTo(gray, DepthType.Cv8U);
CvInvoke.Imshow("Result Mat, " + gray.Size.ToString(), gray);

总结

Emgu CV与C#的相互配合,比OpenCV和C++的方式,在代码运行速度上确实要慢,但是通过合理的代码设计,也可以达到非常理想的运行速度,完全能够满足实际工业项目上的需求。这就需要开发人员多尝试,多试验。

原创不易,请勿抄袭。共同进步,相互学习。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐