一、拉普拉斯融合基本步骤

  1. 两幅图像L,R,以及二值掩模mask,给定金字塔层数level。

  2. 分别根据L,R构建其对应的拉普拉斯残差金字塔(层数为level),并保留高斯金字塔下采样最顶端的图像(尺寸最小的图像,第level+1层):

    拉普拉斯残差金字塔构建方法如下,以L图为例:

    (1) 对L进行高斯下采样得到downL,OpenCV中pyrDown()函数可以实现此功能。然后再对downL进行高斯上采样得到upL,OpenCV中pyrUp()函数可以实现此功能。

    (2) 计算原图L与upL之间的残差,得到一幅残差图lapL0。作为残差金字塔最低端的图像。

    (3) 对downL继续进行(1) (2)操作,不断计算残差图lapL1, lap2, lap3.....lapN。这样得到一系列残差图,即为拉普拉斯残差金字塔。

    (4)拉普拉斯 残差金字塔中一共有level幅图像。而我们需要保留第level+1层的高斯下采样图topL,以便后面使用。

  3. 二值掩模mask下采样构建高斯金字塔,同样利用pyrDown()实现,共有level+1层。

  4. 利用mask金字塔每一层的mask图,将L图和R图的拉普拉斯残差金字塔对应层的图像合并为一幅图像。这样得到合并后的拉普拉斯残差金字塔。同时利用最顶端的mask将步骤2中保留的topL和topR合并为topLR。

  5. 以topLR为金字塔最顶端的图像,利用pyrUp()函数对topLR进行高斯上采样,得到upTopLR,并将upTopLR与步骤4中合并后的残差金字塔对应层的图像相加,重建出该层的图像。

  6. 重复步骤5,直至重建出第0层,也就是金字塔最低端的图像,即blendImg。输出。

 二、代码

  拉普拉斯融合的OpenCV实现代码如下:

 #include <opencv2/opencv.hpp>
