OpenCVSharp学习笔记3--OpenCV如何扫描图像、利用查找表和计时/遍历、修改Mat像素

文章通过实现不同方法遍历和修改OpenCVSharp中的Mat像素,包括指针访问、GetIndexer、GetGenericIndexer、GetUnsafeGenericIndexer、At、Get/Set以及LUT,并在Debug和Release模式下对比了它们的性能。结果显示,LUT方法在性能上最优,而Get/Set方法最差。此外,文章提供了用于测试的代码示例。
摘要由CSDN通过智能技术生成

1、目的:遍历、修改Mat像素

本文主要是在学习How to scan images, lookup tables and time measurement with OpenCVOpenCV如何扫描图像、利用查找表和计时过程中,将对应的原文中的C++源码用OpenCVSharp重写,但由于对原文的理解和对OpenCVSharp的了解有限,无法一一重写,只能结合自己的了解,依葫芦画瓢式地进行了重写。

原文章中,主要介绍了一种 颜色空间缩减算法:将现有颜色空间值除以某个输入值,以获得较少的颜色数。例如,颜色值0到9可取为新值0,10到19可取为10,以此类推。并用多种方式实现该算法,以对比各种方式之间的性能。

本文主要实现用Mat按指针、GetGenericIndexer、GetIndexer、GetUnsafeGenericIndexer、At、Get/Set方式以及LUT方法遍历和修改像素。

上代码前,先看看重复执行100次对应算法的耗时对比吧

Debug模式下:

Release模式下:

2、指针访问Mat

private Mat ScanImageAndReduceByPtr(Mat I, byte[] table)
{
    Debug.Assert(I.Depth() == MatType.CV_8U);
    int channels = I.Channels();
    int nRows = I.Rows;
    int nCols = I.Cols * channels;
    if (I.IsContinuous())
    {
        nCols *= nRows;
        nRows = 1;
    }
    unsafe
    {
        for (int i = 0; i < nRows; i++)
        {
            var p = I.Ptr(i);
            byte* b = (byte*)p.ToPointer();
            for (int j = 0; j < nCols; j++)
            {
                b[j] = table[b[j]];
            }
        }
        return I;
    }
}

3、GetIndexer

private Mat ScanImageAndReduceByIndexer(Mat I, byte[] table)
{
    Debug.Assert(I.Depth() == MatType.CV_8U);
    int channels = I.Channels();
    switch (channels)
    {
        case 1:
            var matByte = new Mat<Byte>(I);
            var indexerByte = matByte.GetIndexer();
            for (int r = 0; r < I.Rows; r++)
            {
                for (int c = 0; c < I.Cols; c++)
                {
                    indexerByte[r, c] = table[indexerByte[r, c]];
                }
            }
            break;
        case 3:
            var matVec3b = new Mat<Vec3b>(I);
            var indexerVec3b = matVec3b.GetIndexer();
            for (int r = 0; r < I.Rows; r++)
            {
                for (int c = 0; c < I.Cols; c++)
                {
                    var vec3b = indexerVec3b[r, c];
                    indexerVec3b[r, c] = new Vec3b(table[vec3b[0]], table[vec3b[1]], table[vec3b[2]]);
                }
            }
            break;
    }
    return I;
}

4、GetGenericIndexer

private Mat ScanImageAndReduceByGenericIndexer(Mat I, byte[] table)
{
    Debug.Assert(I.Depth() == MatType.CV_8U);
    int channels = I.Channels();
    switch (channels)
    {
        case 1:
            var at = I.GetGenericIndexer<byte>();
            for (int r = 0; r < I.Rows; r++)
            {
                for (int c = 0; c < I.Cols; c++)
                {
                    at[r, c] = table[at[r, c]];
                }
            }
            break;
        case 3:
            var atb = I.GetGenericIndexer<Vec3b>();
            for (int r = 0; r < I.Rows; r++)
            {
                for (int c = 0; c < I.Cols; c++)
                {
                    var vec3b = atb[r, c];
                    atb[r, c] = new Vec3b(table[vec3b[0]], table[vec3b[1]], table[vec3b[2]]);
                }
            }
            break;
    }
    return I;
}

5、GetUnsafeGenericIndexer

private Mat ScanImageAndReduceByUnsafeIndexer(Mat I, byte[] table)
{
    Debug.Assert(I.Depth() == MatType.CV_8U);
    int channels = I.Channels();
    unsafe
    {
        switch (channels)
        {
            case 1:
                var at = I.GetUnsafeGenericIndexer<byte>();
                for (int r = 0; r < I.Rows; r++)
                {
                    for (int c = 0; c < I.Cols; c++)
                    {
                        at[r, c] = table[at[r, c]];
                    }
                }
                break;
            case 3:
                var atb = I.GetUnsafeGenericIndexer<Vec3b>();
                for (int r = 0; r < I.Rows; r++)
                {
                    for (int c = 0; c < I.Cols; c++)
                    {
                        var vec3b = atb[r, c];
                        atb[r, c] = new Vec3b(table[vec3b[0]], table[vec3b[1]], table[vec3b[2]]);
                    }
                }
                break;
        }
    }
    return I;
}

6、Mat.At

