Poisson image editing算法实现的Matlab代码解析

之前我发了数篇系列博文来仔细研究Poisson Image Editing算法,每次重新审视和深入,仿佛都能有更为深刻的认识和很大的收获。这应该算是我这个系列的完结篇,会用用Matlab代码一点一点的演示,原文作者到底是如何设计和实现他那个强大且影响深远的算法的。希望你在看本文之前务必参考一下文章来了解算法原理,本文将主要讲解编程实现的问题,对于前面讲过的内容,我不会深究。但我个人总体的感觉是,现在图像处理算法对数学的要求是越来越高了,像泊松融合、泊松抠图这样的算法如果没有偏微分方程(本算法中涉及Poisson Equation)、数值计算(需要高斯-塞德尔迭代法或者共轭梯度法)、场论(涉及散度、梯度和拉普拉斯算子)、矩阵论(涉及大型稀疏矩阵线性方程组)、数学物理方法(涉及带狄利克雷条件的椭圆曲线方程)、最优化理论(涉及到了欧拉-朗格拉日变分法)这些艰深的数学知识,要理解起来实在太困难了(每一步都是一个坎,我感觉有一个地方过不了,那篇经典论文的精华就无法领会,更别说编程实现了)。

图像处理之让手心长出眼睛,其实嘴也可以~

图像的泊松(Poisson)编辑、泊松融合完全详解(3)

图像的泊松(Poisson)编辑、泊松融合完全详解(2)

图像的泊松(Poisson)编辑、泊松融合完全详解(1)

素材是三张图片,如下

bear.jpg                                                              bear-mask.jpg

                        

pool-target.jpg

首先,我们清理一下Matlab的环境:

close all;
clear;
clc;

然后,读入三张图片(注意需要把mask图以二值图的形式读入)

TargetImg   = imread(‘pool-target.jpg‘);
SourceImg   = imread(‘bear.jpg‘);
SourceMask  = im2bw(imread(‘bear-mask.jpg‘));

用函数bwboundaries(W,CONN) 来获取二值图中对象的轮廓,其中CONN 为8或4,指示联通性采用4方向邻域点判别还是8方向邻域点判别,默认为8。

[SrcBoundry, ~] = bwboundaries(SourceMask, 8);

然后我们把这个轮廓在SourceImg上绘制出来,显示出我们将要剪切的区域。参数 ‘r‘ 表示红色。

figure, imshow(SourceImg), axis image
hold on
for k = 1:length(SrcBoundry)
    boundary = SrcBoundry{k};
    plot(boundary(:,2), boundary(:,1), ‘r‘, ‘LineWidth‘, 2)
end
title(‘Source image intended area for cutting from‘);

所得之结果如下图

设定source将要粘贴在target图中的具体位置,并获取TargetImg的长和宽。

position_in_target = [10, 225];%xy
[TargetRows, TargetCols, ~] = size(TargetImg);

函数find()的作用:b=find(X),X是一个矩阵,查询非零元素的位置,如果X是一个行向量,则返回一个行向量,否则,返回一个列向量。

[row, col] = find(SourceMask);

然后来计算mask框在source图中的大小。

start_pos = [min(col), min(row)];
end_pos   = [max(col), max(row)];
frame_size  = end_pos - start_pos;

如果在position_in_target的位置放置frame将超出Target图的范围,则改变position_in_target,以保证frame不会超出Target图的范围。

if (frame_size(1) + position_in_target(1) > TargetCols)
    position_in_target(1) = TargetCols - frame_size(1);
end

if (frame_size(2) + position_in_target(2) > TargetRows)
    position_in_target(2) = TargetRows - frame_size(2);
end

构建一个大小与Target图相等的新的Mask,然后在position_in_target的位置放入SourceMask。

MaskTarget = zeros(TargetRows, TargetCols);
MaskTarget(sub2ind([TargetRows, TargetCols], row - start_pos(2) + position_in_target(2), ...
 col - start_pos(1) + position_in_target(1))) = 1;

此时我们得到的新MaskTarget如下图所示

同前面一样,获取二值图像中对象的轮廓,然后把这个轮廓在TargetImg上绘制出来。

TargBoundry = bwboundaries(MaskTarget, 8);
figure, imshow(TargetImg), axis image
hold on
for k = 1:length(TargBoundry)
    boundary = TargBoundry{k};
    plot(boundary(:,2), boundary(:,1), ‘r‘, ‘LineWidth‘, 1)
end
title(‘Target Image with intended place for pasting Source‘);

结果如下图所示

根据文章所给出的算法我们知道,对于Mask轮廓的内部,我们是不考虑边界项的,此时计算梯度(G)的散度div,就是执行一个拉普拉斯算子,如下

div ( G( Source(x,y) ) )  = -4 f(x,y) + f(x-1,y) + f(x,y-1) + f(x+1,y) + f(x,y+1)

对SourceImg执行拉普拉斯算子,然后提取结果的R、G、B三个分量。

templt = [0 -1 0; -1 4 -1; 0 -1 0];
LaplacianSource = imfilter(double(SourceImg), templt, ‘replicate‘);
VR = LaplacianSource(:, :, 1);
VG = LaplacianSource(:, :, 2);
VB = LaplacianSource(:, :, 3);

