首页 > 其他分享 >OpenCV开发笔记(八十二):两图拼接使用渐进色蒙版场景过渡缝隙

OpenCV开发笔记(八十二):两图拼接使用渐进色蒙版场景过渡缝隙

时间:2024-11-02 08:51:59浏览次数:4  
标签:matLeft 色蒙版 matRight col 两图 openCVDemo 八十二 cv row

若该文为原创文章,转载请注明原文出处
本文章博客地址:https://hpzwl.blog.csdn.net/article/details/143432922

长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中…

OpenCV开发专栏(点击传送门)

上一篇:《OpenCV开发笔记(八十一):通过棋盘格使用鱼眼方式标定相机内参矩阵矫正摄像头图像
下一篇:持续补充中…


前言

  对于图像拼接,前面探讨了通过基于Stitcher进行拼接过渡和基于特征点进行拼接过渡,这2个过渡的方式是摄像头拍摄角度和方向不应差距太大。
  对于特定的场景,本身摄像头拍摄角度差距较大,拉伸变换后也难做到完美的缝隙拼接,这个时候使用渐近过渡反倒是最好的。


Demo

  单独蒙版
   在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述

  蒙版过渡,这里只是根据图来,其实可对每个像素对于第一张图为系数k,而第二张为255-k,实现渐近过渡。
  在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述

  直接使用第一张蒙版优化
  在这里插入图片描述

  在这里插入图片描述

  在这里插入图片描述


准本蒙版

  蒙版可以混合,也可以分开,为了让读者更好的深入理解原理,这里都使用:
  找个工具,造单色渐进色,红色蒙版,只是r通道,bga都为0
  在这里插入图片描述

  (注意:使用rgba四通道)
  在这里插入图片描述

  (上面这张图,加了边框,导致了“入坑二”打印像素值不对)
  在这里插入图片描述

  由于工具渐进色无法叠层,这个工具无法实现rgba不同向渐进色再一张图(横向、纵向、斜向),更改了方式,每个使用一张图:
  为了方便,不管a通道了,直接a为100%(255)。
  在这里插入图片描述

  再弄另外一个通道的:
  在这里插入图片描述

  在这里使用工具就只能单独一张了:
  在这里插入图片描述


一个蒙版图的过渡实例

步骤一:打开图片和蒙版

  在这里插入图片描述

   cv::Mat matLeft = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/29.jpg");
    cv::Mat matRight = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/30.jpg");
    cv::Mat matMask1 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/37.png", cv::IMREAD_UNCHANGED);
    cv::Mat matMask2 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/38.png", cv::IMREAD_UNCHANGED);
    cv::Mat matMask3 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/39.png", cv::IMREAD_UNCHANGED);
    cv::Mat matMask4 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/40.png", cv::IMREAD_UNCHANGED);

步骤二:将蒙版变成和原图一样大小

  在这里插入图片描述

    cv::resize(matLeft, matLeft, cv::Size(0, 0), 0.5, 0.5);
    cv::resize(matRight, matRight, cv::Size(0, 0), 0.5, 0.5);
    cv::resize(matMask1, matMask1, cv::Size(matLeft.cols, matLeft.rows));
    cv::resize(matMask2, matMask2, cv::Size(matLeft.cols, matLeft.rows));
    cv::resize(matMask3, matMask3, cv::Size(matLeft.cols, matLeft.rows));
    cv::resize(matMask4, matMask4, cv::Size(matLeft.cols, matLeft.rows));

步骤三:底图

  由于两张图虽然是同样大小,但是其不是按照整体拼接后的大小,所以需要假设一个拼接后的大小的底图。
  在这里插入图片描述

    // 底图,扩大500横向,方便移动
    cv::Mat matResult = cv::Mat(matLeft.rows, matLeft.cols + 500, CV_8UC3);

步骤四:原图融合

  在这里插入图片描述

        // 副本,每次都要重新清空来调整
        cv::Mat matResult2 = matResult.clone();
#if 1
        // 第一张图,直接比例赋值,因为底图为0
        for(int row = 0; row < matLeft.rows; row++)
        {
            for(int col = 0; col < matLeft.cols; col++)
            {
                double r = matMask1.at<cv::Vec4b>(row, col)[2] / 255.0f;
//                double r = matMask2.at<cv::Vec4b>(row, col)[1] / 255.0f;
//                double r = matMask3.at<cv::Vec4b>(row, col)[0] / 255.0f;
//                double r = matMask4.at<cv::Vec4b>(row, col)[0] / 255.0f;
                matResult2.at<cv::Vec3b>(row, col)[0] = (matLeft.at<cv::Vec3b>(row, col)[0] * r);
                matResult2.at<cv::Vec3b>(row, col)[1] = (matLeft.at<cv::Vec3b>(row, col)[1] * r);
                matResult2.at<cv::Vec3b>(row, col)[2] = (uchar)(matLeft.at<cv::Vec3b>(row, col)[2] * r);
            }
        }
