基于均值坐标(Mean-Value Coordinates)的图像融合算法的优化实现

简介: 基于均值坐标(Mean-Value Coordinates)的图像融合算法的优化实现

基于均值坐标(Mean-Value Coordinates)的图像融合算法的优化实现

目录

1. 概述

我在之前的文章《基于均值坐标(Mean-Value Coordinates)的图像融合算法的具体实现》中,根据《Coordinates for Instant Image Cloning》这篇论文,详细论述了图像融合中泊松融合算法的优化算法——均值坐标(Mean-Value Coordinates)融合算法的具体实现。其实在这篇论文中,还提出了两种优化实现,能够进一步提升效率,这里就论述一下其优化算法的具体实现。

2. 实现

2.1. 原理

均值坐标融合算法的核心思想是算出ROI中每个点的MVC(Mean-Value Coordinates),如果ROI中存在n个点,ROI边界像素为m个点,那么该算法的时间复杂度至少为O(nm)。根据《Coordinates for Instant Image Cloning》的描述,MVC融合算法修正值其实是一个线性区间,只要得到其中一些关键点的融合修正值,其他点的融合修正值就可以根据周围的融合修正值线性插值出来。

因此,可以通过CGAL来对ROI多边形边界构建一个自适应三角网,以边界上每个栅格点作为约束构网,为了满足Delaunay特性,就会在ROI内部新添加一些点,这样就会出现边界小而密集,边界大而稀疏的自适应三角网(可参看这篇文章《通过CGAL将一个多边形剖分成Delaunay三角网》):

这样,n个点就会将为常数级别个数c,也就是时间复杂度至少为O(cm)。当然从三角面上插值也会有时间消耗,但时间复杂度会明显小于计算均值坐标。

2.2. 核心代码

具体核心代码实现如下(完整的代码实现地址见文章末尾):