然后根据Mask,把上述计算结果贴入TargetImg。

TargetImgR = double(TargetImg(:, :, 1));
TargetImgG = double(TargetImg(:, :, 2));
TargetImgB = double(TargetImg(:, :, 3));

TargetImgR(logical(MaskTarget(:))) = VR(SourceMask(:));
TargetImgG(logical(MaskTarget(:))) = VG(SourceMask(:));
TargetImgB(logical(MaskTarget(:))) = VB(SourceMask(:));

TargetImgNew = cat(3, TargetImgR, TargetImgG, TargetImgB);
figure, imagesc(uint8(TargetImgNew)), axis image, title(‘Target image with laplacian of source inserted‘);

结果如下图所示

然后,我们要来计算稀疏的临接矩阵。(这个地方需要参考前面给出的博文1)

如果简单从代码来分析,假设我们现在有这样一个mask

mask =

     0     0     0     0     0
     0     1(1)  1(2)  0     0
     0     1(3)  1(4)  1(5)  0
     0     0     1(6)  1(7)  0
     0     0     0     0     0

那么我们就要建立一个7×7的邻接矩阵,例如(1)是(2)邻接的,所以下面矩阵中[1,2]和[2,1]的位置就1。

>> neighbors = calcAdjancency( mask );
>> full(neighbors)

ans =

     0     1     1     0     0     0     0
     1     0     0     1     0     0     0
     1     0     0     1     0     0     0
     0     1     1     0     1     1     0
     0     0     0     1     0     0     1
     0     0     0     1     0     0     1
     0     0     0     0     1     1     0

下面我们给出函数calcAdjancency()的实现代码:

function neighbors = calcAdjancency( Mask )

[height, width]      = size(Mask);
[row_mask, col_mask] = find(Mask);

neighbors = sparse(length(row_mask), length(row_mask), 0);

%下标转索引
roi_idxs = sub2ind([height, width], row_mask, col_mask);

for k = 1:size(row_mask, 1),
    %4 邻接点
    connected_4 = [row_mask(k), col_mask(k)-1;%left
                   row_mask(k), col_mask(k)+1;%right
                   row_mask(k)-1, col_mask(k);%top
                   row_mask(k)+1, col_mask(k)];%bottom

    ind_neighbors = sub2ind([height, width], connected_4(:, 1), connected_4(:, 2));

    %二分查找函数 i = ismembc2(t, X):返回t在X中的位置,其中X必须为递增的的数值向量
    for neighbor_idx = 1: 4, %number of neighbors,
        adjacent_pixel_idx =  ismembc2(ind_neighbors(neighbor_idx), roi_idxs);
        if (adjacent_pixel_idx ~= 0)
            neighbors(k, adjacent_pixel_idx) = 1;
        end
    end
end
end

上述代码中第一个应该知道的地方是Matlab的二分查找函数ismembc2(),这一点我的注释已经比较明确,不再赘言。另一个地方是下标转索引的函数sub2ind()。下面这个例子说明了它的用法和意义:

% % e.g. M =
%
%     11(1) 12(5) 13    14
%     21    22(6) 23    24
%     31    32(7) 33    34
%     41    42    43    44

% roi_idxs = sub2ind(size(M), [1, 1, 2, 3], [2, 1, 2, 2])
%
% roi_idxs =
%
%      5     1     6     7

回到我们的主干程序,我们调用函数calcAdjancency()来计算MaskTarget 中Ω区域的邻接矩阵。

AdjacencyMat = calcAdjancency( MaskTarget );

然后我们调用PoissonSolver()函数分别对彩色图像的R、G、B三个分量解线性方程组。其核心就是使用迭代法求解型如Ax=b这样的大型稀疏线性方程组。Patrick Perez在原文中使用了高斯-塞德尔迭代法。与此类似,但我们直接调用Matlab中共轭梯度法函数来求解或许更为方便。参考Matlab中给出的帮助信息如下:
X = CGS(A,B) attempts to solve the system of linear equations A*X=B for X.
The N*N coefficient matrix A must be square and the right hand side column vector B must have length N.

ResultImgR = PoissonSolver(TargetImgR, MaskTarget, AdjacencyMat, TargBoundry);
ResultImgG = PoissonSolver(TargetImgG, MaskTarget, AdjacencyMat, TargBoundry);
ResultImgB = PoissonSolver(TargetImgB, MaskTarget, AdjacencyMat, TargBoundry);

最后我们将三个分量合到一起,并显示出最终的融合结果。

ResultImg = cat(3, ResultImgR, ResultImgG, ResultImgB);

figure;
imshow(uint8(ResultImg));

如下图所示:

更多示例与讨论

在Matlab中建立稀疏矩阵如果简单的用 zeros()或ones()函数的话,当图片稍微大一点(例如我在做下面的手眼融合示例时所用的图),就会产生 out of memory的问题。所以程序中要特别注意,使用sparse()等等专门用于建立稀疏矩阵的函数来避免超内存的问题。

全文完。

如果你是图像处理的通道中人,欢迎加入图像处理学习群(529549320)。