private Mat ScanImageAndReduceByAt(Mat I, byte[] table)
{
    Debug.Assert(I.Depth() == MatType.CV_8U);
    int channels = I.Channels();
    switch (channels)
    {
        case 1:
            for (int i = 0; i < I.Rows; ++i)
            {
                for (int j = 0; j < I.Cols; ++j)
                {
                    I.At<Byte>(i, j) = table[I.At<Byte>(i, j)];
                }
            }
            break;
        case 3:
            for (int i = 0; i < I.Rows; ++i)
                for (int j = 0; j < I.Cols; ++j)
                {
                    var vec3b = I.At<Vec3b>(i, j);
                    I.At<Vec3b>(i, j) = new Vec3b(table[vec3b[0]], table[vec3b[1]], table[vec3b[2]]);
                }
            break;
    }
    return I;
}

7、Mat.Get和Mat.Set

private Mat ScanImageAndReduceByGetSet(Mat I, byte[] table)
{
    Debug.Assert(I.Depth() == MatType.CV_8U);
    int channels = I.Channels();
    switch (channels)
    {
        case 1:
            for (int i = 0; i < I.Rows; ++i)
            {
                for (int j = 0; j < I.Cols; ++j)
                {
                    I.Set<Byte>(i, j, table[I.Get<Byte>(i, j)]);
                }
            }
            break;
        case 3:
            for (int i = 0; i < I.Rows; ++i)
                for (int j = 0; j < I.Cols; ++j)
                {
                    var vec3b = I.Get<Vec3b>(i, j);
                    I.Set<Vec3b>(i, j, new Vec3b(table[vec3b[0]], table[vec3b[1]], table[vec3b[2]]));
                }
            break;
    }
    return I;
}

8、LUT

Mat lookUpTable = new Mat(1, 256, MatType.CV_8U);
var p = lookUpTable.Ptr(0);
Mat matByLUT = new Mat();
const int times = 100;
unsafe
{
    byte* b = (byte*)p.ToPointer();
    for (int i = 0; i < 256; ++i)
        b[i] = table[i];

    for (int i = 0; i < times; ++i)
    {
        Cv2.LUT(matClone, lookUpTable, matByLUT);
    }
}

9、总结

方式

Debug模式

Release模式

LUT

51ms

43ms

指针

185ms

65ms

GetIndexer

1049ms

931ms

GetUnsafeGenericIndexer

1051ms

868ms

At

1401ms

1106ms

GetGenericIndexer

6581ms

6551ms

Get/Set

7370ms

6796ms

由对比可知,LUT方式最优、Get/Set方式最差;GetIndexer与GetUnsafeGenericIndexer基本接近。

图像处理对算法性能的要求较高,如果文中有错误之处,还望您指出,避免误导或踩坑,万分感谢!

因代码使用了unsafe代码,解决方案需启用“允许不安全代码”

补上主函数的代码

public override void Run()
{
    //读取图像
    using var lena = Cv2.ImRead(ImagePath.LenaColor, ImreadModes.Color);
    if (lena.Empty()) return;
    int divideWith = 10;
    byte[] table = new byte[256];
    for (int i = 0; i < 256; ++i)
    {
        table[i] = (byte)(1.0 * divideWith * (i / divideWith));
    }

    Mat matByPtr = DoSIAR(ScanImageAndReduceByPtr, lena, table, "ByPtr");

    Mat matByGenericIndexer = DoSIAR(ScanImageAndReduceByGenericIndexer, lena, table, "ByGenericIndexer");

    Mat matByIndexer = DoSIAR(ScanImageAndReduceByIndexer, lena, table, "ByIndexer");

    Mat matByUnsafeIndexer = DoSIAR(ScanImageAndReduceByUnsafeIndexer, lena, table, "ByUnsafeIndexer");

    Mat matByAt = DoSIAR(ScanImageAndReduceByAt, lena, table, "ByAt");

    Mat matByGetSet = DoSIAR(ScanImageAndReduceByGetSet, lena, table, "ByGetSet"); ;

    Mat lookUpTable = new Mat(1, 256, MatType.CV_8U);
    var p = lookUpTable.Ptr(0);
    Mat matByLUT = new Mat();
    Stopwatch sw;
    const int times = 100;
    unsafe
    {
        byte* b = (byte*)p.ToPointer();
        for (int i = 0; i < 256; ++i)
            b[i] = table[i];

        sw = Stopwatch.StartNew();
        for (int i = 0; i < times; ++i)
        {
            var matClone = lena.Clone();//为了与其它算法一样(耗时)
            Cv2.LUT(matClone, lookUpTable, matByLUT);
        }
    }
    sw.Stop();
    //显示文字
    Helper.PutText(matByLUT, $"{times} times ByLUT:{sw.ElapsedMilliseconds} ms");

    //显示所有图像
    //using (new Window("lena", lena))
    using (new Window("ByPtr", matByPtr))
    using (new Window("ByGenericIndexer", matByGenericIndexer))
    using (new Window("ByIndexer", matByIndexer))
    using (new Window("ByUnsafeIndexer", matByUnsafeIndexer))
    using (new Window("ByAt", matByAt))
    using (new Window("ByGetSet", matByGetSet))
    using (new Window("ByLUT", matByLUT))
    {
        Cv2.WaitKey();
    }
}

private Mat DoSIAR(Func<Mat, byte[], Mat> SIARFun, Mat lena, byte[] table, string name)
{
    const int times = 100;
    Mat mat = null;
    Stopwatch sw = Stopwatch.StartNew();
    for (int i = 0; i < times; ++i)
    {
        mat = lena.Clone();
        mat = SIARFun(mat, table);
    }
    sw.Stop();
    //图像中显示文字
    Helper.PutText(mat, $"{times} times {name}:{sw.ElapsedMilliseconds} ms");
    return mat;
}

源代码:LearnOpenCVSharp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图南科技

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值