最近遇到一个需求,在Android设备上采集到的红外热图,由于像素比较低(60x80),无法看清楚拍摄的内容,经过讨论,决定将红外热图覆盖在可见光图上进行展示。但是可见光相机和红外相机位置存在偏差,实际上拍摄的两张图片不能直接进行覆盖。所以在覆盖之前,需要手动两张图片进行校准。这就是本文所要展示的内容。
这个需求最大的难点就是,如何得到两张图片重叠的部分。
1.在屏幕上绘制可见光照片,自适应屏幕。
2.绘制红外热图。红外图在绘制的时候设置一个透明度,可以透过红外图隐约看到可见光图,方便移动重叠。
3.缩放和移动红外热图,让红外图和可见光图重叠。实际上可见光图比红外图大的多,所以必然有有一部分可见光图片没有被红外图覆盖。
4.切割两张图片重叠的部分,生成新的图片。
第一步,很简单,没什么好说的第四步,这是本篇的重点和难点。如何确定和切割两张图片的重叠部分。
以可将光图为参照,计算红外热图的相对位置,然后进行切割,示意图如下。
(x1,y1)为可见光图绘制原点,(x2,y2)为红外热图绘制原点。两张图坐标原点的差值为diffX,diffY。可见光图的宽高方便时bigW、bigH。
红外热图相对于可见图的在X轴上的相对位为diffX/bigW,Y轴的相对位置为diffY/bigH。这里红外热图的原点可以在红外热图之外,也就是diffX和diffY可以为负数,但是两张图必须有重叠部分。
android手机的坐标原点在屏幕左上角,x轴往右越大,y轴往下越大。所以,重叠部分坐标应该是:x轴方向右侧两点坐标取大值,左侧取小值。y轴方向上边两点坐标取大值,下边两点坐标取小值。以这个为计算原理,计算重叠部分坐标。
计算得到重叠部分
在计算矩形坐标时,通常会用到Rect类,以方便记录矩形的四点坐标。这个类只有四个参数,如:new Rect(a, b, c, d)。
a:AC点的x坐标
b:AB点的y坐标
c:BD点的x坐标
d:CD点的y坐标
所以A(a, b),B(c, b),C(a, d),D(c, d);
已Rect存储两张图片四点的坐标,以可见光图的左上角为(0,0)点,计算出重叠部分的坐标。
代码如下
/**
* 融合图裁剪
* @param big 底图
* @param small 小图
* @param xInRatio 小图在底图中的横坐标(相对于底图宽的比例)
* @param yInRatio 小图在底图中的纵坐标(相对于底图高的比例)
* @param alpha 小图的透明度 0~255
* @return
*/
public static Bitmap mergeWithCrop(Bitmap big,Bitmap small,float xInRatio,float yInRatio,int alpha){
int smallW = small.getWidth();
int smallH = small.getHeight();
int bigW = big.getWidth();
int bigH = big.getHeight();
int smallX = (int)(bigW*xInRatio);
int smallY = (int)(bigH*yInRatio);
int bigX = 0;
int bigY = 0;
Rect sRect = new Rect(smallX, smallY, smallX+smallW, smallY+smallH);
Rect bRect = new Rect(bigX, bigY, bigX+bigW, bigY+bigH);
Rect overlap = intersect(sRect, bRect);
int overlayW = overlap.right - overlap.left;
int overlayH = overlap.bottom - overlap.top;
//防止红外图完全覆盖可见光图时报错。
if (overlap.left == 0 && overlap.top == 0){
overlayW--;
overlayH--;
}
Bitmap newBmp = Bitmap.createBitmap(big,overlap.left,overlap.top,overlayW,overlayH);
big.recycle();
Canvas canvas = new Canvas(newBmp);
Paint paint = new Paint();
paint.setAlpha(alpha);
canvas.drawBitmap(small, smallX >= 0 ? 0 : smallX, smallY >= 0 ? 0 : smallY, paint);
small.recycle();
return newBmp;
}
/**
* 求矩形的重叠区域
* @param r1
* @param r2
* @return
*/
public static Rect intersect(Rect r1,Rect r2){
Rect r = new Rect();
r.left = Math.max(r1.left, r2.left);
r.top = Math.max(r1.top, r2.top);
r.right = Math.min(r1.right, r2.right);
r.bottom = Math.min(r1.bottom, r2.bottom);
if (r.left > r.right || r.top > r.bottom)
r = null;
return r;
}
另一方面,Bitmap在绘制图片时,可以传入四个参数,参数和Rect参数一样,可以只绘制矩形区域大小。
Bitmap newBmp = Bitmap.createBitmap(big,overlap.left,overlap.top,overlayW,overlayH);
根据范围,先绘制可见光图的重叠部分,再绘制红外热图的重叠部分,红外热图设置一下透明度,最后得到融合后的图。
Demo