#include <iostream>
#include <string> using namespace std;
using namespace cv; /************************************************************************/
/* 说明:
*金字塔从下到上依次为 [0,1,...,level-1] 层
*blendMask 为图像的掩模
*maskGaussianPyramid为金字塔每一层的掩模
*resultLapPyr 存放每层金字塔中直接用左右两图Laplacian变换拼成的图像
*/
/************************************************************************/ class LaplacianBlending {
private:
Mat left;
Mat right;
Mat blendMask; //Laplacian Pyramids
vector<Mat> leftLapPyr, rightLapPyr, resultLapPyr;
Mat leftHighestLevel, rightHighestLevel, resultHighestLevel;
//mask为三通道方便矩阵相乘
vector<Mat> maskGaussianPyramid; int levels; void buildPyramids()
{
buildLaplacianPyramid(left, leftLapPyr, leftHighestLevel);
buildLaplacianPyramid(right, rightLapPyr, rightHighestLevel);
buildGaussianPyramid();
} void buildGaussianPyramid()
{
//金字塔内容为每一层的掩模
assert(leftLapPyr.size()>); maskGaussianPyramid.clear();
Mat currentImg;
cvtColor(blendMask, currentImg, CV_GRAY2BGR);
//保存mask金字塔的每一层图像
maskGaussianPyramid.push_back(currentImg); //0-level currentImg = blendMask;
for (int l = ; l<levels + ; l++) {
Mat _down;
if (leftLapPyr.size() > l)
pyrDown(currentImg, _down, leftLapPyr[l].size());
else
pyrDown(currentImg, _down, leftHighestLevel.size()); //lowest level Mat down;
cvtColor(_down, down, CV_GRAY2BGR);
//add color blend mask into mask Pyramid
maskGaussianPyramid.push_back(down);
currentImg = _down;
}
} void buildLaplacianPyramid(const Mat& img, vector<Mat>& lapPyr, Mat& HighestLevel)
{
lapPyr.clear();
Mat currentImg = img;
for (int l = ; l<levels; l++) {
Mat down, up;
pyrDown(currentImg, down);
pyrUp(down, up, currentImg.size());
Mat lap = currentImg - up;
lapPyr.push_back(lap);
currentImg = down;
}
currentImg.copyTo(HighestLevel);
} Mat reconstructImgFromLapPyramid()
{
//将左右laplacian图像拼成的resultLapPyr金字塔中每一层
//从上到下插值放大并与残差相加,即得blend图像结果
Mat currentImg = resultHighestLevel;
for (int l = levels - ; l >= ; l--)
{
Mat up;
pyrUp(currentImg, up, resultLapPyr[l].size());
currentImg = up + resultLapPyr[l];
}
return currentImg;
} void blendLapPyrs()
{
//获得每层金字塔中直接用左右两图Laplacian变换拼成的图像resultLapPyr
resultHighestLevel = leftHighestLevel.mul(maskGaussianPyramid.back()) +
rightHighestLevel.mul(Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid.back());
for (int l = ; l<levels; l++)
{
Mat A = leftLapPyr[l].mul(maskGaussianPyramid[l]);
Mat antiMask = Scalar(1.0, 1.0, 1.0) - maskGaussianPyramid[l];
Mat B = rightLapPyr[l].mul(antiMask);
Mat blendedLevel = A + B; resultLapPyr.push_back(blendedLevel);
}
} public:
LaplacianBlending(const Mat& _left, const Mat& _right, const Mat& _blendMask, int _levels) ://construct function, used in LaplacianBlending lb(l,r,m,4);
left(_left), right(_right), blendMask(_blendMask), levels(_levels)
{
assert(_left.size() == _right.size());
assert(_left.size() == _blendMask.size());
//创建拉普拉斯金字塔和高斯金字塔
buildPyramids();
//每层金字塔图像合并为一个
blendLapPyrs();
}; Mat blend()
{
//重建拉普拉斯金字塔
return reconstructImgFromLapPyramid();
}
}; Mat LaplacianBlend(const Mat &left, const Mat &right, const Mat &mask)
{
LaplacianBlending laplaceBlend(left, right, mask, );
return laplaceBlend.blend();
} int main() {
Mat img8UL = imread("data/apple.jpg");
Mat img8UR = imread("data/orange.jpg"); int imgH = img8UL.rows;
int imgW = img8UL.cols; imshow("left", img8UL);
imshow("right", img8UR); Mat img32fL, img32fR;
img8UL.convertTo(img32fL, CV_32F);
img8UR.convertTo(img32fR, CV_32F); //创建mask
Mat mask = Mat::zeros(imgH, imgW, CV_32FC1);
mask(Range::all(), Range(, mask.cols * 0.5)) = 1.0; Mat blendImg = LaplacianBlend(img32fL, img32fR, mask); blendImg.convertTo(blendImg, CV_8UC3);
imshow("blended", blendImg); waitKey();
return ;
}

 融合结果如下图:

  

金字塔层数level=5                                                金字塔层数level=10

  

  附上自己实现pyrDown和pyrUp写的拉普拉斯融合,仅供参考:

 #include <opencv2\opencv.hpp>