时间: 2024-07-31 10:12:09

Poisson image editing算法实现的Matlab代码解析的相关文章

SAR成像学习(四)距离方向成像matlab代码解析 2

如果发射信号是线性调频信号,上一次讲的距离成像算法流程(匹配滤波方法)依然可以用,但那个流程要求T x =4X 0 c >T p  .如果T x <T p  ,即幅宽相对较小的情况,上一讲中的流程会带来一个问题,解决这个问题的办法是pulse compression.本文将会讨论这个puse compression的原理和实现. 1 what is pulse compression 对于线性调频信号:p(t)=a(t)exp(jβt+jαt 2 ) ,信号持续时间为T p  ,瞬时频率为β+

SAR成像学习(三)距离方向成像matlab代码解析 1

本文将结合matlab代码讲解SAR距离向成像问题. 本文只研究距离向,且是正侧视情况. 文中以同一方位向坐标上四个目标点的成像为例,这四个目标的关系如下: 目标的相关信息: % 关于目标 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Xc=2.e3; % Range distance to center of target area X0=50; % target area in range is within [Xc-X0,Xc+X0] ntarg

SAR成像学习(五)方位向成像及matlab代码解析

1 overview overview of this blog 2 critical points 目标区域如下图: 正侧视情况 斜测视的情况 注意y轴的原点在合成孔径的中间,以下成像只研究这一个合成孔径的成像问题.请留意图中Y 0 ,Y c ,X c  等的位置.另外一个需要注意的定义是aspect angle of the radar with respect of the target area θ n (μ) : θ n (μ)=arctan(y n ?μx n  ) 它可以用来表示平

时空上下文视觉跟踪(STC)算法的解读与代码复现(转)

本文转载自zouxy09博客,原文地址为http://blog.csdn.net/zouxy09/article/details/16889905:在此,仅当笔记mark一下及给大家分享一下. 时空上下文视觉跟踪(STC)算法的解读与代码复现 [email protected] http://blog.csdn.net/zouxy09 本博文主要是关注一篇视觉跟踪的论文.这篇论文是Kaihua Zhang等人今年投稿到一个会议的文章,因为会议还没有出结果,所以作者还没有发布他的Matlab源代码

hog特征原理详解及matlab代码学习笔记

1.HOG特征: 方向梯度直方图(Histogram of Oriented Gradient, HOG)特征是一种在计算机视觉和图像处理中用来进行物体检测的特征描述子.它通过计算和统计图像局部区域的梯度方向直方图来构成特征.Hog特征结合SVM分类器已经被广泛应用于图像识别中,尤其在行人检测中获得了极大的成功.需要提醒的是,HOG+SVM进行行人检测的方法是法国研究人员Dalal在2005的CVPR上提出的,而如今虽然有很多行人检测算法不断提出,但基本都是以HOG+SVM的思路为主. (1)主

在C#的Web项目中调用Matlab代码的方法

为了毕设的图形检索方向的研究,本人需要在信科的师兄师姐们已经完成的C#界面中,调用现在研究的算法的Matlab代码,以便看到实验的效果.前段时间已经拖延了1个多月,一方面因为实习越来越多事情,时间减少了很多:但更重要在于C#调用Matlab的方法真心麻烦,C#的Web项目中进行这个操作貌似会碰到更多细节上的问题.而且总是很不稳定,操作系统.Matlab或VS的版本.遗漏一些文件或步骤都会造成失败!这个问题本人已经搞了很长时间,直至前几天,在同学的帮助下,自己再弄一遍,总算成功了!下面我及时把这个

粒子群优化算法PSO及matlab实现

算法学习自:MATLAB与机器学习教学视频 1.粒子群优化算法概述 粒子群优化(PSO, particle swarm optimization)算法是计算智能领域,除了蚁群算法,鱼群算法之外的一种群体智能的优化算法,该算法最早由Kennedy和Eberhart在1995年提出的,该算法源自对鸟类捕食问题的研究. • PSO算法首先在可行解空间中初始化一群粒子,每个粒子都代表极值优化问题的一个潜在最优解,用位置.速度和适应度值三项指标表示该粒子特征. • 粒子在解空间中运动,通过跟踪个体极值Pb

自动曝光修复算法 附完整C代码

众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了使感光器件获得合适的曝光量 AW自动白平衡(Automatic White Balance)白平衡的本质是使白色物体在任何光源下都显示白色 前面的文章也有提及过,在刚开始做图像算法的时候,我是先攻克的自动白平衡算法. 后来攻克自动曝光的时候,傻啦吧唧的,踩了不少坑. 我相信一定不止我一个,一开始的时

Latex 中插入 Matlab 代码

这篇文章将介绍如何在 Latex 排版过程中添加 Matlab 代码 功能效果 主要有如下排版功能: 语法高亮 自动添加边框 自动添加行号 先上图,大家感受一下效果 listings 包 首先确保你能使用使用 listings 包 简单快捷的使用方法如下 \usepackage{listings} \lstset{language=Matlab} \begin{lstlisting} % Plot function f(x) = 2*x^3 - x - 2 ezplot('2*x^3-x-2',