背景
用户通过笔在纸上手写了个人签名,通过拍照上传的方式将其笔迹设置为签名图片。
如果直接使用此图片(包括裁剪后的图片),则在签名的过程中会签名图案中不但有用户的笔迹,还有纸的颜色背景,效果堪忧。
解决目标
将用户的手写笔迹采集,并且背景色是透明的
解决思路
- 用户选择已经拍摄的笔迹照片
- 用户通过裁剪区域选择手写笔迹(尺寸为300*120)
- 程序将裁剪好的手写笔迹区域进行笔迹采集和透明化处理
-将图片中的黑色像素点1 保留,其他像素点设置为透明 (难点和重点:哪些色值可以被认定为笔迹、用户拍照时候的光线影响、用户手写纸的背景色)
-将图片保存为手写区域 - 将处理好的手写笔迹采集结果给用户预览
- 用户确认后将此手写笔迹处理后的图提交给服务端
Android模块的核心代码(图片裁剪不在此范围)
package cn.org.bjca.wcert.ywq.utils;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Environment;
import java.io.File;
import java.io.FileOutputStream;
/*************************************************************************************************
* <pre>
* @包路径: cn.org.bjca.wcert.ywq.utils
* @版权所有: 北京数字认证股份有限公司 (C) 2020
*
* @类描述: 手写笔迹处理
* @版本: V4.0.0
* @作者 daizhenhong
* @创建时间 2020/11/2 3:50 PM
</pre>
************************************************************************************************/
public class HandSignUtil {
final static String filePathHeader = "file://";
private final static int maxColorBlack = 145;
private final static int mColorDifMax = 14;
/**
* 将已经裁剪了的手写签名笔迹图片进行透明化处理
*
* @param context
* @param imagePath 图片的文件地址(前缀是file://)
* @return 经过背景透明化处理后的图片地址(需要添加file://协议头)
* @throws Exception
*/
public static String getHandSignByImagePath(Context context, String imagePath) throws Exception {
String path = imagePath.substring(filePathHeader.length());
Bitmap originBitmap = BitmapFactory.decodeFile(path);
Bitmap translateBitmap = translateBitmap(originBitmap);
String filePath = getSaveImagePath(context);
File saveFile = new File(filePath);
translateBitmap.compress(Bitmap.CompressFormat.PNG, 100, new FileOutputStream(saveFile));
return filePathHeader + saveFile.toString();
}
private static String getSaveImagePath(Context context) {
String cachePath = "";
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
|| !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
File fileParent = new File(cachePath);
if (!fileParent.exists()) {
fileParent.mkdirs();
}
File path = new File(cachePath, System.currentTimeMillis() + ".png");
return path.toString();
}
/**
* 将图片进行背景透明化处理
*
* @param originBitmap 原始图片的bitmap对象
* @return 背景透明化处理后的bitmap
*/
private static Bitmap translateBitmap(Bitmap originBitmap) {
Bitmap translateBitmap = originBitmap.copy(Bitmap.Config.ARGB_8888, true);
int nWidth = translateBitmap.getWidth();
int nHeight = translateBitmap.getHeight();
// 由于图片经过裁剪后是jpeg格式的,如果直接保存成png图片,会造成透明的背景色变成黑色(因为jpeg图片没有alpha通道)
Bitmap resultBitmap = Bitmap.createBitmap(nWidth, nHeight, Bitmap.Config.ARGB_8888);
for (int y = 0; y < nHeight; ++y)
for (int x = 0; x < nWidth; ++x) {
int nPixelColor = originBitmap.getPixel(x, y);
int newColor = getNewColor(nPixelColor);
resultBitmap.setPixel(x, y, newColor);
}
return resultBitmap;
}
/**
* 获取像素点需要变更的颜色
* 当像素点不是黑色的,则将其设置为透明
*
* @param nPixelColor
* @return
*/
private static int getNewColor(int nPixelColor) {
if (isBlackColor(nPixelColor)) {
return Color.argb(Color.alpha(nPixelColor), Color.red(nPixelColor), Color.green(nPixelColor), Color.blue(nPixelColor));
}
return Color.TRANSPARENT;
}
/**
* 判断是否是黑色笔迹
*
* @param color 颜色的int值
* @return true-认定为黑色笔迹
*/
private static boolean isBlackColor(int color) {
int r = Color.red(color);
int g = Color.green(color);
int b = Color.blue(color);
int colorMax = Math.max(Math.max(r, g), b);
int colorMin = Math.min(Math.min(r, g), b);
int dif = colorMax - colorMin;
return colorMax < maxColorBlack && dif <= mColorDifMax;
}
}
黑色像素点我们的计算方式:图像的像素有rgb(jpeg图片)或rgba组件(png图片),我们目前的设定是:rgb的最大一项不大于145 ,同时rgb之前的最大差距不能超过14 ↩︎