#include <iostream>
#include <string> using namespace std; //#define DEBUG void borderInterp(cv::Mat &_src, int radius)
{
int imgH = _src.rows;
int imgW = _src.cols;
float *pSrc = (float*)_src.data;
for (int i = radius; i < imgH-radius*; i++)
{
for (int j = ; j < ; j++)
{
int srcIdx = (i*imgW + j + ) * ;
int dstIdx = (i*imgW + j) * ;
pSrc[dstIdx] = pSrc[srcIdx];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
}
for (int j = imgW - radius; j < imgW; j++)
{
int srcIdx = (i*imgW + j - ) * ;
int dstIdx = (i*imgW + j) * ;
pSrc[dstIdx] = pSrc[srcIdx];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
pSrc[dstIdx + ] = pSrc[srcIdx + ]; }
}
for (int j = ; j < imgW; j++)
{
for (int i = ; i < ; i++)
{
int srcIdx = ((i + )*imgW + j) * ;
int dstIdx = (i*imgW + j) * ;
pSrc[dstIdx] = pSrc[srcIdx];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
}
for (int i = imgH - radius; i < imgH; i++)
{
int srcIdx = ((i - )*imgW + j) * ;
int dstIdx = (i*imgW + j) * ;
pSrc[dstIdx] = pSrc[srcIdx];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
pSrc[dstIdx + ] = pSrc[srcIdx + ];
}
}
} void myPyrDown(cv::Mat src, cv::Mat &dst, cv::Size dSize)
{
dSize = dSize.area() == ? cv::Size((src.cols + ) / , (src.rows + ) / ) : dSize; float scale = . / ; int imgH = src.rows;
int imgW = src.cols;
cv::Mat _src = cv::Mat::zeros(imgH + , imgW + , CV_32FC3);
int _imgH = _src.rows;
int _imgW = _src.cols;
src.copyTo(_src(cv::Rect(, , imgW, imgH)));
borderInterp(_src, ); //高斯卷积
cv::Mat gaussImg = cv::Mat::zeros(imgH, imgW, CV_32FC3);
cv::Mat tmpRowGaussImg = _src.clone();
float *pSrc = (float*)_src.data;
float *pRowGaussImg = (float*)tmpRowGaussImg.data;
//行卷积
for (int i = ; i < imgH+; i++)
{
for (int j = ; j < imgW+; j++)
{
float val[] = { };
int idx = i*_imgW + j;
for (int chan = ; chan < ; chan++)
{
val[chan] += pSrc[(idx - ) * + chan] + pSrc[(idx + ) * + chan]
+ * (pSrc[(idx - ) * + chan] + pSrc[(idx + ) * + chan])
+ * pSrc[idx * + chan];
}
pRowGaussImg[idx * ] = val[] * scale;
pRowGaussImg[idx * + ] = val[] * scale;
pRowGaussImg[idx * + ] = val[] * scale;
}
} float *pGaussImg = (float*)gaussImg.data;
//列卷积
for (int j = ; j < imgW; j++)
{
for (int i = ; i < imgH; i++)
{
int gi = i + ;
int gj = j + ;
float val[] = { };
int idx = gi*_imgW + gj;
for (int chan = ; chan < ; chan++)
{
val[chan] += pRowGaussImg[(idx-*_imgW) * + chan] + pRowGaussImg[(idx + *_imgW) * + chan]
+ * (pRowGaussImg[(idx - _imgW) * + chan] + pRowGaussImg[(idx + _imgW) * + chan])
+ * pRowGaussImg[idx * + chan];
} int id = (i*imgW + j) * ;
pGaussImg[id] = val[] * scale;
pGaussImg[id + ] = val[] * scale;
pGaussImg[id + ] = val[] * scale;
}
} int downH = dSize.height;
int downW = dSize.width; if (abs(downH * - imgH) > ) downH = imgH*0.5;
if (abs(downW * - imgW) > ) downW = imgW*0.5;
downH = (downH < ) ? : downH;
downW = (downW < ) ? : downW; dst = cv::Mat::zeros(downH, downW, CV_32FC3);
float *pDst = (float*)dst.data;
for (int i = ; i < imgH; i++)
{
for (int j = ; j < imgW; j++)
{
if (i % != || j % != ) continue;
int srcIdx = (i*imgW + j) * ;
int y = int((i+) * 0.5);
int x = int((j+) * 0.5);
y = (y >= downH) ? (downH - ) : y;
x = (x >= downW) ? (downW - ) : x;
int dstIdx = (y*downW + x) * ;
pDst[dstIdx] = pGaussImg[srcIdx];
pDst[dstIdx + ] = pGaussImg[srcIdx + ];
pDst[dstIdx + ] = pGaussImg[srcIdx + ];
}
}
} void myPyrUp(cv::Mat src, cv::Mat &dst, cv::Size dSize)
{
dSize = dSize.area() == ? cv::Size(src.cols * , src.rows * ) : dSize;
cv::Mat _src;
src.convertTo(_src, CV_32FC3); float scale = . / ; int imgH = src.rows;
int imgW = src.cols;
int upImgH = dSize.height;
int upImgW = dSize.width; if (abs(upImgH - imgH * ) > upImgH % ) upImgH = imgH*;
if (abs(upImgW - imgW * ) > upImgW % ) upImgW = imgW*; cv::Mat upImg = cv::Mat::zeros(upImgH, upImgW, CV_32FC3);
float *pSrc = (float*)_src.data;
float *pUpImg = (float*)upImg.data;
for (int i = ; i < upImgH; i++)
{
for (int j = ; j < upImgW; j++)
{
if (i % != || j % != ) continue;
int dstIdx = (i*upImgW + j)*;
int y = int((i+)*0.5);
int x = int((j+)*0.5);
y = (y >= imgH) ? (imgH - ) : y;
x = (x >= imgW) ? (imgW - ) : x;
int srcIdx = (y*imgW + x) * ; pUpImg[dstIdx] = pSrc[srcIdx];
pUpImg[dstIdx + ] = pSrc[srcIdx + ];
pUpImg[dstIdx + ] = pSrc[srcIdx + ];
}
} dst = cv::Mat::zeros(dSize, CV_32FC3);
cv::Mat _upImg = cv::Mat::zeros(upImgH + , upImgW + , CV_32FC3);
int _imgH = _upImg.rows;
int _imgW = _upImg.cols;
upImg.copyTo(_upImg(cv::Rect(, , upImgW, upImgH)));
borderInterp(_upImg, ); //高斯卷积
cv::Mat tempRowGaussImg = _upImg.clone();
float *pUpData = (float*)_upImg.data;
float *pRowGaussImg = (float*)tempRowGaussImg.data;
//行卷积
for (int i = ; i < upImgH + ; i++)
{
for (int j = ; j < upImgW + ; j++)
{
float val[] = { };
int idx = i*_imgW + j;
for (int chan = ; chan < ; chan++)
{
val[chan] += pUpData[(idx - ) * + chan] + pUpData[(idx + ) * + chan]
+ * (pUpData[(idx - ) * + chan] + pUpData[(idx + ) * + chan])
+ * pUpData[idx * + chan];
} pRowGaussImg[idx * ] = val[] * scale;
pRowGaussImg[idx * + ] = val[] * scale;
pRowGaussImg[idx * + ] = val[] * scale;
}
} //列卷积
float *pDst = (float*)dst.data;
for (int j = ; j < upImgW; j++)
{
for (int i = ; i < upImgH; i++)
{
int gi = i + ;
int gj = j + ;
float val[] = { };
int idx = gi*_imgW + gj;
for (int chan = ; chan < ; chan++)
{
val[chan] += pRowGaussImg[(idx - * _imgW) * + chan] + pRowGaussImg[(idx + * _imgW) * + chan]
+ * (pRowGaussImg[(idx - _imgW) * + chan] + pRowGaussImg[(idx + _imgW) * + chan])
+ * pRowGaussImg[idx * + chan];
} int id = (i*upImgW + j) * ;
pDst[id] = val[] * scale;
pDst[id + ] = val[] * scale;
pDst[id + ] = val[] * scale;
}
}
} void buildLaplacianPyramid(cv::Mat srcImg, vector<cv::Mat> &pyramidImgs, cv::Mat &topLevelImg, int levels)
{
cv::Mat currentImg = srcImg;
for (int k = ; k < levels; k++)
{
cv::Mat downImg, upImg, lpImg; #ifdef DEBUG
cv::pyrDown(currentImg, downImg);
cv::pyrUp(downImg, upImg, currentImg.size());
#else
myPyrDown(currentImg, downImg, cv::Size());
myPyrUp(downImg, upImg, currentImg.size());
#endif lpImg = currentImg - upImg;
pyramidImgs.push_back(lpImg);
currentImg = downImg;
}
currentImg.copyTo(topLevelImg);
} void buildGaussPyramid(cv::Mat mask, vector<cv::Mat> &maskGaussPyramidImgs, vector<cv::Mat> pyramidImgs,cv::Mat topLevelImg, int levels)
{
cv::Mat currentMask;
//mask转3通道
if (mask.channels() == )
{
cv::cvtColor(mask, currentMask, CV_GRAY2BGR);
}
else if(mask.channels()==)
{
currentMask = mask;
} maskGaussPyramidImgs.push_back(currentMask);
for (int k = ; k < levels+; k++)
{
cv::Mat downMask;
if (k < levels)
{
#ifdef DEBUG
cv::pyrDown(currentMask, downMask, pyramidImgs[k].size());
#else
myPyrDown(currentMask, downMask, pyramidImgs[k].size());
#endif
}
else
{
#ifdef DEBUG
cv::pyrDown(currentMask, downMask, topLevelImg.size());
#else
myPyrDown(currentMask, downMask, topLevelImg.size());
#endif
} maskGaussPyramidImgs.push_back(downMask);
currentMask = downMask;
}
} void buildResultPyramid(vector<cv::Mat> leftPyramidImgs, vector<cv::Mat> rightPyramidImgs, vector<cv::Mat> maskPyramids, vector<cv::Mat> &resultPyramidImgs, int levels)
{ for (int k = ; k < levels; k++)
{
cv::Mat left = leftPyramidImgs[k].mul(maskPyramids[k]);
cv::Mat right = rightPyramidImgs[k].mul(cv::Scalar(1.0,1.0,1.0) - maskPyramids[k]);
cv::Mat result = left + right;
resultPyramidImgs.push_back(result);
} } void reconstruct(vector<cv::Mat> lpPyramidImgs, cv::Mat blendTopLevelImg, cv::Mat &blendImg, int levels)
{
cv::Mat currentImg = blendTopLevelImg;
for (int k = levels - ; k >= ; k--)
{
cv::Mat upImg;
#ifdef DEBUG
cv::pyrUp(currentImg, upImg, lpPyramidImgs[k].size());
#else
myPyrUp(currentImg, upImg, lpPyramidImgs[k].size());
#endif
currentImg = upImg + lpPyramidImgs[k];
}
currentImg.copyTo(blendImg);
} cv::Mat laplacianBlending(cv::Mat leftImg, cv::Mat rightImg, cv::Mat mask)
{
cv::Mat leftImg32f, rightImg32f, mask32f;
leftImg.convertTo(leftImg32f, CV_32FC1);
rightImg.convertTo(rightImg32f, CV_32FC1);
mask.convertTo(mask32f, CV_32FC1); vector<cv::Mat> leftLpPyramidImgs, rightLpPyramidImgs, resultLpPyramidImgs, gaussPyramidMaskImgs;
cv::Mat leftTopLevelImg, rightTopLevelImg;
int levels =;
//拉普拉斯金字塔
buildLaplacianPyramid(leftImg32f, leftLpPyramidImgs, leftTopLevelImg, levels);
buildLaplacianPyramid(rightImg32f, rightLpPyramidImgs, rightTopLevelImg, levels);
//mask创建gauss金字塔
buildGaussPyramid(mask32f, gaussPyramidMaskImgs, leftLpPyramidImgs, leftTopLevelImg, levels);
//结合左右两图的laplacian残差图
buildResultPyramid(leftLpPyramidImgs, rightLpPyramidImgs, gaussPyramidMaskImgs, resultLpPyramidImgs, levels);
//
cv::Mat blendImg = cv::Mat::zeros(leftImg.size(), CV_32FC3); cv::Mat blendTopLevelImg = leftTopLevelImg.mul(gaussPyramidMaskImgs[levels]) + rightTopLevelImg.mul(cv::Scalar(1.0, 1.0, 1.0) - gaussPyramidMaskImgs[levels]);
reconstruct(resultLpPyramidImgs, blendTopLevelImg, blendImg, levels); blendImg.convertTo(blendImg, CV_8UC3);
return blendImg;
} void main()
{
cv::Mat appleImg = cv::imread("data/apple.jpg");
cv::Mat pearImg = cv::imread("data/orange.jpg"); int imgH = appleImg.rows;
int imgW = appleImg.cols;
cv::Mat mask = cv::Mat::zeros(imgH, imgW, CV_32FC1);
mask(cv::Range::all(), cv::Range(, imgW*0.5)) = 1.0;
cv::Mat blendImg = laplacianBlending(appleImg, pearImg, mask);
cv::namedWindow("blendImg", );
cv::imshow("blendImg", blendImg);
cv::imwrite("data/blendImg.png", blendImg);
cv::waitKey();
}