#endif

步骤五:另外一张图的融合

  在这里插入图片描述

#if 1
        // 第二张图,加法,因为底图为原图了
        for(int row = 0; row < matRight.rows; row++)
        {
            for(int col = 0; col < matRight.cols; col++)
            {
                double g = matMask2.at<cv::Vec4b>(row, col)[1] / 255.0f;
                // 偏移了x坐标
                matResult2.at<cv::Vec3b>(row, col + x)[0] += matRight.at<cv::Vec3b>(row, col)[0] * g;
                matResult2.at<cv::Vec3b>(row, col + x)[1] += matRight.at<cv::Vec3b>(row, col)[1] * g;
                matResult2.at<cv::Vec3b>(row, col + x)[2] += matRight.at<cv::Vec3b>(row, col)[2] * g;
            }
        }
#endif

步骤六(与步骤五互斥):优化的融合

  在这里插入图片描述

#if 1
        // 第二张图,加法,因为底图为原图了(优化)
        for(int row = 0; row < matRight.rows; row++)
        {
            for(int col = 0; col < matRight.cols; col++)
            {
                double r2;
                if(x + col <= matLeft.cols)
                {
                    r2 = (255 - matMask1.at<cv::Vec4b>(row, col + x)[2]) / 255.0f;
                }else{
                    r2 = 1.0f;
                }
                // 偏移了x坐标
                matResult2.at<cv::Vec3b>(row, col + x)[0] += matRight.at<cv::Vec3b>(row, col)[0] * r2;
                matResult2.at<cv::Vec3b>(row, col + x)[1] += matRight.at<cv::Vec3b>(row, col)[1] * r2;
                matResult2.at<cv::Vec3b>(row, col + x)[2] += matRight.at<cv::Vec3b>(row, col)[2] * r2;
            }
        }
#endif

函数原型

  手码的像素算法,没有什么高级函数。


Demo源码

