很多rpg或gal的cg为了节省空间,使用的都是类似于差分的的图像合成方式,将一组cg分成一张整体和数张微小的不同,在显示对应cg时只需将整体和不同的碎片进行混合,以达到动态的效果。而为了较好的合成效果,碎片的背景通常是绿幕或者透明。
openCV是一个轻量、高效、跨平台的图像处理库。为了模拟cg差分的合成效果,基于openCV写了一个简单的图像混合应用。
在项目中使用openCV
使用OpenCV库,需要在官网(https://opencv.org/)下载项目需要的库,选择对应的开发平台,这里我使用的是windows的4.6版本。
下载后获得一个exe文件,是一个自动的zip解压。在解压的文件夹进入opencv/build/java/中找到x64或x86,选择开发设备对应内核架构文件夹下的opencv_java460.dll。
在你的项目中使用openCV,将opencv-460.jar和opencv_java460.dll复制到一个文件夹下,这里我选择直接扔到项目目录,你也可以新建一个lib目录存储这些库。
打开IDEA,新建一个项目,点击File/Project Structure,选择Project Settings/Libraries,点击加号(+),在弹框中选择java,找到存放opencv库的目录,选择dll文件和jar文件。弹出选择模块(Choose Modules)点击ok。然后应用更改(Apply)并确定(Ok)。
写一个测试
package com.demo.coo;
import org.opencv.core.Core;
public class Main {
static{//启动类中需要加载链接库
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
public static void main(String[] args) {
// write your code here
System.out.println("open cv version->"+Core.VERSION);
}
}
如果版本能被顺利输出,说明导入成功。
先写两个方法,用于输出文件和日志。
public static void outDst(Mat dst,String pathName) {
if(new File(pathName).exists()) {
throw new RuntimeException("File is exist!");
}
Imgcodecs.imwrite(pathName,dst);
}
public static final void Dlog(String message){
System.out.println(message);
}
图像合并方法的核心代码
根据上文描述,提供了两种图像合成。第一种是绿幕合成,另一种是透明通道合成。
先看第一种
//Add two Images, second of them is clip, merge them into one picture
public static void ADD_C(String filePath1,String filePath2,String filePath3) {
Mat srcA = Imgcodecs.imread(filePath1);
Mat srcB = Imgcodecs.imread(filePath2);
if(srcA.empty() || srcB.empty()) {
// Dlog("File path is empty!");
if(debug) {
Dlog("File:"+filePath1+" founded: "+!srcA.empty());
Dlog("File:"+filePath2+" founded: "+!srcB.empty());
}
return;
}else {
if(debug) {
Dlog("Images have been loaded!");
}
}
Mat hsv = new Mat();
Mat mask = new Mat();
Imgproc.cvtColor(srcB, hsv, Imgproc.COLOR_BGR2HSV); //*生成掩膜
Core.inRange(hsv, new Scalar(35,43,46), new Scalar(77,255,255), mask); //*绿色覆盖
Core.bitwise_not(mask, mask); //反掩
Mat dst = new Mat();
Core.bitwise_and(srcB,srcB,dst,mask); //黑底
List<Mat> _lDstMat = new ArrayList<Mat>();
Core.split(dst , _lDstMat);
_lDstMat.add(mask); //Mask Not
Mat dstA = new Mat();
Mat dstB = new Mat();
Core.merge(_lDstMat, dstB); //*透明 祛黑
Imgproc.cvtColor(srcA, dstA, Imgproc.COLOR_BGR2BGRA); //通道扩容
Core.copyTo(dstB, dstA, mask);
outDst(dstA,filePath3); //*直接覆盖
}
filePath1 是原图的文件路径
filePath2 是背景为绿幕的碎片的图片路径
filePath3 是输出合成文件的图片路径
写一个测试看看效果。
在路径下查看,可以发现合成后的图片已被输出。
然后看第二个图片合成方法。
//用原图的透明通道作为掩膜的图像融合方式,效果更好一些。
public static void ADD_A(String filePath1,String filePath2,String filePath3) {
Mat srcA = Imgcodecs.imread(filePath1,Imgcodecs.IMREAD_UNCHANGED);
Mat srcB = Imgcodecs.imread(filePath2,Imgcodecs.IMREAD_UNCHANGED); //$1 一定要以IMREAD_UNCHANGED模式读入,否则会忽视透明通道
Mat cvtB = new Mat();
Imgproc.cvtColor(srcB, cvtB, Imgproc.COLOR_BGR2BGRA); //$2 将原图转换为带透明通道模式
List<Mat> _lB = new ArrayList<Mat>();
Core.split(cvtB,_lB); //$3 将转换后的图片按通道分割
if(_lB.size()!=4 || _lB.get(3).empty()) {
Dlog("Unable to find alpha channel.");
return;
}
Mat alpha = new Mat();
List<Mat> _lAlp = new ArrayList<Mat>();
_lAlp.add(_lB.get(3)); //$4 将透明通道单独提取出 并直接作为掩码
Core.merge(_lAlp, alpha);
Mat bin = new Mat();
Imgproc.threshold(alpha, bin, 0, 255, Imgproc.THRESH_BINARY);
Core.copyTo(srcB, srcA, bin);
outDst(srcA,filePath3);
}
filePath1 是原图的文件路径
filePath2 是背景为透明的碎片的图片路径
filePath3 是输出合成文件的图片路径
写一个测试看看效果。
在路径下查看,可以发现合成后的图片已被输出。
git地址:https://github.com/IdleFishEngineer/ImgAdd/tree/master
如果有疑问请在评论区讨论。
写作不易,请给个赞吧!