图像融合之拉普拉斯融合(laplacian blending)的更多相关文章

  1. 图像融合之泊松融合(Possion Matting)

    前面有介绍拉普拉斯融合,今天说下OpenCV泊松融合使用.顺便提一下,泊松是拉普拉斯的学生. 泊松融合的原理请参考这篇博文https://blog.csdn.net/u011534057/articl ...

  2. 拉普拉斯矩阵(Laplacian Matrix) 及半正定性证明

    摘自 https://blog.csdn.net/beiyangdashu/article/details/49300479 和 https://en.wikipedia.org/wiki/Lapla ...

  3. 图像sift配准后融合

    image rectification 图像校正 在配准时,先找到特征点,找到特征点后剔除伪匹配点. 然后针对两幅图像做几何矫正(一般通过估计出来的仿射矩阵完成). 这部完成后,图像可以匹配了,但是两 ...

  4. SC3聚类 | 拉普拉斯矩阵 | Laplacian matrix | 图论 | R代码

    Laplacian和PCA貌似是同一种性质的方法,坐标系变换.只是拉普拉斯属于图论的范畴,术语更加专业了. 要看就把一篇文章看完整,再看其中有什么值得借鉴的,总结归纳理解后的东西才是属于你的. 问题: ...

  5. 利用OpenCV实现图像拼接Stitching模块讲解

    https://zhuanlan.zhihu.com/p/71777362 1.1 图像拼接基本步骤 图像拼接的完整流程如上所示,首先对输入图像提取鲁棒的特征点,并根据特征描述子完成特征点的匹配,然后 ...

  6. SSE图像算法优化系列二十九:基础的拉普拉斯金字塔融合用于改善图像增强中易出现的过增强问题(一)

    拉普拉斯金字塔融合是多图融合相关算法里最简单和最容易实现的一种,我们在看网络上大部分的文章都是在拿那个苹果和橙子融合在一起,变成一个果橙的效果作为例子说明.在这方面确实融合的比较好.但是本文我们主要讲 ...

  7. 基于均值坐标(Mean-Value Coordinates)的图像融合算法的具体实现

    目录 1. 概述 2. 实现 2.1. 准备 2.2. 核心 2.2.1. 均值坐标(Mean-Value Coordinates) 2.2.2. ROI边界栅格化 2.2.3. 核心实现 2.2.4 ...

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

    目录 1. 概述 2. 实现 2.1. 原理 2.2. 核心代码 2.3. 第二种优化 3. 结果 1. 概述 我在之前的文章<基于均值坐标(Mean-Value Coordinates)的图像 ...

  9. DirectX基础学习系列5 融合技术

    7.1融合方程 1概念 融合技术将当前光栅化像素的颜色与以前已光栅化并处于同一个位置的像素颜色进行合成,即将当前要进行光栅化的三角形单元与已写入后台的像素进行融合 2需要遵循的原则: (1)先绘制不需 ...