//三角网优化
void QImageShowWidget::MVCBlendOptimize(int posX, int posY)
{
    QTime startTime = QTime::currentTime();
    //Step1:找到边界上所有的像素点
    vector<Vector2d> ROIBoundPointList;
    CalBoundPoint(ROIBoundPointList);
    //
    CDT cdt;
    vector<Vertex_handle> vertexList;    
    for(int i = 0; i<ROIBoundPointList.size(); i++)
    {
        //cout<<ROIBoundPointList[i].x<<','<<ROIBoundPointList[i].y<<'\t';
        //vertexList.push_back(cdt.insert(Point(pointList[i].x(), pointList[i].y() )));
        vertexList.push_back(cdt.insert(CDTPoint(ROIBoundPointList[i].x, ROIBoundPointList[i].y )));
    }
    for(unsigned int i =0;i<vertexList.size()-1;i++)
    {
        cdt.insert_constraint(vertexList[i],vertexList[i+1]);
    }
    std::cout << "Number of vertices: " << cdt.number_of_vertices() <<std::endl;
    std::cout << "Meshing the triangulation..." << std::endl;
    CGAL::refine_Delaunay_mesh_2(cdt, Criteria());
    std::cout << "Number of vertices: " << cdt.number_of_vertices() <<std::endl;
    vector<Vector2d> vertex_list;
    map<Vector2d, size_t> vertex_map;
    for(CDT::Vertex_iterator vit = cdt.vertices_begin(); vit!= cdt.vertices_end(); ++vit)
    {
        vertex_map.insert(make_pair(Vector2d(vit->point().x(), vit->point().y()), vertex_list.size()));
        vertex_list.push_back(Vector2d(vit->point().x(), vit->point().y()));
    }
    //计算边界的像素差值
    vector<int> diff;
    for(size_t i = 0; i < ROIBoundPointList.size()-1; i++)
    {
        //size_t l = (size_t) srcImg.cols * ROIBoundPointList[i].y + ROIBoundPointList[i].x;
        for(int bi = 0; bi < winBandNum; bi++)
        {
            size_t m = (size_t) dstImg.cols * winBandNum * (ROIBoundPointList[i].y + posY)+ winBandNum * (ROIBoundPointList[i].x + posX) + bi;
            size_t n = (size_t) srcImg.cols * winBandNum * ROIBoundPointList[i].y + winBandNum * ROIBoundPointList[i].x + bi;
            int d = (int)(dstImg.data[m]) - (int)(srcImg.data[n]);
            diff.push_back(d);
            //rMat.data[n] = d;
        }
        //clipMap[l] = false;         //在多边形边上的点没法计算MVC
    }
    cout<<"开始计算 mean-value coordinates..." << endl;
    vector<Vec3d> tri_mesh_vertex_R(vertex_list.size());
    #pragma omp parallel for        //开启OpenMP并行加速
    for (int vi = 0; vi < vertex_list.size(); ++vi)
    {
        //逐点计算MVC
        vector<double> alphaAngle(ROIBoundPointList.size());
        for(size_t pi = 1; pi < ROIBoundPointList.size(); pi++)
        {
            alphaAngle[pi] = threePointCalAngle(ROIBoundPointList[pi-1], vertex_list[vi], ROIBoundPointList[pi]);
        }
        alphaAngle[0] = alphaAngle[ROIBoundPointList.size()-1];
        vector<double> MVC(ROIBoundPointList.size()-1, 0);
        for(size_t pi = 1; pi < ROIBoundPointList.size(); pi++)
        {
            double w_a = tan(alphaAngle[pi-1]/2) + tan(alphaAngle[pi]/2);
            double w_b = (ROIBoundPointList[pi-1] - vertex_list[vi]).Mod();
            MVC[pi-1] = w_a / w_b;
            if(_isnan(MVC[pi-1])==1)
            {
                MVC[pi-1] = 0;
            }
        }
        double sum = 0;
        for(size_t pi = 0; pi < MVC.size(); pi++)
        {
            sum = sum + MVC[pi];
        }
        for(size_t pi = 0; pi < MVC.size(); pi++)
        {
            MVC[pi] = MVC[pi] / sum;
        }
        Vec3d r(0.0,0.0,0.0);
        for(size_t pi = 0; pi < MVC.size(); pi++)
        {
            for(int bi = 0; bi < winBandNum; bi++)
            {
                r[bi] = r[bi] + MVC[pi] * diff[pi * winBandNum + bi];
            }
        }
        tri_mesh_vertex_R[vi] = r;
    }
    cout<<"计算完成!" << endl;
    //遍历每一个三角面
    vector<vector<size_t>> face_vertex_index;
    CDT::Face_iterator fit;
    for (fit = cdt.faces_begin(); fit!= cdt.faces_end(); ++fit)
    {
        vector<size_t> index(3);
        for(int i = 0; i<3; i++)
        {
            auto iter = vertex_map.find(Vector2d(fit->vertex(i)->point().x(), fit->vertex(i)->point().y()));
            if(iter == vertex_map.end())
            {
                continue;
            }
            index[i] = iter->second;
        }
        face_vertex_index.push_back(index);
    }
    size_t srcImgBufNum = static_cast<size_t>(srcImg.cols) * static_cast<size_t>(srcImg.rows);
    vector<size_t> clipMap(srcImgBufNum, 0);           //标识范围内的点: 0标识初始不能写入,1以上标识在那个三角形
    #pragma omp parallel for        //开启OpenMP并行加速
    for(int fi = 0; fi < face_vertex_index.size(); fi++)
    {
        Vector2d v0 = vertex_list[face_vertex_index[fi][0]];
        Vector2d v1 = vertex_list[face_vertex_index[fi][1]];
        Vector2d v2 = vertex_list[face_vertex_index[fi][2]];
        double minX = std::min(std::min(v0.x, v1.x), v2.x);
        double minY = std::min(std::min(v0.y, v1.y), v2.y);
        double maxX = std::max(std::max(v0.x, v1.x), v2.x);
        double maxY = std::max(std::max(v0.y, v1.y), v2.y);
        int sX = std::max(int(floor(minX)), 0);
        int sY = std::max(int(floor(minY)), 0);
        int eX = std::max(int(ceil(maxX)), srcImg.cols - 1);
        int eY = std::max(int(ceil(maxY)), srcImg.rows - 1);
        for(int yi = sY; yi <= eY; yi++)
        {
            for(int xi = sX; xi <= eX; xi++)
            {
                if(PointinTriangle(Vector3d(v0), Vector3d(v1), Vector3d(v2), Vector3d(xi, yi, 0)))
                {
                    size_t m = static_cast<size_t>(srcImg.cols) * static_cast<size_t>(yi) + xi;
                    clipMap[m] = fi+1;
                }
            }
        }
    }
    cout<<"开始插值计算..." << endl;
    //Mat result(srcImg.rows, srcImg.cols, CV_8UC1);
    #pragma omp parallel for
    for (int ri = 0; ri < srcImg.rows; ++ri)
    {
        for (int ci = 0; ci < srcImg.cols; ++ci)
        {
            size_t l = (size_t) srcImg.cols * ri + ci;
            if(clipMap[l] == 0)
            {
                continue;
            }
            if(!Point_In_Polygon_2D(ci, ri, ROIBoundPointList))
            {
                continue;
            }
            size_t fi = clipMap[l]-1;
            size_t index0 = face_vertex_index[fi][0];
            size_t index1 = face_vertex_index[fi][1];
            size_t index2 = face_vertex_index[fi][2];
            vector<double> r(winBandNum, 0);
            for(int bi = 0; bi < winBandNum; bi++)
            {
                Vector3d p0(vertex_list[index0].x, vertex_list[index0].y, tri_mesh_vertex_R[index0][bi]);
                Vector3d p1(vertex_list[index1].x, vertex_list[index1].y, tri_mesh_vertex_R[index1][bi]);
                Vector3d p2(vertex_list[index2].x, vertex_list[index2].y, tri_mesh_vertex_R[index2][bi]);
                Vector3d vp(ci, ri, 0);
                CalPlanePointZ(p0, p1, p2, vp);
                r[bi] = vp.z;
            }
            for(int bi = 0; bi < winBandNum; bi++)
            {
                size_t n = (size_t) srcImg.cols * winBandNum * ri + winBandNum * ci + bi;
                size_t m = (size_t) dstImg.cols * winBandNum * (ri + posY)+ winBandNum * (ci + posX) + bi;
                dstImg.data[m] = min(max(srcImg.data[n] + r[bi], 0.0), 255.0);
            }
        }
    }
    //imwrite("D:/result.tif", result);
    cout<<"插值完成!" << endl;
    QTime stopTime = QTime::currentTime();
    int elapsed = startTime.msecsTo(stopTime);
    cout<<"总结完成用时"<<elapsed<<"毫秒";
}