void OpenCVManager::testMaskSplicing()
{
    cv::Mat matLeft = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/29.jpg");
    cv::Mat matRight = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/30.jpg");
    cv::Mat matMask1 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/37.png", cv::IMREAD_UNCHANGED);
    cv::Mat matMask2 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/38.png", cv::IMREAD_UNCHANGED);
    cv::Mat matMask3 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/39.png", cv::IMREAD_UNCHANGED);
    cv::Mat matMask4 = cv::imread("D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/40.png", cv::IMREAD_UNCHANGED);

#if 0
    // 打印通道数和数据类型
    // ..\openCVDemo\modules\openCVManager\OpenCVManager.cpp 9166 "2024-10-31 20:07:42:619" 4 24 24
    LOG << matMask.channels() << matMask.type() << CV_8UC4; // 4 24
    // 打印mask蒙版行像素,隔一定行数打一次
    for(int row = 0; row < matMask.rows; row += 10)
    {
        for(int col = 100; col < matMask.cols; col++)
        {
            int r = matMask.at<cv::Vec4b>(row, col)[2];
            int g = matMask.at<cv::Vec4b>(row, col)[1];
            int b = matMask.at<cv::Vec4b>(row, col)[0];
            int a = matMask.at<cv::Vec4b>(row, col)[3];
            LOG << "row:" << row << ", col:" << col << "r(rgba):" << r << g << b << a;
            break;
        }
    }
#endif

    // 图片较大,缩为原来的0.5倍
    cv::resize(matLeft, matLeft, cv::Size(0, 0), 0.5, 0.5);
    cv::resize(matRight, matRight, cv::Size(0, 0), 0.5, 0.5);
    cv::resize(matMask1, matMask1, cv::Size(matLeft.cols, matLeft.rows));
    cv::resize(matMask2, matMask2, cv::Size(matLeft.cols, matLeft.rows));
    cv::resize(matMask3, matMask3, cv::Size(matLeft.cols, matLeft.rows));
    cv::resize(matMask4, matMask4, cv::Size(matLeft.cols, matLeft.rows));
    // 底图,扩大500横向,方便移动
    cv::Mat matResult = cv::Mat(matLeft.rows, matLeft.cols + 500, CV_8UC3);

    // 第一张图
    int key = 0;
    int x = 0;
    while(true)
    {
        // 副本,每次都要重新清空来调整
        cv::Mat matResult2 = matResult.clone();
#if 1
        // 第一张图,直接比例赋值,因为底图为0
        for(int row = 0; row < matLeft.rows; row++)
        {
            for(int col = 0; col < matLeft.cols; col++)
            {
                double r = matMask1.at<cv::Vec4b>(row, col)[2] / 255.0f;
//                double r = matMask2.at<cv::Vec4b>(row, col)[1] / 255.0f;
//                double r = matMask3.at<cv::Vec4b>(row, col)[0] / 255.0f;
//                double r = matMask4.at<cv::Vec4b>(row, col)[0] / 255.0f;
                matResult2.at<cv::Vec3b>(row, col)[0] = (matLeft.at<cv::Vec3b>(row, col)[0] * r);
                matResult2.at<cv::Vec3b>(row, col)[1] = (matLeft.at<cv::Vec3b>(row, col)[1] * r);
                matResult2.at<cv::Vec3b>(row, col)[2] = (uchar)(matLeft.at<cv::Vec3b>(row, col)[2] * r);
            }
        }
#endif
#if 0
        // 第二张图,加法,因为底图为原图了
        for(int row = 0; row < matRight.rows; row++)
        {
            for(int col = 0; col < matRight.cols; col++)
            {
                double g = matMask2.at<cv::Vec4b>(row, col)[1] / 255.0f;
                // 偏移了x坐标
                matResult2.at<cv::Vec3b>(row, col + x)[0] += matRight.at<cv::Vec3b>(row, col)[0] * g;
                matResult2.at<cv::Vec3b>(row, col + x)[1] += matRight.at<cv::Vec3b>(row, col)[1] * g;
                matResult2.at<cv::Vec3b>(row, col + x)[2] += matRight.at<cv::Vec3b>(row, col)[2] * g;
            }
        }
#endif
#if 1
        // 第二张图,加法,因为底图为原图了(优化)
        for(int row = 0; row < matRight.rows; row++)
        {
            for(int col = 0; col < matRight.cols; col++)
            {
                double r2;
                if(x + col <= matLeft.cols)
                {
                    r2 = (255 - matMask1.at<cv::Vec4b>(row, col + x)[2]) / 255.0f;
                }else{
                    r2 = 1.0f;
                }
                // 偏移了x坐标
                matResult2.at<cv::Vec3b>(row, col + x)[0] += matRight.at<cv::Vec3b>(row, col)[0] * r2;
                matResult2.at<cv::Vec3b>(row, col + x)[1] += matRight.at<cv::Vec3b>(row, col)[1] * r2;
                matResult2.at<cv::Vec3b>(row, col + x)[2] += matRight.at<cv::Vec3b>(row, col)[2] * r2;
            }
        }
#endif

//        cv::imshow("matMask1", matMask1);
//        cv::imshow("matLeft", matLeft);
        cv::imshow("matResult2", matResult2);
        key = cv::waitKey(0);
        if(key == 'a')
        {
            x--;
            if(x < 0)
            {
                x = 0;
            }
        }else if(key == 'd')
        {
            x++;
            if(x + matRight.cols > matResult2.cols)
            {
                x = matResult2.cols - matRight.cols;
            }
        }else if(key == 'q')
        {
            break;
        }
    }
}

工程模板v1.72.0

  在这里插入图片描述


入坑

入坑一:读取通道rgba失败

问题:读取通道rgba失败

  在这里插入图片描述

原因

  是uchar,转换成byte,而不是int
  在这里插入图片描述

解决

  在这里插入图片描述

  在这里插入图片描述

入坑二:读取通道一直是0,0,0,255

问题

  读取通道一直是0,0,0,255。
  在这里插入图片描述

原因

  弄了张图,还是255,然后发现是为了截图更清楚,弄了个边框,而我们打印正好是打印了0位置。
  在这里插入图片描述

  在这里插入图片描述

解决

  最终是要去掉边框,没边框就是空看不出,如下图:
  在这里插入图片描述

  在这里插入图片描述

入坑三:过渡有黑线赋值不对

问题

  直接位置赋值,出现条纹
  在这里插入图片描述

  在这里插入图片描述

原因

  类型是vec4b
  在这里插入图片描述

解决

  在这里插入图片描述

  在这里插入图片描述

入坑四:原图融合比例有黑线

问题

  在这里插入图片描述

原因

  跟上面一样,mask蒙版是rgba的,需要vec4b
  在这里插入图片描述