随机推荐

  1. Golang学习--开篇

    最近开始接收一个新项目,是使用Golang写的,需要重新捡起Golang来,于是就有了这个系列博客. Golang的环境配置,我就不说了,让我们直接开始. Golang官网:https://golan ...

  2. LeetCode & Q38-Count and Say-Easy

    String Description: The count-and-say sequence is the sequence of integers with the first five terms ...

  3. linux命令行传递参数定期执行PHP文件

    最近在做一个项目,需要在linux下传递参数定期执行PHP文件,网上查询资料,确实有相关资料,现整理如下: 1.linux执行PHP文件 #{PHP安装bin路径} {PHP文件路径} {需要参数1 ...

  4. GIT入门笔记(20)- 使用eclipse 基于 git 开发过程梳理

    一.创建本地分支 1.下载/更新 本地 主干 如果本地还没有 本地主干,下载:git clone 如果本地已有了 本地主干,更新:git pull 工程右键菜单:team -> pull 2.基 ...

  5. 大数据学习总结(6)what is our foucus

    1.搜索业务 2.画像业务 3.关系图谱 借助es构建搜索.画像和关系图谱

  6. python入门(2)python的安装

    python入门(2)python的安装 Python是跨平台的,可以运行在Windows.Mac和各种Linux/Unix系统上. 2.x还是3.x Python有两个版本,一个是2.x版,一个是3 ...

  7. OAuth2.0学习(1-6)授权方式3-密码模式(Resource Owner Password Credentials Grant)

    授权方式3-密码模式(Resource Owner Password Credentials Grant) 密码模式(Resource Owner Password Credentials Grant ...

  8. OAuth2.0学习(1-1)OAuth2.0是什么?

    目前很多开放平台如新浪微博开放平台都在使用提供开放API接口供开发者使用,随之带来了第三方应用要到开放平台进行授权的问题 OAuth就是用于为第三方应用授权访问用户的资源应用的. 目前有OAuth1. ...

  9. Java-Maven(七):Eclipse中Maven依赖、聚合、继承特性

    之前通过学习了解,maven集成到eclipse中的如何创建项目,以及maven命令插件在eclipse中安装后的用法.那么接下来我们将会学习一些maven在项目中的一些特性,及如何使用. Maven ...

  10. Spark RDDs vs DataFrames vs SparkSQL

    简介 Spark的 RDD.DataFrame 和 SparkSQL的性能比较. 2方面的比较 单条记录的随机查找 aggregation聚合并且sorting后输出 使用以下Spark的三种方式来解 ...