VideoProcessUtil.java 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. package com.izouma.mobilecybergames;
  2. import android.content.Context;
  3. import android.graphics.Bitmap;
  4. import android.os.Environment;
  5. import android.text.TextUtils;
  6. import android.util.Log;
  7. import android.util.Pair;
  8. import com.googlecode.tesseract.android.ResultIterator;
  9. import com.googlecode.tesseract.android.TessBaseAPI;
  10. import org.opencv.android.Utils;
  11. import org.opencv.core.Core;
  12. import org.opencv.core.CvException;
  13. import org.opencv.core.Mat;
  14. import org.opencv.core.MatOfPoint;
  15. import org.opencv.core.Point;
  16. import org.opencv.core.Rect;
  17. import org.opencv.core.Scalar;
  18. import org.opencv.core.Size;
  19. import org.opencv.imgproc.Imgproc;
  20. import org.opencv.ml.SVM;
  21. import org.opencv.videoio.VideoCapture;
  22. import org.opencv.videoio.Videoio;
  23. import java.io.BufferedInputStream;
  24. import java.io.BufferedReader;
  25. import java.io.ByteArrayOutputStream;
  26. import java.io.File;
  27. import java.io.FileInputStream;
  28. import java.io.FileNotFoundException;
  29. import java.io.FileOutputStream;
  30. import java.io.IOException;
  31. import java.io.InputStream;
  32. import java.io.InputStreamReader;
  33. import java.nio.charset.StandardCharsets;
  34. import java.util.ArrayList;
  35. import java.util.HashMap;
  36. import java.util.List;
  37. import java.util.Map;
  38. import java.util.regex.Pattern;
  39. import java.util.stream.Collectors;
  40. import static org.opencv.core.Core.NATIVE_LIBRARY_NAME;
  41. import static org.opencv.core.Core.ROTATE_90_COUNTERCLOCKWISE;
  42. import static org.opencv.core.Core.rotate;
  43. import static org.opencv.core.CvType.CV_32F;
  44. import static org.opencv.core.CvType.CV_32FC1;
  45. import static org.opencv.core.CvType.CV_8U;
  46. import static org.opencv.core.CvType.CV_8UC1;
  47. import static org.opencv.core.CvType.CV_8UC3;
  48. import static org.opencv.imgproc.Imgproc.CHAIN_APPROX_SIMPLE;
  49. import static org.opencv.imgproc.Imgproc.COLOR_BGR2GRAY;
  50. import static org.opencv.imgproc.Imgproc.COLOR_BGR2HSV;
  51. import static org.opencv.imgproc.Imgproc.MORPH_ELLIPSE;
  52. import static org.opencv.imgproc.Imgproc.MORPH_RECT;
  53. import static org.opencv.imgproc.Imgproc.RETR_CCOMP;
  54. import static org.opencv.imgproc.Imgproc.bilateralFilter;
  55. import static org.opencv.imgproc.Imgproc.blur;
  56. import static org.opencv.imgproc.Imgproc.boundingRect;
  57. import static org.opencv.imgproc.Imgproc.cvtColor;
  58. import static org.opencv.imgproc.Imgproc.filter2D;
  59. import static org.opencv.imgproc.Imgproc.findContours;
  60. import static org.opencv.imgproc.Imgproc.getStructuringElement;
  61. import static org.opencv.imgproc.Imgproc.medianBlur;
  62. import static org.opencv.imgproc.Imgproc.rectangle;
  63. import static org.opencv.imgproc.Imgproc.resize;
  64. public class VideoProcessUtil {
  65. static {
  66. System.loadLibrary(NATIVE_LIBRARY_NAME);
  67. }
  68. public static void init(Context context) {
  69. File dir = context.getExternalFilesDir("tessdata");
  70. if (!dir.exists()) {
  71. dir.mkdirs();
  72. }
  73. copyAssets(context, "pubg.traineddata", dir);
  74. copyAssets(context, "pubg.xml", dir);
  75. }
  76. public static void copyAssets(Context context, String name, File dir) {
  77. try {
  78. InputStream is = context.getAssets().open(name);
  79. FileOutputStream fos = new FileOutputStream(new File(dir, name));
  80. byte[] buffer = new byte[1024];
  81. int byteCount = 0;
  82. while ((byteCount = is.read(buffer)) != -1) {
  83. fos.write(buffer, 0, byteCount);
  84. }
  85. fos.flush();
  86. is.close();
  87. fos.close();
  88. } catch (IOException e) {
  89. e.printStackTrace();
  90. }
  91. }
  92. public static Map<String, Object> processVideo(Context context, String path) {
  93. String xmlPath = new File(context.getExternalFilesDir("tessdata"), "pubg.xml").getPath();
  94. SVM svm = SVM.load(xmlPath);
  95. Map<String, Object> map = new HashMap<>();
  96. Mat frame = new Mat();
  97. Mat v = new Mat();
  98. long frameCount = 0;
  99. VideoCapture capture = new VideoCapture();
  100. if (capture.open(path)) {
  101. System.out.println(capture.get(Videoio.CAP_PROP_FRAME_WIDTH));
  102. System.out.println(capture.get(Videoio.CAP_PROP_FRAME_HEIGHT));
  103. } else {
  104. return null;
  105. }
  106. while (capture.read(frame)) {
  107. frameCount++;
  108. if (frameCount % 200 == 0) {
  109. System.gc();
  110. }
  111. if (frameCount % 3 != 0) {
  112. continue;
  113. }
  114. if (matchGameOver(svm, frame)) {
  115. String rank = extractRank(frame);
  116. String time = extractGameTime(frame);
  117. if (TextUtils.isEmpty(rank) || TextUtils.isEmpty(time)) {
  118. continue;
  119. }
  120. rank = rank.trim().replace(" ", "").replace("\n", "");
  121. time = time.trim().replace(" ", "").replace("\n", "");
  122. if (!Pattern.matches("^第\\d{1,3}$", rank)
  123. || !Pattern.matches("((^([0-9]+[.][0-9]*))|(^\\d{1,2}))分钟", time)) {
  124. continue;
  125. }
  126. map.put("rank", rank);
  127. map.put("time", time);
  128. System.out.println(map);
  129. break;
  130. }
  131. System.out.println(frameCount + " frame processed");
  132. }
  133. capture.release();
  134. frame.release();
  135. v.release();
  136. System.gc();
  137. return map;
  138. }
  139. private static boolean matchGameOver(SVM svm, Mat inputImage) {
  140. if (inputImage.cols() < inputImage.rows()) {
  141. rotate(inputImage, inputImage, ROTATE_90_COUNTERCLOCKWISE);
  142. }
  143. Mat v = new Mat();
  144. removeBlackBar(inputImage, inputImage);
  145. resize(inputImage, v, new Size(854, 480));
  146. Mat p = v.reshape(1, 1);
  147. p.convertTo(p, CV_32FC1);
  148. float res = svm.predict(p);
  149. return res == 1;
  150. }
  151. private static String extractRank(Mat inputImage) {
  152. String result = null;
  153. int m = inputImage.rows();
  154. int n = inputImage.cols();
  155. inputImage = inputImage.rowRange(0, (int) (m * 0.5)).colRange((int) (n * 0.5), n);
  156. Mat resultGray = new Mat(inputImage.rows(), inputImage.cols(), CV_8U, new Scalar(0));
  157. Mat resultColor = new Mat(inputImage.rows(), inputImage.cols(), CV_8UC3, new Scalar(255, 255, 255));
  158. HSVFilter(inputImage, resultGray, resultColor, 25, 65, 40, 255, 130, 255);
  159. int erosion_size = m / 400;
  160. erode(resultGray, resultGray, erosion_size);
  161. int dilation_size = m / 400 * 4;
  162. dilate(resultGray, resultGray, dilation_size);
  163. List<MatOfPoint> contours = new ArrayList<>();
  164. Mat hierarchy = new Mat();
  165. findContours(resultGray, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
  166. Rect textRect = new Rect();
  167. for (int i = 0; i < contours.size(); i++) {
  168. Rect rect = boundingRect(contours.get(i));
  169. if (rect.area() > textRect.area()) {
  170. textRect = rect;
  171. }
  172. }
  173. if (textRect.width > 0 && textRect.height > 0) {
  174. Mat textImg = inputImage.rowRange(textRect.y, textRect.y + textRect.height).colRange(textRect.x, textRect.x + textRect.width);
  175. Mat mat1 = new Mat(textImg.rows(), textImg.cols(), CV_8UC1);
  176. for (int i = 0; i < textImg.rows(); i++) {
  177. for (int j = 0; j < textImg.cols(); j++) {
  178. double s = (textImg.get(i, j)[1] + textImg.get(i, j)[2]) / 2;
  179. mat1.put(i, j, s > 220 ? s : 0);
  180. }
  181. }
  182. medianBlur(mat1, mat1, 3);
  183. for (MatOfPoint contour : contours) {
  184. Rect rect = boundingRect(contour);
  185. rectangle(inputImage, rect, new Scalar(0, 0, 255), 1);
  186. }
  187. rectangle(inputImage, textRect, new Scalar(0, 255, 0), 2);
  188. result = doOCR(mat1);
  189. textImg.release();
  190. mat1.release();
  191. }
  192. inputImage.release();
  193. resultGray.release();
  194. resultColor.release();
  195. System.gc();
  196. return result;
  197. }
  198. private static String extractGameTime(Mat inputImage) {
  199. String result = null;
  200. int m = inputImage.rows();
  201. int n = inputImage.cols();
  202. inputImage = inputImage.rowRange((int) (m * 0.5), (int) (m * 0.8)).colRange((int) (n * 0.5), n);
  203. Mat resultGray = new Mat(inputImage.rows(), inputImage.cols(), CV_8U, new Scalar(0));
  204. Mat resultColor = new Mat(inputImage.rows(), inputImage.cols(), CV_8UC3, new Scalar(255, 255, 255));
  205. HSVFilter(inputImage, resultGray, resultColor, 15, 28, 80, 255, 80, 255);
  206. int dilation_size = resultGray.rows() / 120 * 3;
  207. dilate(resultGray, resultGray, dilation_size);
  208. int erosion_size = resultGray.rows() / 120 * 3;
  209. erode(resultGray, resultGray, erosion_size);
  210. dilate(resultGray, resultGray, dilation_size);
  211. List<MatOfPoint> contours = new ArrayList<>();
  212. Mat hierarchy = new Mat();
  213. findContours(resultGray, contours, hierarchy, RETR_CCOMP, CHAIN_APPROX_SIMPLE);
  214. Rect textRect = new Rect();
  215. for (int i = 0; i < contours.size(); i++) {
  216. Rect rect = boundingRect(contours.get(i));
  217. if (rect.area() > textRect.area() && rect.height < inputImage.rows() / 3) {
  218. textRect = rect;
  219. }
  220. }
  221. if (textRect.width > 0 && textRect.height > 0) {
  222. Mat textImg = inputImage.rowRange(textRect.y, textRect.y + textRect.height).colRange(textRect.x, textRect.x + textRect.width);
  223. textImg.convertTo(textImg, CV_8UC3);
  224. Mat imageBf = new Mat();
  225. bilateralFilter(textImg, imageBf, 3, 3, 2);//双边滤波
  226. Mat imageEnhance = new Mat();
  227. Mat kernel = new Mat(3, 3, CV_32F);
  228. kernel.put(0, 0, 0);
  229. kernel.put(0, 1, -1);
  230. kernel.put(0, 2, 0);
  231. kernel.put(1, 0, 0);
  232. kernel.put(1, 1, 4);
  233. kernel.put(1, 2, 0);
  234. kernel.put(2, 0, 0);
  235. kernel.put(2, 1, -1);
  236. kernel.put(2, 2, 0);
  237. filter2D(imageBf, imageEnhance, CV_8UC3, kernel);
  238. cvtColor(textImg, textImg, COLOR_BGR2GRAY);
  239. textImg.convertTo(textImg, CV_8UC1);
  240. for (MatOfPoint contour : contours) {
  241. Rect rect = boundingRect(contour);
  242. rectangle(inputImage, rect, new Scalar(0, 0, 255), 1);
  243. }
  244. rectangle(inputImage, textRect, new Scalar(0, 255, 0), 1);
  245. result = doOCR(textImg);
  246. imageBf.release();
  247. imageEnhance.release();
  248. textImg.release();
  249. }
  250. inputImage.release();
  251. resultGray.release();
  252. resultColor.release();
  253. System.gc();
  254. return result;
  255. }
  256. private static void dilate(Mat src, Mat dst, int dilation_size) {
  257. Mat dilationElement = getStructuringElement(MORPH_ELLIPSE,
  258. new Size(2 * dilation_size + 1, 2 * dilation_size + 1),
  259. new Point(dilation_size, dilation_size));
  260. Imgproc.dilate(src, dst, dilationElement);
  261. }
  262. private static void erode(Mat src, Mat dst, int erosion_size) {
  263. Mat erodeElement = getStructuringElement(MORPH_RECT,
  264. new Size(2 * erosion_size + 1, 2 * erosion_size + 1),
  265. new Point(erosion_size, erosion_size));
  266. Imgproc.erode(src, dst, erodeElement);
  267. }
  268. private static void HSVFilter(Mat inputImage, Mat resultGray, Mat resultColor, int h_min, int h_max, int s_min, int s_max, int v_min, int v_max) {
  269. Mat hsvImage = new Mat();
  270. blur(inputImage, hsvImage, new Size(3, 3));
  271. cvtColor(hsvImage, hsvImage, COLOR_BGR2HSV);
  272. double H = 0.0, S = 0.0, V = 0.0;
  273. for (int i = 0; i < hsvImage.rows(); i++) {
  274. for (int j = 0; j < hsvImage.cols(); j++) {
  275. H = hsvImage.get(i, j)[0];
  276. S = hsvImage.get(i, j)[1];
  277. V = hsvImage.get(i, j)[2];
  278. if ((H >= h_min && H <= h_max)
  279. && (S >= s_min && S <= s_max)
  280. && (V >= v_min && V <= v_max)) {
  281. resultGray.put(i, j, 255d);
  282. resultColor.put(i, j, inputImage.get(i, j));
  283. }
  284. }
  285. }
  286. hsvImage.release();
  287. }
  288. private static String doOCR(Mat img) {
  289. Bitmap bmp = null;
  290. try {
  291. Imgproc.cvtColor(img, img, Imgproc.COLOR_GRAY2RGBA, 4);
  292. bmp = Bitmap.createBitmap(img.cols(), img.rows(), Bitmap.Config.ARGB_8888);
  293. Utils.matToBitmap(img, bmp);
  294. } catch (CvException e) {
  295. Log.d("Exception", e.getMessage());
  296. }
  297. final TessBaseAPI baseApi = new TessBaseAPI();
  298. baseApi.init("", "pubg");
  299. baseApi.setPageSegMode(TessBaseAPI.PageSegMode.PSM_SINGLE_LINE);
  300. baseApi.setVariable(TessBaseAPI.VAR_SAVE_BLOB_CHOICES, TessBaseAPI.VAR_TRUE);
  301. // Ensure that text is recognized.
  302. baseApi.setImage(bmp);
  303. String recognizedText = baseApi.getUTF8Text();
  304. // Iterate through the results.
  305. ResultIterator iterator = baseApi.getResultIterator();
  306. List<Pair<String, Double>> choicesAndConfidences;
  307. iterator.begin();
  308. do {
  309. choicesAndConfidences = iterator.getSymbolChoicesAndConfidence();
  310. for (Pair<String, Double> choiceAndConfidence : choicesAndConfidences) {
  311. String choice = choiceAndConfidence.first;
  312. Double conf = choiceAndConfidence.second;
  313. System.out.println("choice:" + choice + ", conf:" + conf);
  314. }
  315. } while (iterator.next(TessBaseAPI.PageIteratorLevel.RIL_SYMBOL));
  316. iterator.delete();
  317. baseApi.end();
  318. bmp.recycle();
  319. return recognizedText;
  320. }
  321. private static void removeBlackBar(Mat src, Mat dst) {
  322. int[] top = new int[20];
  323. int[] bottom = new int[20];
  324. int[] left = new int[20];
  325. int[] right = new int[20];
  326. int rowStep = (int) (src.rows() * 0.4 / 20);
  327. int colStep = (int) (src.cols() * 0.4 / 20);
  328. int r = (int) (src.rows() * 0.3);
  329. for (int i = 0; i < 20; i++) {
  330. for (int c = 2; c + 2 < src.cols(); c++) {
  331. double B = (src.get(r, c - 2)[0] + src.get(r, c - 1)[0] + src.get(r, c)[0] + src.get(r, c + 1)[0] + src.get(r, c + 2)[0]) / 3f;
  332. double G = (src.get(r, c - 2)[1] + src.get(r, c - 1)[1] + src.get(r, c)[1] + src.get(r, c + 1)[1] + src.get(r, c + 2)[1]) / 3f;
  333. double R = (src.get(r, c - 2)[2] + src.get(r, c - 1)[2] + src.get(r, c)[2] + src.get(r, c + 1)[2] + src.get(r, c + 2)[2]) / 3f;
  334. if (B > 5 && G > 5 && R > 5) {
  335. left[i] = c;
  336. break;
  337. }
  338. }
  339. for (int c = src.cols() - 3; c - 2 >= 0; c--) {
  340. double B = (src.get(r, c - 2)[0] + src.get(r, c - 1)[0] + src.get(r, c)[0] + src.get(r, c + 1)[0] + src.get(r, c + 2)[0]) / 3f;
  341. double G = (src.get(r, c - 2)[1] + src.get(r, c - 1)[1] + src.get(r, c)[1] + src.get(r, c + 1)[1] + src.get(r, c + 2)[1]) / 3f;
  342. double R = (src.get(r, c - 2)[2] + src.get(r, c - 1)[2] + src.get(r, c)[2] + src.get(r, c + 1)[2] + src.get(r, c + 2)[2]) / 3f;
  343. if (B > 5 && G > 5 && R > 5) {
  344. right[i] = c;
  345. break;
  346. }
  347. }
  348. r += rowStep;
  349. }
  350. int c = (int) (src.cols() * 0.3);
  351. for (int i = 0; i < 20; i++) {
  352. for (r = 2; r + 2 < src.rows(); r++) {
  353. double B = (src.get(r, c - 2)[0] + src.get(r, c - 1)[0] + src.get(r, c)[0] + src.get(r, c + 1)[0] + src.get(r, c + 2)[0]) / 3f;
  354. double G = (src.get(r, c - 2)[1] + src.get(r, c - 1)[1] + src.get(r, c)[1] + src.get(r, c + 1)[1] + src.get(r, c + 2)[1]) / 3f;
  355. double R = (src.get(r, c - 2)[2] + src.get(r, c - 1)[2] + src.get(r, c)[2] + src.get(r, c + 1)[2] + src.get(r, c + 2)[2]) / 3f;
  356. if (B > 5 && G > 5 && R > 5) {
  357. top[i] = r;
  358. break;
  359. }
  360. }
  361. for (r = src.rows() - 3; r - 2 >= 0; r--) {
  362. double B = (src.get(r, c - 2)[0] + src.get(r, c - 1)[0] + src.get(r, c)[0] + src.get(r, c + 1)[0] + src.get(r, c + 2)[0]) / 3f;
  363. double G = (src.get(r, c - 2)[1] + src.get(r, c - 1)[1] + src.get(r, c)[1] + src.get(r, c + 1)[1] + src.get(r, c + 2)[1]) / 3f;
  364. double R = (src.get(r, c - 2)[2] + src.get(r, c - 1)[2] + src.get(r, c)[2] + src.get(r, c + 1)[2] + src.get(r, c + 2)[2]) / 3f;
  365. if (B > 5 && G > 5 && R > 5) {
  366. bottom[i] = r;
  367. break;
  368. }
  369. }
  370. c += colStep;
  371. }
  372. int offsetTop = (int) Math.max(avg(top) - 2, 0);
  373. int offsetBottom = (int) Math.min(avg(bottom) + 2, src.rows() - 1);
  374. int offsetLeft = (int) Math.max(avg(left) - 2, 0);
  375. int offsetRight = (int) Math.min(avg(right) + 2, src.cols() - 1);
  376. new Mat(src, new Rect(new Point(offsetLeft, offsetTop), new Point(offsetRight, offsetBottom))).copyTo(dst);
  377. }
  378. private static double avg(int[] arr) {
  379. int sum = 0;
  380. for (int i : arr) {
  381. sum += i;
  382. }
  383. return sum / (double) arr.length;
  384. }
  385. }