前言
本篇文章參考了以下文章:
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:
轉存失敗
可以設置透明度的水印,這個實用價值才大。。
我。。。老是提示轉存失敗,原素材可以到下面拿: