所以身份证别的关键:找到身份证号所在的区域、获取号码图片
图片无损压缩
#define DEFAULT_CARD_WIDTH 640
#define DEFAULT_CARD_HEIGHT 400
#define FIX_CARD_SIZE Size(DEFAULT_CARD_WIDTH,DEFAULT_CARD_HEIGHT)
Mat dst;
/*图片无损压缩*/
resize(src_img,src_img,FIX_CARD_SIZE);
图片灰度化,图片的降噪处理:去除噪色提高比对效率
cvtColor(src_img, dst, COLOR_BGR2GRAY);
灰度图片二值化:过滤掉颜色浅的区域,留下关键信息
threshold(dst, dst, 150, 255, 0);
图像膨胀:膨胀成一个块区域便于轮廓检测
Mat erodeElement = getStructuringElement(MORPH_RECT, Size(20, 10));
轮廓检测
findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
图片分割
rectangle(src_img, finalRect, Scalar(255, 255, 0));
提取身份证核心区域
Mat dst_img = src_img(finalRect);
cmake_minimum_required(VERSION 3.18.1)
project("msopencv")
#引入头文件
include_directories(${CMAKE_SOURCE_DIR}/include)
#编译源文件
file(GLOB all_file *.cpp *.c)
add_library(
msopencv
SHARED
${all_file})
add_library( lib_opencv SHARED IMPORTED)
set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libopencv_java3.so)
find_library(
log-lib
log)
target_link_libraries( # Specifies the target library.
msopencv
jnigraphics
android
lib_opencv
${log-lib})
/**
* 获取图片身份证号核心区域
*/
extern "C"
JNIEXPORT jobject JNICALL
Java_com_meishe_msopencv_ImageProcess_getIdNumber(JNIEnv *env, jclass clazz, jobject src,
jobject config) {
Mat src_img;
Mat dst_img;
//imshow("src_", src_img);
/*将bitmap转换为Mat型格式数据*/
Java_org_opencv_android_Utils_nBitmapToMat2(env, clazz, src, (jlong) &src_img, 0);
Mat dst;
/*无损压缩 640*400*/
resize(src_img, src_img,FIX_IDCARD_SIZE);
//imshow("dst", src_img);
/*灰度化*/
cvtColor(src_img, dst, COLOR_BGR2GRAY);
//imshow("gray", dst);
/*二值化*/
threshold(dst, dst, 150, 255, CV_THRESH_BINARY);
//imshow("threshold", dst);
/*膨胀*/
Mat erodeElement = getStructuringElement(MORPH_RECT, Size(20, 10));
erode(dst, dst, erodeElement);
//imshow("erode", dst);
/*轮廓检测 arraylist*/
vector< vector<Point> > contours;
vector<Rect> rects;
findContours(dst, contours, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));
for (int i = 0; i < contours.size(); i++)
{
Rect rect = boundingRect(contours.at(i));
//rectangle(dst, rect, Scalar(0, 0, 255)); // 在dst 图片上显示 rect 矩形
if (rect.width > rect.height * 9) {
rects.push_back(rect);
rectangle(dst, rect, Scalar(0,255,255));
dst_img = src_img(rect);
}
}
// imshow("轮廓检测", dst);
if (rects.size() == 1) {
Rect rect = rects.at(0);
dst_img = src_img(rect);
}else {
int lowPoint = 0;
Rect finalRect;
for (int i = 0; i < rects.size(); i++)
{
Rect rect = rects.at(i);
Point p = rect.tl();
if (rect.tl().y > lowPoint) {
lowPoint = rect.tl().y;
finalRect = rect;
}
}
rectangle(dst, finalRect, Scalar(255, 255, 0));
//imshow("contours", dst);
dst_img = src_img(finalRect);
}
/*身份证核心区域生成bitmap*/
jobject bitmap = createBitmap(env,dst_img,config);
src_img.release();
dst_img.release();
dst.release();
return bitmap;
}
拿到图片核心区域,返回bigtmap
mSubscribe = Observable.just(1).observeOn(Schedulers.io()).subscribe(new Consumer<Integer>() {
@Override
public void accept(Integer integer) throws Exception {
Log.e("lpf","----doInBackground---");
mTessBaseAPI = new TessBaseAPI();
try {
InputStream is = null;
is = getAssets().open(mLanguage + ".traineddata");
File file = new File(PathUtils.getTessDir()+File.separator + mLanguage + ".traineddata");
if (!file.exists()) {
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
byte[] buffer = new byte[2048];
int len;
while ((len = is.read(buffer)) != -1) {
fos.write(buffer, 0, len);
}
fos.close();
}
is.close();
PathUtils.getTessDir();
mTessBaseAPI.init(PathUtils.getRootDir(), mLanguage);
} catch (IOException e) {
e.printStackTrace();
Log.e("lpf","----copy error:"+e.getMessage());
}
}
});
mTessBaseAPI.setImage(mResultImage);
mTvCardNumberView.setText(mTessBaseAPI.getUTF8Text());
总结:这样就通过OpenCV将身份证号识别出来了,其中traineddata数据是训练的结果,文案训练请查看
身份证号训练教程
源码地址