前言
本篇文章参考了以下文章:
https://blog.csdn.net/mars_xiaolei/article/details/78791133
素材
demo例子起码用到了几个素材,在这里下载:
logo_pc.png
005.jpg
转存失败
underlay.png
overlay.png
转存失败
test.png
代码
头文件:
#ifndef DEMO_IMAGEMIX_H
#define DEMO_IMAGEMIX_H
#include <iostream>
#include "opencv2/opencv.hpp"
#include "opencv2/core.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgcodecs.hpp"
#include <opencv2/imgproc.hpp>
#include <opencv2/imgproc/types_c.h>
#include <vector>
using namespace cv;
using namespace std;
class Demo_ImageMix
{
public:
Demo_ImageMix();
void mix1(std::string orgImg, std::string logoPic,double alphaValue);
void mix2(std::string orgImg, std::string logoPic,double alphaValue);
int MakeWatermarkTest(
std::string underlayPic,
std::string overlayPic,
std::string testPic);
int MakeWatermark(
std::string underlayPic,
std::string overlayPic
,double alphaValue
);
private:
void overlayImage(Mat* src, Mat* overlay, const Point& location);
};
#endif // DEMO_IMAGEMIX_H
cpp源文件:
#include "demo_imagemix.h"
/***
* 该class用于演示
* 1、两张图片叠加包含 透明 效果
* 2、底图上面添加png图片水印【水印背景透明】
**/
Demo_ImageMix::Demo_ImageMix()
{
}
/**
任意图片叠加
**/
void Demo_ImageMix::mix1(std::string orgImg, std::string logoPic,double alphaValue){
//两图像的权值
// double alphaValue = 0.3;
// alphaValue=0.5;
double betaValue = 1 - alphaValue;
/**
整体思路:
1、分别读取两张图片,然后根据图片尺寸创建一个新的能够容纳两张图片.
2、在底图上面获取logo对应区域
3、logo_img与没有东西的imgPart4Logo混合,得到有透明度的结果图片:dstImage
5、dstImage复制到imgPart4logo区域上面。--因为直接在底图上面操作的,这时候应该已经改变了。
6、显示结果图片 image_src
**/
Mat image_src=cv::imread(orgImg,1);
cv::Mat logo_img=cv::imread(logoPic,1);
Mat imgPart4logo=image_src(Rect(0,0,logo_img.cols,logo_img.rows));
Mat dstImage;
cv::addWeighted(logo_img, alphaValue, imgPart4logo,
betaValue, 0.0,
dstImage);
dstImage.copyTo(imgPart4logo);
cv::imshow("result",image_src);
waitKey(0);
return;
}
/**
任意图片叠加
https://blog.csdn.net/mars_xiaolei/article/details/78791133
*@param alphaValue logo图片的透明度
**/
void Demo_ImageMix::mix2(std::string orgImg, std::string logoPic,double alphaValue){
//两图像的权值
// double alphaValue = 0.3;
// alphaValue=0.5;
double betaValue = 1 - alphaValue;
/**
整体思路:
1、分别读取两张图片,然后根据图片尺寸创建一个新的能够容纳两张图片最大宽度与最大高度的mat对象--img_merge
2、分别在img_merge上面取src以及logo两个图片区域
3、image_src先复制到对应的img_merge区域上面
4、logo_img与没有东西的imgPart4Logo混合,得到有透明度的结果图片:dstImage
5、dstImage复制到imgPart4logo区域上面。
6、显示结果图片 img_merge
**/
Mat image_src=cv::imread(orgImg,1);
cv::Mat logo_img=cv::imread(logoPic,1);
Mat img_merge;
cv::Size size(MAX(image_src.cols,logo_img.cols),MAX(image_src.rows,logo_img.rows));
img_merge.create(size,CV_MAKETYPE(image_src.depth(),3));
img_merge=Scalar::all(0);
Mat imgPart4Src=img_merge(Rect(0,0,image_src.cols,image_src.rows));
Mat imgPart4logo=img_merge(Rect(0,0,logo_img.cols,logo_img.rows));
image_src.copyTo(imgPart4Src);
/***
****/
//logo_img.copyTo(imgPart4logo);
Mat dstImage;
cv::addWeighted(logo_img, alphaValue, imgPart4logo,
betaValue, 0.0,
dstImage);
dstImage.copyTo(imgPart4logo);
cv::imshow("result",img_merge);
waitKey(0);
return;
}
/***
光是看这个函数就像是在 合并两张图片的某个通道的值。。。
https://answers.opencv.org/question/73016/how-to-overlay-an-png-image-with-alpha-channel-to-another-png/
**/
void Demo_ImageMix::overlayImage(Mat* src, Mat* overlay, const Point& location)
{
for (int y = max(location.y, 0); y < src->rows; ++y)
{
int fY = y - location.y;
if (fY >= overlay->rows)
break;
for (int x = max(location.x, 0); x < src->cols; ++x)
{
int fX = x - location.x;
if (fX >= overlay->cols)
break;
double opacity = ((double)overlay->data[fY * overlay->step + fX * overlay->channels() + 3]) / 255;
for (int c = 0; opacity > 0 && c < src->channels(); ++c)
{
unsigned char overlayPx = overlay->data[fY * overlay->step + fX * overlay->channels() + c];
unsigned char srcPx = src->data[y * src->step + x * src->channels() + c];
src->data[y * src->step + src->channels() * x + c] = srcPx * (1. - opacity) + overlayPx * opacity;
}
}
}
}
/**
打水印
https://answers.opencv.org/question/73016/how-to-overlay-an-png-image-with-alpha-channel-to-another-png/
**/
int Demo_ImageMix::MakeWatermarkTest(
std::string underlayPic,
std::string overlayPic,
std::string testPic){
Mat underlay = imread(underlayPic,IMREAD_UNCHANGED);
Mat overlay = imread(overlayPic,IMREAD_UNCHANGED);
Mat test = imread(testPic,IMREAD_UNCHANGED);
if( underlay.empty() || overlay.empty() || test.empty() )
{
cout << "Could not read input image files " << endl;
return -1;
}
Mat rgba[4];
split(underlay,rgba);
imshow("alpha1.png",rgba[3]);
imwrite("alpha1.png",rgba[3]);
split(overlay,rgba);
imshow("alpha2.png",rgba[3]);
imwrite("alpha2.png",rgba[3]);
overlayImage( &underlay, &overlay, Point() );
overlayImage( &test, &underlay, Point(120,180) );
split(underlay,rgba);
imshow("alpha3.png",rgba[3]);
imwrite("alpha3.png",rgba[3]);
imshow("result1",underlay);
imwrite("result1.png",underlay);
imshow("result2",test);
imwrite("result2.png",test);
waitKey();
return 0;
}
/***
在原有基础上顺便加一个透明度。
**/
int Demo_ImageMix::MakeWatermark(
std::string underlayPic,
std::string overlayPic
,double alphaValue
){
Mat underlay = imread(underlayPic,IMREAD_UNCHANGED);
Mat overlay = imread(overlayPic,IMREAD_UNCHANGED);
double betaValue = 1 - alphaValue;
if( underlay.empty() || overlay.empty() )
{
cout << "Could not read input image files " << endl;
return -1;
}
//--处理透明度
Mat dstImage;
Mat img_merge;
cv::Size size(overlay.cols,overlay.rows);
//--!!!注意,png图片应该是四通道的,jpg应该是3通道的,如果这里设置为3通道,那么跟overlay图片运算时候就会出问题。
img_merge.create(size,CV_MAKETYPE(overlay.depth(),4));
img_merge=Scalar::all(0);
Mat imgPart4OverLay=img_merge(Rect(0,0,overlay.cols,overlay.rows));
cv::addWeighted(overlay, alphaValue, imgPart4OverLay,
betaValue, 0.0,
dstImage);
Mat rgba[4];
imshow("添加透明度之后的logo图片",dstImage);
overlayImage( &underlay, &dstImage, Point() );
imshow("打水印结果",underlay);
waitKey();
return 0;
}
入口函数 main.cpp
#include <iostream>
#include <fstream>
#include "demo_imagemix.h"
using namespace std;
static const std::string LogoPcPath="/home/too-white/temp/logo_pc.png";
static const std::string DemoImage005 = "/home/too-white/temp/005.jpg";
static const std::string DemoUnderLayImage = "/home/too-white/temp/underlay.png";
static const std::string DemoOverlayImage = "/home/too-white/temp/overlay.png";
static const std::string DemoTestLayImage = "/home/too-white/temp/test.png";
int checkFileEsits(std::string & filePath){
fstream file;
file.open(filePath.data(),
ios::in);
if(file.fail()){
file.close();
return 0;
}
file.close();
return 1;
}
int main(){
std::cout<<"开始执行"<<std::endl;
Demo_ImageMix imgMix;
//imgMix.mix1(DemoImage005,LogoPcPath,0.5);
//imgMix.MakeWatermarkTest(DemoUnderLayImage,LogoPcPath,DemoImage005);
imgMix.MakeWatermark(DemoImage005,LogoPcPath,0.5);
return 0;
}
测试结果
注意,测试的时候只执行了
mix1【与mix2的结果是一样的,只是写法不一样】,MakeWaterMarkTest与MakeWaterMark三个方法,下面是结果:
mix1:
两张图片叠加的效果是---真的是叠加啊。。。
MakeWaterMarkTest:
转存失败
看到这个结果,感觉就是合并其中一条通道实现效果的。
MakeWaterMark:
转存失败
可以设置透明度的水印,这个实用价值才大。。
我。。。老是提示转存失败,原素材可以到下面拿:
来源:CSDN
作者:码农下的天桥
链接:https://blog.csdn.net/cdnight/article/details/103962584