解决

  在这里插入图片描述

  在这里插入图片描述


上一篇:《OpenCV开发笔记(八十一):通过棋盘格使用鱼眼方式标定相机内参矩阵矫正摄像头图像
下一篇:持续补充中…


本文章博客地址:https://hpzwl.blog.csdn.net/article/details/143432922

标签:matLeft,色蒙版,matRight,col,两图,openCVDemo,八十二,cv,row
From: https://blog.csdn.net/qq21497936/article/details/143432922

相关文章

  • kettle从入门到精通 第八十二课 ETL之kettle kettle中的【阻塞数据直到步骤都完成】使
     1、在使用步骤【阻塞数据直到步骤都完成】(英文为Blockthisstepuntilstepsfinish)之前,我们先来了解下什么是 CopyNr? CopyNr是指 “副本编号” 或 “拷贝编号”,也就是下图中的复制的记录行数,图中的两个步骤复制的记录行数都是0,表示只有一个副本。 2、写日志步骤右......
  • 客快物流大数据项目(八十二):Kudu的读写原理 一般有用 看1
    Kudu的读写原理一、​​​​​​​工作模式Kudu的工作模式如下图,有些在上面的内容中已经介绍了,这里简单标注一下:每个kudutable按照hash或range分区为多个tablet;每个tablet中包含一个MemRowSet以及多个DiskRowSet;每个DiskRowSet包含BaseData以及DeltaStores;Delta......
  • 【QT+QGIS跨平台编译】之八十二:【QGIS_Gui跨平台编译】—【错误处理:QgsExpressionCont
    文章目录一、未定义基类QgsExpressionContextGenerator二、错误处理一、未定义基类QgsExpressionContextGenerator报错信息二、错误处理第33行增加:#include"qgsexpressioncontextgenerator.h"......
  • Python计算两图相似性-哈希算法(Hash)
    1、简介aHash:平均值哈希。速度比较快,但是常常不太精确。pHash:感知哈希。精确度比较高,但是速度方面较差一些。dHash:差异值哈希。精确度较高,均值哈希算法、差值哈希算法和感知哈希算法都是值越小,相似度越高,取值为0-64,即汉明距离中,64位的hash值有多少不同。三直方图和单通道直方图......
  • Python计算两图相似性-像素匹配(pixelmatch)
    1、简介GitHub:https://github.com/whtsky/pixelmatch-py 2、代码fromPILimportImagefrompixelmatch.contrib.PILimportpixelmatchimg_a=Image.open("WD1.png").resize((1920,1080))img_b=Image.open("WD2.png").resize((1920,1080))img......
  • Python计算两图相似性-直方图
    1、简介利用直方图计算图片的相似度时,是按照颜色的全局分布情况来看待的,无法对局部的色彩进行分析,同一张图片如果转化成为灰度图时,在计算其直方图时差距就更大了。对于灰度图可以将图片进行等分,然后在计算图片的相似度。 2、代码fromPILimportImage#将图片转化为RG......
  • Python计算两图相似性-余弦相似度
    1、简介把图片表示成一个向量,通过计算向量之间的余弦距离来表征两张图片的相似度。  2、代码fromPILimportImagefromnumpyimportaverage,dot,linalg#对图片进行统一化处理defget_thum(image,size=(1920,1080),greyscale=False):#利用image对图像......
  • Python计算两图相似性-基于互信息(Mutual Information)
    1、简介通过计算两个图片的互信息来表征他们之间的相似度,如果两张图片尺寸相同,还是能在一定程度上表征两张图片的相似性的。但是,大部分情况下图片的尺寸不相同,如果把两张图片尺寸调成相同的话,又会让原来很多的信息丢失,所以很难把握。经过实际验证,此种方法的确很难把握。 2、......
  • 前端歌谣的刷题之路-第八十二题-新数组
     前言我是歌谣我有个兄弟巅峰的时候排名c站总榜19叫前端小歌谣曾经我花了三年的时间创作了他现在我要用五年的时间超越他今天又是接近兄弟的一天人生难免坎坷大不了从头再来歌谣的意志是永恒的放弃很容易但是坚持一定很酷本题目源自于牛客网微信公众号前端小歌谣题目......
  • 第八十二天
    周三,庸庸碌碌的一天早上摆,下午,编译原理,讲的是自下而上的语法分析器,1.规范规约,画出语法树,找子树,找句柄2.优先级规约,(1)三条规则(2)first和last集,用来判断=><其实吧,这节课的东西上课的时候不太明白,现在也是一样晚上,离散,芜湖......