主要思路还是通过ROI多边形栅格建立三角网,计算网格点的MVC,继而计算融合修正值;而其他点的融合修正值则通过所在三角形顶点的融合修正值插值得到。

注意这里麻烦的地方是还得计算每个点是在那个三角形内,我这里是采取索引数组的办法。如果直接采取遍历每个点与每个三角形的办法,那么时间复杂度可能会超过计算MVC的复杂度。而插值的算法可以参考这篇文章《已知空间三点组成的面求该面上某点的Z值》

2.3. 第二种优化

《Coordinates for Instant Image Cloning》这篇论文中,还介绍了第二种优化算法。算法思想是除了减少ROI内的点,还可以减少ROI边界上的点:每个点的MVC值其实可以不用到边界上所有的点,可以通过一种规则算法来指定需要的点。可惜这个规则算法我也没看明白,有机会再进一步研究。

3. 结果

融合的源影像:


融合的目标影像:


融合的结果:


运行的时间:

这里可以看到,优化后的融合效率为501毫秒,而优化之前的效率为1秒,效率提升了50%。

实现代码

分类: 图像处理 , 计算机视觉

标签: Mean-Value , OpenCV , 泊松融合 , 均值坐标 , 图像融合

相关文章
|
9天前
|
机器学习/深度学习 人工智能 自然语言处理
深度学习中的优化算法及其应用
【10月更文挑战第8天】 本文将探讨深度学习中常用的优化算法,包括梯度下降法、Adam和RMSProp等,介绍这些算法的基本原理与应用场景。通过实例分析,帮助读者更好地理解和应用这些优化算法,提高深度学习模型的训练效率与性能。
106 63
|
12天前
|
机器学习/深度学习 人工智能 自然语言处理
【MM2024】阿里云 PAI 团队图像编辑算法论文入选 MM2024
阿里云人工智能平台 PAI 团队发表的图像编辑算法论文在 MM2024 上正式亮相发表。ACM MM(ACM国际多媒体会议)是国际多媒体领域的顶级会议,旨在为研究人员、工程师和行业专家提供一个交流平台,以展示在多媒体领域的最新研究成果、技术进展和应用案例。其主题涵盖了图像处理、视频分析、音频处理、社交媒体和多媒体系统等广泛领域。此次入选标志着阿里云人工智能平台 PAI 在图像编辑算法方面的研究获得了学术界的充分认可。
【MM2024】阿里云 PAI 团队图像编辑算法论文入选 MM2024
|
4天前
|
存储 缓存 算法
如何通过优化算法和代码结构来提升易语言程序的执行效率?
如何通过优化算法和代码结构来提升易语言程序的执行效率?
|
4天前
|
机器学习/深度学习 人工智能 算法
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
21 0
[大语言模型-算法优化] 微调技术-LoRA算法原理及优化应用详解
|
11天前
|
算法
基于粒子群算法的分布式电源配电网重构优化matlab仿真
本研究利用粒子群算法(PSO)优化分布式电源配电网重构,通过Matlab仿真验证优化效果,对比重构前后的节点电压、网损、负荷均衡度、电压偏离及线路传输功率,并记录开关状态变化。PSO算法通过迭代更新粒子位置寻找最优解,旨在最小化网络损耗并提升供电可靠性。仿真结果显示优化后各项指标均有显著改善。
|
6天前
|
机器学习/深度学习 算法 数据挖掘
基于GWO灰狼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了基于分组卷积神经网络(GroupCNN)和灰狼优化(GWO)的时间序列回归预测算法。算法运行效果良好,无水印展示。使用Matlab2022a开发,提供完整代码及详细中文注释。GroupCNN通过分组卷积减少计算成本,GWO则优化超参数,提高预测性能。项目包含操作步骤视频,方便用户快速上手。
|
8天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于WOA鲸鱼优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
本项目展示了一种基于WOA优化的GroupCNN分组卷积网络时间序列预测算法。使用Matlab2022a开发,提供无水印运行效果预览及核心代码(含中文注释)。算法通过WOA优化网络结构与超参数,结合分组卷积技术,有效提升预测精度与效率。分组卷积减少了计算成本,而WOA则模拟鲸鱼捕食行为进行优化,适用于多种连续优化问题。
|
18天前
|
机器学习/深度学习 算法 数据安全/隐私保护
基于GA遗传优化的GroupCNN分组卷积网络时间序列预测算法matlab仿真
该算法结合了遗传算法(GA)与分组卷积神经网络(GroupCNN),利用GA优化GroupCNN的网络结构和超参数,提升时间序列预测精度与效率。遗传算法通过模拟自然选择过程中的选择、交叉和变异操作寻找最优解;分组卷积则有效减少了计算成本和参数数量。本项目使用MATLAB2022A实现,并提供完整代码及视频教程。注意:展示图含水印,完整程序运行无水印。
|
16天前
|
机器学习/深度学习 算法 决策智能
【机器学习】揭秘深度学习优化算法:加速训练与提升性能
【机器学习】揭秘深度学习优化算法:加速训练与提升性能
|
11天前
|
机器学习/深度学习 人工智能 算法
【MM2024】面向 StableDiffusion 的多目标图像编辑算法 VICTORIA
阿里云人工智能平台 PAI 团队与华南理工大学合作在国际多媒体顶级会议 ACM MM2024 上发表 VICTORIA 算法,这是一种面向 StableDiffusion 的多目标图像编辑算法。VICTORIA 通过文本依存关系来修正图像编辑过程中的交叉注意力图,从而确保关系对象的一致性,支持用户通过修改描述性提示一次性编辑多个目标。

热门文章

最新文章