feat: 增加ocr文字识别功能

This commit is contained in:
2023-03-23 15:48:24 +08:00
parent 506299b3a3
commit 12b870ea79
53 changed files with 9603 additions and 166 deletions

View File

@@ -7,6 +7,7 @@ import com.kongzue.dialog.interfaces.OnDialogButtonClickListener;
import com.kongzue.dialog.util.BaseDialog;
import com.kongzue.dialog.util.DialogSettings;
import com.kongzue.dialog.v3.MessageDialog;
import com.navinfo.ocr.OCRManager;
import com.navinfo.outdoor.api.Constant;
import com.navinfo.outdoor.api.UserApplication;
import com.navinfo.outdoor.base.BaseActivity;
@@ -115,7 +116,8 @@ public class HomeActivity extends BaseActivity {
// 注册位置更新的lifeCycle
getLifecycle().addObserver(LocationLifeCycle.getInstance());
// 初始化图像识别组件
OCRManager.Companion.getInstance().init(HomeActivity.this);
} else {
finish();
}

View File

@@ -29,15 +29,20 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import com.elvishew.xlog.XLog;
import com.github.lazylibrary.util.FileUtils;
import com.kongzue.dialog.interfaces.OnDialogButtonClickListener;
import com.kongzue.dialog.util.BaseDialog;
import com.kongzue.dialog.util.DialogSettings;
import com.kongzue.dialog.v3.MessageDialog;
import com.navinfo.ocr.OCRManager;
import com.navinfo.ocr.model.OcrViewResultModel;
import com.navinfo.outdoor.R;
import com.navinfo.outdoor.api.Constant;
import com.navinfo.outdoor.api.UserApplication;
import com.navinfo.outdoor.base.BaseActivity;
import com.navinfo.outdoor.bean.LocationRecorder;
import com.navinfo.outdoor.util.BitmapUtil;
import com.navinfo.outdoor.util.GPSUtils;
import com.navinfo.outdoor.util.Geohash;
import com.navinfo.outdoor.util.GeometryTools;
@@ -51,6 +56,7 @@ import com.navinfo.outdoor.util.ToastUtils;
import com.otaliastudios.cameraview.CameraListener;
import com.otaliastudios.cameraview.CameraLogger;
import com.otaliastudios.cameraview.CameraOptions;
import com.otaliastudios.cameraview.CameraUtils;
import com.otaliastudios.cameraview.CameraView;
import com.otaliastudios.cameraview.FileCallback;
import com.otaliastudios.cameraview.PictureResult;
@@ -82,13 +88,16 @@ import org.locationtech.jts.geom.MultiLineString;
import com.wanghong.webpnative.WebPNative;
import org.greenrobot.eventbus.EventBus;
import org.reactivestreams.Subscription;
import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Timer;
import java.util.TimerTask;
@@ -96,12 +105,21 @@ import java.util.concurrent.TimeUnit;
import static com.tencent.tencentmap.mapsdk.maps.model.MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE;
import io.reactivex.BackpressureStrategy;
import io.reactivex.Flowable;
import io.reactivex.FlowableEmitter;
import io.reactivex.FlowableOnSubscribe;
import io.reactivex.FlowableSubscriber;
import io.reactivex.Notification;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.BiFunction;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
@@ -118,12 +136,14 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
private String finalVideoPath, geoWkt, detail; // 摄像后最终保存的文件名
private ViewGroup layerChange; // 切换地图和相机的父控件
private CheckBox capturePicture; //拍照
private File paperFile, logFile;
private File paperFile, logFile, checkFile/*辅助检查文件*/;
private ImageView ivZoomAdd, ivZoomDel, ivLocation, ivPicRoadImage, ivPicVideoImage, imageView;
private View layerMapController;
private MyLocation oldCurrentLocation = null;
private Timer timer;
private TimerTask timerTask;
// private Timer timer;
// private TimerTask timerTask;
private Disposable disposable; // RxJava循环拍照的控制对象
private FlowableEmitter<PictureResult> pictureEmitter; // RxJava控制照片生成
private SystemTTS systemTTS;
private StringBuilder picturesBuilder;
private LatLng startLatLine, endLatLine;
@@ -145,10 +165,7 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 0x101) {
System.out.println("收到拍照按钮请求");
camera.takePictureSnapshot();
} else if (msg.what == 0x102) {
if (msg.what == 0x102) {
if (imageView != null) {
imageView.setEnabled(true);
}
@@ -157,7 +174,7 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
capturePicture.setText("开始采集");
}
capturePicture.setChecked(false);
stopTimer();
stopTakePhoto();
} else if (msg.what == 0x104) {
tvConvert.setText("转换成功:"+(++msg.arg1));
}
@@ -207,12 +224,8 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
if (finalVideoPath != null) {
File file = new File(finalVideoPath);
paperFile = new File(Objects.requireNonNull(file.getParentFile()).getAbsoluteFile() + "/" + "paper.txt");
checkFile = new File(Objects.requireNonNull(file.getParentFile()).getAbsoluteFile() + "/" + "check.json");
videoIndex = Integer.parseInt(file.getName().replace(".webp", ""));
if (videoIndex == 0) {
videoIndex = -1;
} else {
videoIndex = videoIndex - 1;
}
convertIndex = videoIndex;
}
}
@@ -250,31 +263,25 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
radioGroupPicture.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
stopTakePhoto();
capturePicture.setChecked(false);
switch (checkedId) {
case R.id.radio_btn_hand://手动
radioPicture = 1;
isOration = false;
capturePicture.setText("拍摄");
capturePicture.setChecked(false);
stopTimer();
break;
case R.id.radio_btn_auto://自动1秒:
radioPicture = 2;
capturePicture.setText("开始采集");
capturePicture.setChecked(false);
stopTimer();
break;
case R.id.radio_btn_auto_sec://自动2 秒
radioPicture = 3;
capturePicture.setText("开始采集");
capturePicture.setChecked(false);
stopTimer();
break;
case R.id.radio_btn_half_sec://自动0.5 秒
radioPicture = 4;
capturePicture.setText("开始采集");
capturePicture.setChecked(false);
stopTimer();
break;
}
}
@@ -314,13 +321,14 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
} else {
picturesBuilder.append(TimestampUtil.time()).append(",").append("capturePicture 点击了拍摄 ,");
}
startTimer();
startTakePhoto(radioPicture);
startTakePhoto(radioPicture);
} else {
if (radioPicture != 1) {
capturePicture.setText("开始采集");
picturesBuilder.append(TimestampUtil.time()).append(",").append("capturePicture 点击了暂停采集 ,");
}
stopTimer();
stopTakePhoto();
}
}
});
@@ -382,6 +390,96 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
}
});
Flowable<PictureResult> pictureResultFlowable = Flowable.create(new FlowableOnSubscribe<PictureResult>() {
@Override
public void subscribe(FlowableEmitter<PictureResult> emitter) throws Exception {
pictureEmitter = emitter;
}
}, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.io());
Flowable<Integer> integerFlowable = Flowable.create(new FlowableOnSubscribe<Integer>() {
@Override
public void subscribe(FlowableEmitter<Integer> emitter) {
for (int i = videoIndex; ; i++) { // 标记当前是第几个图片记录Index
emitter.onNext(i);
}
}
}, BackpressureStrategy.BUFFER).subscribeOn(Schedulers.io());
Flowable.zip(pictureResultFlowable, integerFlowable, new BiFunction<PictureResult, Integer, Map>(){
@Override
public Map<String, Object> apply(PictureResult pictureResult, Integer fileIndex) throws Exception { // 将图片转为File文件
// 图片和paper.txt处理
File currentFile = new File(paperFile.getParentFile().getAbsolutePath() + "/" + fileIndex + ".webp");
CameraUtils.writeToFile(pictureResult.getData(), currentFile); // 生成照片文件
// 记录当前点位信息
LocationRecorder recorder = new LocationRecorder();
recorder.setTime(System.currentTimeMillis());
// 记录主定位方式
recorder.setTencentLocationX(LocationLifeCycle.getInstance().getMainLocation().getLongitude());
recorder.setTencentLocationY(LocationLifeCycle.getInstance().getMainLocation().getLatitude());
// 记录辅助定位方式
MyLocation gpsLocation = LocationLifeCycle.getInstance().getReferenceLocation();
if (gpsLocation!=null) {
recorder.setGpsLocationX(gpsLocation.getLongitude());
recorder.setGpsLocationY(gpsLocation.getLatitude());
}
recorder.setBearing(LocationLifeCycle.getInstance().getMainLocation().getBearing());
recorder.setRssi(GPSUtils.getInstance(PicturesActivity.this).getSateliteCount());
recorder.setSatelliteCount(GPSUtils.getInstance(PicturesActivity.this).getSateliteCount());
FileUtils.writeFile(paperFile.getAbsolutePath(), recorder.toString(formatter, fileIndex), true);
Map<String, Object> map = new HashMap<>();
map.put("index", fileIndex);
map.put("picture", pictureResult);
return map;
}
})
.subscribeOn(Schedulers.io()) // 指定上游为IO线程
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Map>() {
@Override
public void accept(Map map) throws Exception {
// 绘制图标
initMarker(booleanExtra);
// 设置主界面拍照个数显示
tvTitle.setText(Integer.parseInt(map.get("index").toString())+"");
}
})
.observeOn(Schedulers.computation())// 开始进行图片辅助判定
.subscribe(new FlowableSubscriber<Map>() {
@Override
public void onSubscribe(Subscription s) {
s.request(Integer.MAX_VALUE);
}
@Override
public void onNext(Map map) {
int index = Integer.parseInt(map.get("index").toString());
PictureResult pictureResult = (PictureResult) map.get("picture");
// 记录当前完成多少个图片的处理
// 开始ocr文字识别
List<OcrViewResultModel> ocrViewResultModels = OCRManager.Companion.getInstance().ocr(BitmapUtil.getBitmapFromBytes(pictureResult.getData()));
if (ocrViewResultModels!=null&&!ocrViewResultModels.isEmpty()) {
// 该照片存在文字信息获取占比最大的文字为后续POI判定做准备
}
// 获取当前手机姿态,判定该照片手机姿态是否合格
float yDegree = sensorOritationLifecycle.getYDegree();
// 周边搜索判定周边POI是否存在
}
@Override
public void onError(Throwable t) {
}
@Override
public void onComplete() {
}
});
camera.addCameraListener(new CameraListener() {
@Override
public void onPictureTaken(@NonNull PictureResult result) {
@@ -389,44 +487,29 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
super.onPictureTaken(result);
System.out.println("收到拍照按钮jieguo:"+result.getSize().toString());
isBack = true;
// 如果当前手机是竖向,则不
if (isOration) {
if (Objects.requireNonNull(camera.getPictureSize()).getWidth() < camera.getPictureSize().getHeight()) {
isOration = true;
ToastUtils.Message(PicturesActivity.this, "不允许竖向拍摄...");
picturesBuilder.append("camera 用户竖屏拍照 ,");
stopTimer();
// 如果当前手机是竖向,则不允许拍照
if (Objects.requireNonNull(camera.getPictureSize()).getWidth() < camera.getPictureSize().getHeight()) {
isOration = true;
ToastUtils.Message(PicturesActivity.this, "不允许竖向拍摄...");
picturesBuilder.append("camera 用户竖屏拍照 ,");
stopTakePhoto();
capturePicture.setChecked(false);
if (radioPicture != 1) {
capturePicture.setText("开始采集");
capturePicture.setChecked(false);
if (radioPicture != 1) {
capturePicture.setText("开始采集");
capturePicture.setChecked(false);
}
return;
} else {
isOration = false;
}
return;
} else {
isOration = false;
}
File file = new File(finalVideoPath);
synchronized (finalVideoPath) {
// 生成点位marker
initMarker(booleanExtra);
result.toFile(file, new FileCallback() {
@Override
public void onFileReady(@Nullable File file) {
int currentIndex = videoIndex = Integer.parseInt(file.getName().replace(".webp", ""));
// 下一张照片的路径
finalVideoPath = Objects.requireNonNull(file.getParentFile()).getAbsolutePath() + "/" + (videoIndex + 1) + ".webp";
tvTitle.setText("拍摄成功:" + (videoIndex + 1));
UserApplication.fixedThreadPool.execute(new Jpg2WebpRunnable(/*result, */file, 0, booleanExtra, currentIndex));
}
});
if (pictureEmitter!=null) {
pictureEmitter.onNext(result);
}
} else {
isBack = false;
if (isOration) {
stopTimer();
stopTakePhoto();
}
if (radioPicture != 1) {
capturePicture.setText("开始采集");
@@ -441,18 +524,6 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
layoutParamsMap.height = dm.widthPixels / 3;
layoutParamsMap.width = dm.heightPixels / 3;
tvMapView.setLayoutParams(layoutParamsMap);
timerTask = new TimerTask() {
@Override
public void run() {
if (radioPicture == 1) {
camera.takePictureSnapshot();
} else {
Message message = new Message();
message.what = 0x101;
handler.sendMessage(message);
}
}
};
Button btnSetting = findViewById(R.id.btn_setting);
btnSetting.setOnClickListener(new View.OnClickListener() {
@@ -484,7 +555,6 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
@Override
public void run() {
if (file.exists() && file != null) {
// if (initWeb(file, count, isBoolean, index)) {
initMarkerPaper(index);
if (PicturesActivity.this != null&&handler != null) {
if (radioPicture == 1) {
@@ -498,18 +568,10 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
handler.sendMessage(message);
}
}
// runOnUiThread(new Runnable() {
// @SuppressLint("SetTextI18n")
// @Override
// public void run() {
//
// }
// });
// }
} else {
isBack = false;
if (isOration) {
stopTimer();
stopTakePhoto();
}
if (radioPicture != 1) {
capturePicture.setText("开始采集");
@@ -525,32 +587,6 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
}
}
private boolean initWeb(File file, int count, boolean isBoolean, int index) {
try {
count++;
WebPNative webPNative = new WebPNative();
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
webPNative.encodeRGBA(bitmap, file.getPath(), 90);
if (!bitmap.isRecycled()) {
bitmap.recycle();
}
initMarkerPaper(index);
return true;
} catch (RuntimeException e) {
e.printStackTrace();
//如果是写入txt记录失败上传失败记录
UMCrashManager.reportCrash(this, e);
} catch (Exception e) {
e.printStackTrace();
UMCrashManager.reportCrash(this, e);
if (count < 3) {
//当尝试次数小于3次则加入转换队列尝试重新转换
UserApplication.fixedThreadPool.execute(new Jpg2WebpRunnable(/*result, */file, count, isBoolean, index));
}
}
return false;
}
private void initLine() {
if (geoWkt != null) {
List<LineString> lineStringList = new ArrayList<>();
@@ -871,7 +907,7 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
picturesBuilder.append(TimestampUtil.time()).append(",").append("onPause ,");
tvMapView.onPause();
camera.close();
stopTimer();
stopTakePhoto();
}
@Override
@@ -887,7 +923,7 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
camera.destroy();
tvMapView.onDestroy();
systemTTS.stopSpeak();
stopTimer();
stopTakePhoto();
if (polyline != null) {
polyline.remove();
}
@@ -911,21 +947,6 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
}
}
// @Subscribe(threadMode = ThreadMode.MAIN)
// public void onEventMessageMainThread(Message msg) {
// if (msg.what == Constant.EVENT_WHAT_LOCATION_CHANGE) { // 用户位置更新
// if (tencentMap != null && !isMapSlide) {
// TencentLocation tencentLocation = (TencentLocation) msg.obj;
// CameraUpdate cameraSigma = CameraUpdateFactory.newCameraPosition(new CameraPosition(
// new LatLng(tencentLocation.getLatitude(), tencentLocation.getLongitude()), //中心点坐标,地图目标经纬度
// 17, //目标缩放级别
// 0, //目标倾斜角
// tencentLocation.getBearing())); //目标旋转角 0~360° (正北方为0)
// tencentMap.animateCamera(cameraSigma);
// }
// }
// }
@RequiresApi(api = Build.VERSION_CODES.N)
@SuppressLint("SetTextI18n")
public void initMarkerPaper(int index) {
@@ -1096,7 +1117,7 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
Observable.create(new ObservableOnSubscribe<String>() {
@Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
stopTimer();
stopTakePhoto();
handler.removeMessages(0x101); // 如果handler中存在缓存的拍摄请求也清空
picturesBuilder.append(TimestampUtil.time()).append(",").append("onClick 点击了结束采集 ,");
emitter.onComplete();
@@ -1150,48 +1171,26 @@ public class PicturesActivity extends BaseActivity implements View.OnClickListen
removables.add(marker);
}
private void startTimer() {
if (timer == null) {
timer = new Timer();
}
if (timerTask == null) {
timerTask = new TimerTask() {
@Override
public void run() {
if (handler!=null) {
if (radioPicture == 1) {
camera.takePictureSnapshot();
} else {
Message message = new Message();
message.what = 0x101;
handler.sendMessage(message);
}
}
}
};
}
private void startTakePhoto(int radioPicture) {
if (radioPicture == 1) {
timer.schedule(timerTask, 0);
} else if (radioPicture == 2) {
timer.schedule(timerTask, 0, 1000);
} else if (radioPicture == 3) {
timer.schedule(timerTask, 0, 2000);
} else if (radioPicture == 4) {
timer.schedule(timerTask, 0, 500);
camera.takePictureSnapshot();
} else {
disposable = Observable.interval(1000L, TimeUnit.MILLISECONDS, Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<Long>() {
@Override
public void accept(Long aLong) throws Exception {
camera.takePictureSnapshot();
}
});
}
}
private void stopTimer() {
if (timer != null) {
timer.cancel();
timer = null;
}
if (timerTask != null) {
timerTask.cancel();
timerTask = null;
private void stopTakePhoto() {
if (disposable!=null&&!disposable.isDisposed()) {
disposable.dispose();
}
}
// 设置当前界面亮度
private void setWindowBrightness(int brightness) {
// Window window = getWindow();

View File

@@ -79,7 +79,7 @@ public class StaySubmitFragment extends BaseFragment implements View.OnClickList
private CheckBox cbSelect;
private File logFile;
private StringBuilder staySubmitBuilder;
private Logger logger = XLogUtils.Companion.getInstance().getUploadLogWriter();
private Logger logger;
public static StaySubmitFragment newInstance(Bundle bundle) {
StaySubmitFragment fragment = new StaySubmitFragment();
@@ -93,6 +93,7 @@ public class StaySubmitFragment extends BaseFragment implements View.OnClickList
if (!EventBus.getDefault().isRegistered(this)) {//加上判断
EventBus.getDefault().register(this);
}
logger=XLogUtils.Companion.getInstance().getUploadLogWriter();
}
@Override

File diff suppressed because it is too large Load Diff