图像增强算法的安卓移植

一、图像增强代码的 C++ 实现

在博客 一种基于Opencv文档图像增强算法的实现 提到了一种基于 C++ OpenCV 的图像增强算法, 并添加了辅助增强算法使效果更加明显.

1. PC 端实现代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

//Gamma校正 fGamaa=0.45是常用值
void GammaCorrection(Mat& src, Mat& dst, float fGamma)
{
CV_Assert(src.data);
// accept only char type matrices
CV_Assert(src.depth() != sizeof(uchar));
// build look up table
unsigned char lut[256];
for (int i = 0; i < 256; i++)
{
lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
}

dst = src.clone();
const int channels = dst.channels();
switch (channels)
{
case 1:
{

MatIterator_<uchar> it, end;
for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
*it = lut[(*it)];

break;
}
case 3:
{

MatIterator_<Vec3b> it, end;
for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
{
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
}
}

int main(int argc, char** argv)
{
string addr = "C:\\Users\\64975\\Desktop\\test1.jpg";
Mat image = imread(addr);
//划分算法
//如果混合色与基色相同则结果色为白色
//如混合色为白色则结果色为基色不变
//如混合色为黑色则结果色为白色
Mat src = image.clone();
src.convertTo(src, CV_32FC3, 1.0 / 255);
Mat gauss;
Mat dst = src.clone();
GaussianBlur(src, gauss, Size(101, 101), 0);
dst = src / gauss;
dst.convertTo(dst, CV_8UC3, 255);
//gamma变换
Mat ss;
Mat matGamma;
ss = dst.clone();
GammaCorrection(ss, matGamma, 1.5);
imwrite("C:\\Users\\64975\\Desktop\\test1_out.jpg", matGamma);
return 0;
}

2. 图片处理前后对比

上述图片中, 左侧为未处理图片, 右侧为处理后的图片.

可以明显发现, 由于光线所造成的阴影部分以及背景颜色在处理后都可以忽略不计, 并且图片中的文字质量仍保证在可辨识的状态.

3. 对处理前后图片的模型识别结果

(注: 图片中文字过多使得检测模型只能检测到部分, 故测试采用文字数较少图片进行测试)

效果不是很明显, 还需要更多的测试来对其图像增强效果进行判断. 为了简便步骤, 这步工作在移植该算法到安卓端后进行.

二、图像增强算法移植安卓

1. 移植过程

Step 1 : 在 xxx/src/main/cpp 文件夹下新建两个文件 image_enhancement.cpp 和 image_enhancement.h

Step 2 : 编写 image_enhancement.cpp 和 image_enhancement.h 文件, 这里为移植的代码.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//image_enhancement.h

#ifndef ANDROID_DEMO_IMAGE_ENHANCEMENT_H
#define ANDROID_DEMO_IMAGE_ENHANCEMENT_H

#include <opencv2/opencv.hpp>

#include <string>
using namespace std;

using namespace cv;
void GammaCorrection(Mat &src,Mat &dst, float fGamma);
std::string ImageEnhance(std::string addr);

#endif //ANDROID_DEMO_IMAGE_ENHANCEMENT_H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//image_enhancement.cpp

#include <opencv2/opencv.hpp>
#include <string>

using namespace std;
using namespace cv;
void GammaCorrection(Mat& src, Mat& dst, float fGamma)
{
CV_Assert(src.data);
// accept only char type matrices
CV_Assert(src.depth() != sizeof(uchar));
// build look up table
unsigned char lut[256];
for (int i = 0; i < 256; i++)
{
lut[i] = saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);
}

dst = src.clone();
const int channels = dst.channels();
switch (channels)
{
case 1:
{

MatIterator_<uchar> it, end;
for (it = dst.begin<uchar>(), end = dst.end<uchar>(); it != end; it++)
*it = lut[(*it)];

break;
}
case 3:
{

MatIterator_<Vec3b> it, end;
for (it = dst.begin<Vec3b>(), end = dst.end<Vec3b>(); it != end; it++)
{
(*it)[0] = lut[((*it)[0])];
(*it)[1] = lut[((*it)[1])];
(*it)[2] = lut[((*it)[2])];
}
break;
}
}
}

std::string ImageEnhance(std::string addr) {
Mat image = imread(addr);
Mat src = image.clone();
src.convertTo(src, CV_32FC3, 1.0 / 255);
Mat gauss;
Mat dst = src.clone();
GaussianBlur(src, gauss, Size(101, 101), 0);
dst = src / gauss;
dst.convertTo(dst, CV_8UC3, 255);
//gamma变换
Mat ss;
Mat matGamma;
ss = dst.clone();
GammaCorrection(ss, matGamma, 1.5);
return addr;
}

Step 3 : 在 xxx/src/main/cpp/native.cpp 文件末尾添加函数

为了满足 JNI 的要求, 这里的函数命名都需要严格要求

1
2
3
4
5
6
7
extern "C" JNIEXPORT jstring JNICALL
Java_com_baidu_paddle_lite_demo_ocr_OCRPredictorNative_enhancement(JNIEnv *env, jobject thiz,jstring image_path) {
std::string image_path1 = jstring_to_cpp_string(env, image_path);
std::string image_out_path1 = ImageEnhance(image_path1);
jstring j_string = cpp_string_to_jstring(env, image_out_path1);
return j_string;
}

Step 4 : 在 xxx/src/main/java/xxxxx/OCRPredictorNative.java 文件中添加函数, 作为 Java 调用 C++ 的接口.

1
protected native String enhancement(String imagePath);

Step 5 : 根据需要调用函数处理图像.

2. 编译提示

观察编译结果可能会发现并没有生成有关 image_enhancement 的 .o 文件. 这时就会出现链接文件时找不到 ImageEnhance() 函数.

需要删除 根目录/app/.cxx 和 根目录/app/build 两个文件夹之后再 Make Build 该项目.

三、存在问题

  • 目前图像增强代码移植安卓失败, 会出现 Assertion failed locateROI 之类的错误. 暂时还没有解决办法.

  • 在初期测试时, 对于文字占比较大的图片虽然识别率不高, 但是能够全部检测到. 但是在最近测试时发现, 模型对于此类图片的检测率只能达到 50 % 甚至更少. 无论使用其他开源模型还是使用官方网页模型以及官方在 PC 上的模型测试都是如此.