From 892c19130cf395195a1a08b8a53e2c4b322804d1 Mon Sep 17 00:00:00 2001 From: UnknownObject Date: Fri, 16 Jun 2023 21:46:30 +0800 Subject: [PATCH] add colored qr code --- .idea/encodings.xml | 6 + app/src/main/cpp/CMakeLists.txt | 24 ++ app/src/main/cpp/color_reco.cpp | 39 ++- app/src/main/cpp/color_reco.h | 30 ++- app/src/main/cpp/colored_qr_detect/QRCode.cpp | 66 +++++ app/src/main/cpp/colored_qr_detect/QRCode.h | 52 ++++ .../main/cpp/colored_qr_detect/QRCodeSet.cpp | 72 +++++ .../main/cpp/colored_qr_detect/QRCodeSet.h | 36 +++ .../colored_qr_detect/colored_qr_decode.cpp | 253 ++++++++++++++++++ .../cpp/colored_qr_detect/colored_qr_decode.h | 61 +++++ .../com/uns/maincar/constants/Commands.java | 12 + .../cpp_interface/ColoredQRDecoder.java | 125 +++++++++ .../uns/maincar/cpp_interface/QRDecoder.java | 4 +- .../java/com/uns/maincar/gui/DebugPage.java | 4 + .../com/uns/maincar/gui/MainActivity.java | 172 +++++++----- .../com/uns/maincar/gui/PermissionGetter.java | 4 + .../java/com/uns/maincar/gui/RaceTasks.java | 4 + .../uns/maincar/gui/SingleFunctionTest.java | 4 + .../layout/activity_single_function_test.xml | 30 +++ .../arm64-v8a/metadata_generation_record.json | 6 +- .../metadata_generation_record.json | 6 +- .../x86/generate_cxx_metadata_1466_timing.txt | 5 + .../x86/generate_cxx_metadata_31_timing.txt | 5 + .../meta/x86/metadata_generation_record.json | 6 +- .../x86_64/metadata_generation_record.json | 6 +- .../6x33t2q6/obj/x86/libopencv_jni_shared.a | Bin 700 -> 700 bytes .../meta/x86/metadata_generation_record.json | 6 +- .../cxx/create_cxx_tasks_296_timing.txt | 12 + .../cxx/create_cxx_tasks_31_timing.txt | 12 + .../cxx/ndk_locator_record_50t213g5.log | 12 +- 30 files changed, 974 insertions(+), 100 deletions(-) create mode 100644 .idea/encodings.xml create mode 100644 app/src/main/cpp/colored_qr_detect/QRCode.cpp create mode 100644 app/src/main/cpp/colored_qr_detect/QRCode.h create mode 100644 app/src/main/cpp/colored_qr_detect/QRCodeSet.cpp create mode 100644 app/src/main/cpp/colored_qr_detect/QRCodeSet.h create mode 100644 app/src/main/cpp/colored_qr_detect/colored_qr_decode.cpp create mode 100644 app/src/main/cpp/colored_qr_detect/colored_qr_decode.h create mode 100644 app/src/main/java/com/uns/maincar/cpp_interface/ColoredQRDecoder.java diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..c7dc576 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt index de749ba..c09cc1c 100644 --- a/app/src/main/cpp/CMakeLists.txt +++ b/app/src/main/cpp/CMakeLists.txt @@ -165,6 +165,21 @@ add_library( # Sets the name of the library. lib_hyper_lpr/src/SegmentationFreeRecognizer.cpp lib_hyper_lpr/javaWarpper.cpp) +add_library( # Sets the name of the library. + colored_qr_code + + # Sets the library as a shared library. + SHARED + + # Provides a relative path to your source file(s). + color_reco.cpp + public_types.cpp + opencv_support.cpp + opencv_libqr/cv_qrcode.cpp + colored_qr_detect/QRCode.cpp + colored_qr_detect/QRCodeSet.cpp + colored_qr_detect/colored_qr_decode.cpp) + # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library @@ -287,3 +302,12 @@ target_link_libraries( # Specifies the target library. ${OpenCV_LIBS} ${jnigraphics-lib} ${log-lib}) + +target_link_libraries( # Specifies the target library. + colored_qr_code + + # Links the target library to the log library + # included in the NDK. + ${OpenCV_LIBS} + ${jnigraphics-lib} + ${log-lib}) diff --git a/app/src/main/cpp/color_reco.cpp b/app/src/main/cpp/color_reco.cpp index 9376157..7270d79 100644 --- a/app/src/main/cpp/color_reco.cpp +++ b/app/src/main/cpp/color_reco.cpp @@ -16,13 +16,13 @@ namespace uns } //识别颜色,逐像素便利并统计出现次数最多的颜色作为最终结果 - std::string ColorReco::RecoColor(const cv::Mat& img) + Colors::CVColor ColorReco::RecoColor(const cv::Mat &img) { int max_count = 0; - std::string color_name = "null"; + Colors::CVColor max_color; Colors::ColorCount counter = basic_color_counter; if (img.empty()) - return color_name; + return std_color_white; for (int r = 0; r < img.rows; r++) { for (int c = 0; c < img.cols; c++) @@ -46,17 +46,42 @@ namespace uns counter[std_color_white]++; } } - for (auto& ele : counter) + for (auto &ele: counter) { - if ((ele.first == std_color_white)/* || (ele.first == std_color_yellow)*/) + if ((ele.first == std_color_white)) continue; if (ele.second > max_count) { + max_color = ele.first; max_count = ele.second; - color_name = color_name_map.at(ele.first); } } - return color_name; + return max_color; + } + + std::string ColorReco::RecoColorStr(const Colors::CVColor &color) + { + return color_name_map.at(color); + } + + bool ColorReco::IsRed(const Colors::CVColor &result) + { + return (result == std_color_red); + } + + bool ColorReco::IsGreen(const Colors::CVColor &result) + { + return (result == std_color_green); + } + + bool ColorReco::IsBlue(const Colors::CVColor &result) + { + return (result == std_color_blue); + } + + bool ColorReco::IsYellow(const Colors::CVColor &result) + { + return (result == std_color_yellow); } }; diff --git a/app/src/main/cpp/color_reco.h b/app/src/main/cpp/color_reco.h index 86c149e..73c9bda 100644 --- a/app/src/main/cpp/color_reco.h +++ b/app/src/main/cpp/color_reco.h @@ -44,19 +44,31 @@ namespace uns }; std::map color_name_map = { - { std_color_red, "red" }, - { std_color_green, "green"}, - { std_color_blue, "blue"}, - { std_color_yellow, "yellow"}, - { std_color_purple, "purple"}, - { std_color_cyan, "cyan"}, - { std_color_black, "black"}, - { std_color_white, "white"}, + {std_color_red, "red" }, + {std_color_green, "green"}, + {std_color_blue, "blue"}, + {std_color_yellow, "yellow"}, + {std_color_purple, "purple"}, + {std_color_cyan, "cyan"}, + {std_color_black, "black"}, + {std_color_white, "white"}, }; private: bool MuchLarger(int a, int b, double rate); + public: - std::string RecoColor(const cv::Mat& img); + Colors::CVColor RecoColor(const cv::Mat &img); + + std::string RecoColorStr(const Colors::CVColor &color); + + public: + bool IsRed(const Colors::CVColor &result); + + bool IsGreen(const Colors::CVColor &result); + + bool IsBlue(const Colors::CVColor &result); + + bool IsYellow(const Colors::CVColor &result); }; }; diff --git a/app/src/main/cpp/colored_qr_detect/QRCode.cpp b/app/src/main/cpp/colored_qr_detect/QRCode.cpp new file mode 100644 index 0000000..28e06f3 --- /dev/null +++ b/app/src/main/cpp/colored_qr_detect/QRCode.cpp @@ -0,0 +1,66 @@ +#include "QRCode.h" + +namespace uns +{ + bool QRCode::IsSameRect(const cv::Rect &r1, const cv::Rect &r2) const + { + cv::Point c1 = r1.tl(); + cv::Point c2 = r2.tl(); + int x_offset = __ABS__(c1.x - c2.x); + int y_offset = __ABS__(c1.y - c2.y); + return ((x_offset <= EQUAL_OFFSET) && (y_offset <= EQUAL_OFFSET)); + } + + QRCode::QRCode() + { + color = Color::Invalidate; + } + + QRCode::QRCode(const cv::Rect &r) + { + position = r; + color = Color::Invalidate; + } + + QRCode::QRCode(const QRCode &obj) + { + color = obj.color; + position = obj.position; + } + + QRCode::QRCode(Color c, const cv::Rect &r) + { + color = c; + position = r; + } + + QRCode::Color QRCode::GetColor() const + { + return color; + } + + cv::Rect QRCode::GetRect() const + { + return position; + } + + void QRCode::SetColor(Color color) + { + this->color = color; + } + + void QRCode::SetRect(const cv::Rect &rect) + { + position = rect; + } + + bool QRCode::operator==(const QRCode &obj) const + { + return IsSameRect(position, obj.position); + } + + bool QRCode::operator!=(const QRCode &obj) const + { + return (!IsSameRect(position, obj.position)); + } +} diff --git a/app/src/main/cpp/colored_qr_detect/QRCode.h b/app/src/main/cpp/colored_qr_detect/QRCode.h new file mode 100644 index 0000000..eca02b2 --- /dev/null +++ b/app/src/main/cpp/colored_qr_detect/QRCode.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +#define __ABS__(x) (x < 0 ? -x : x) + +namespace uns +{ + class QRCode + { + public: + enum class Color + { + Red = 0, + Green = 1, + Yellow = 2, + Blue = 3, + Invalidate = -1 + }; + private: + Color color; + cv::Rect position; + const int EQUAL_OFFSET = 2; + private: + bool IsSameRect(const cv::Rect &r1, const cv::Rect &r2) const; + + public: + QRCode(); + + QRCode(const cv::Rect &r); + + QRCode(const QRCode &obj); + + QRCode(Color c, const cv::Rect &r); + + public: + Color GetColor() const; + + cv::Rect GetRect() const; + + void SetColor(Color color); + + void SetRect(const cv::Rect &rect); + + public: + bool operator==(const QRCode &obj) const; + + bool operator!=(const QRCode &obj) const; + }; +} + diff --git a/app/src/main/cpp/colored_qr_detect/QRCodeSet.cpp b/app/src/main/cpp/colored_qr_detect/QRCodeSet.cpp new file mode 100644 index 0000000..483ef5c --- /dev/null +++ b/app/src/main/cpp/colored_qr_detect/QRCodeSet.cpp @@ -0,0 +1,72 @@ +#include "QRCodeSet.h" + +namespace uns +{ + QRCodeSet::QRCodeSet() + { + } + + QRCodeSet::QRCodeSet(const QRCodeSet &obj) + { + qr_codes.clear(); + qr_codes.shrink_to_fit(); + current_index = obj.current_index; + for (auto &ele: obj.qr_codes) + qr_codes.push_back(ele); + } + + void QRCodeSet::Clear() + { + qr_codes.clear(); + current_index = 0; + } + + size_t QRCodeSet::Size() + { + return qr_codes.size(); + } + + bool QRCodeSet::HasNextCode() + { + return (current_index < qr_codes.size()); + } + + void QRCodeSet::ResetPointer() + { + current_index = 0; + } + + QRCode &QRCodeSet::GetNextCode() + { + return qr_codes[current_index++]; + } + + void QRCodeSet::Insert(const QRCode &obj, const cv::Mat &source_img) + { + for (auto &ele: qr_codes) + if (ele == obj) + return; + if (obj.GetColor() != QRCode::Color::Invalidate) + qr_codes.push_back(obj); + else + { + QRCode code(obj); + ColorReco color_reco; + Colors::CVColor result = color_reco.RecoColor(source_img(code.GetRect())); + if (color_reco.IsRed(result)) + code.SetColor(QRCode::Color::Red); + else if (color_reco.IsGreen(result)) + code.SetColor(QRCode::Color::Green); + else if (color_reco.IsBlue(result)) + code.SetColor(QRCode::Color::Blue); + else if (color_reco.IsYellow(result)) + code.SetColor(QRCode::Color::Yellow); + qr_codes.push_back(code); + } + } + + QRCode &QRCodeSet::operator[](size_t index) + { + return qr_codes[index]; + } +} diff --git a/app/src/main/cpp/colored_qr_detect/QRCodeSet.h b/app/src/main/cpp/colored_qr_detect/QRCodeSet.h new file mode 100644 index 0000000..3fb2762 --- /dev/null +++ b/app/src/main/cpp/colored_qr_detect/QRCodeSet.h @@ -0,0 +1,36 @@ +#pragma once + +#include +#include "QRCode.h" +#include "../color_reco.h" + +namespace uns +{ + class QRCodeSet + { + private: + int current_index = 0; + std::vector qr_codes; + public: + QRCodeSet(); + + QRCodeSet(const QRCodeSet &obj); + + public: + void Clear(); + + size_t Size(); + + bool HasNextCode(); + + void ResetPointer(); + + QRCode &GetNextCode(); + + void Insert(const QRCode &obj, const cv::Mat &source_img); + + public: + QRCode &operator[](size_t index); + }; +} + diff --git a/app/src/main/cpp/colored_qr_detect/colored_qr_decode.cpp b/app/src/main/cpp/colored_qr_detect/colored_qr_decode.cpp new file mode 100644 index 0000000..faafd6a --- /dev/null +++ b/app/src/main/cpp/colored_qr_detect/colored_qr_decode.cpp @@ -0,0 +1,253 @@ +#include "colored_qr_decode.h" + +namespace uns +{ + SingleQRImage::SingleQRImage() + { + color = QRCode::Color::Invalidate; + } + + SingleQRImage::SingleQRImage(const SingleQRImage &img) + { + color = img.color; + img.binary.copyTo(binary); + img.colored.copyTo(colored); + } + + SingleQRImage::SingleQRImage(const cv::Mat &clr_src, const RGBChannel &rgb_src, + const QRCode &code) + { + clr_src(code.GetRect()).copyTo(colored); + switch (code.GetColor()) + { + case QRCode::Color::Red: + rgb_src[1](code.GetRect()).copyTo(binary); + break; + case QRCode::Color::Green: + rgb_src[0](code.GetRect()).copyTo(binary); + break; + case QRCode::Color::Blue: + rgb_src[0](code.GetRect()).copyTo(binary); + break; + case QRCode::Color::Yellow: + rgb_src[2](code.GetRect()).copyTo(binary); + break; + default: + clr_src(code.GetRect()).copyTo(binary); + break; + } + color = code.GetColor(); + } + + bool SingleQRImage::DataValidate() const + { + return ((!binary.empty()) && (!colored.empty())); + } + + QRCode::Color SingleQRImage::GetColor() const + { + return color; + } + + cv::Mat SingleQRImage::GetBinaryImage() const + { + return binary; + } + + cv::Mat SingleQRImage::GetColoredImage() const + { + return colored; + } + + RGBChannel ColoredQrDecode::SplitRGBChannel(const cv::Mat &obj) + { + cv::Mat r(obj.size(), CV_8UC1), g(obj.size(), CV_8UC1), b(obj.size(), CV_8UC1); + for (int i = 0; i < obj.rows; i++) + { + for (int j = 0; j < obj.cols; j++) + { + cv::Vec3b pixel = obj.at(i, j); + r.at(i, j) = pixel[2]; + g.at(i, j) = pixel[1]; + b.at(i, j) = pixel[0]; + } + } + return RGBChannel({r, g, b}); + } + + //תͼƬ45 + bool ColoredQrDecode::RotateImage(const cv::Mat &src, cv::Mat &dst, float angle) + { + if (src.empty()) + return false; + double alpha = -angle * CV_PI / 180.0;//convert angle to radian format + cv::Point2f srcP[3]; + cv::Point2f dstP[3]; + srcP[0] = cv::Point2f(0, (float) src.rows); + srcP[1] = cv::Point2f((float) src.cols, 0); + srcP[2] = cv::Point2f((float) src.cols, (float) src.rows); + //rotate the pixels + for (int i = 0; i < 3; i++) + dstP[i] = cv::Point2f(srcP[i].x * cos(alpha) - srcP[i].y * sin(alpha), + srcP[i].y * cos(alpha) + srcP[i].x * sin(alpha)); + double minx, miny, maxx, maxy; + minx = std::min(std::min(std::min(dstP[0].x, dstP[1].x), dstP[2].x), float(0.0)); + miny = std::min(std::min(std::min(dstP[0].y, dstP[1].y), dstP[2].y), float(0.0)); + maxx = std::max(std::max(std::max(dstP[0].x, dstP[1].x), dstP[2].x), float(0.0)); + maxy = std::max(std::max(std::max(dstP[0].y, dstP[1].y), dstP[2].y), float(0.0)); + int w = maxx - minx; + int h = maxy - miny; + //translation + for (int i = 0; i < 3; i++) + { + if (minx < 0) + dstP[i].x -= minx; + if (miny < 0) + dstP[i].y -= miny; + } + cv::Mat warpMat = cv::getAffineTransform(srcP, dstP); + cv::warpAffine(src, dst, warpMat, cv::Size(w, h), 1, 0, + cv::Scalar(255, 255, 255));//extend size + return true; + } + + //ʹõOpenCVзֶά + bool ColoredQrDecode::SplitMultipleQR(const cv::Mat &img, std::vector &rects) + { + uns_cv_export::QRCodeDetector qrcode; + std::vector corners; + if (!qrcode.detectMulti(img, corners)) + return false; + if (!corners.empty()) + { + if ((corners.size() % 4) != 0) + return false; + for (size_t i = 0; i < corners.size(); i += 4) + rects.push_back(cv::boundingRect( + std::vector(corners.begin() + i, corners.begin() + i + 4))); + return true; + } + else + return false; + } + + //ǷһάͼƬ + bool ColoredQrDecode::HasNextImage() + { + return qr_codes.HasNextCode(); + } + + //ȡһάͼƬ + SingleQRImage ColoredQrDecode::GetNextImage() + { + return SingleQRImage(source_image, split_channel_image, qr_codes.GetNextCode()); + } + + //ȡָɫĶά + SingleQRImage ColoredQrDecode::GetCodeByColor(QRCode::Color color) + { + qr_codes.ResetPointer(); + while (qr_codes.HasNextCode()) + { + QRCode code = qr_codes.GetNextCode(); + if (code.GetColor() == color) + { + qr_codes.ResetPointer(); + return SingleQRImage(source_image, split_channel_image, code); + } + } + qr_codes.ResetPointer(); + return SingleQRImage(); + } + + //зֲ洢ά + bool ColoredQrDecode::SplitQR(const cv::Mat &img, bool rotate_45) + { + std::vector rects; + if (rotate_45) + RotateImage(img, source_image, 45.00f); + else + img.copyTo(source_image); + qr_codes.Clear(); + split_channel_image = SplitRGBChannel(source_image); + SplitMultipleQR(split_channel_image[0], rects); + SplitMultipleQR(split_channel_image[1], rects); + SplitMultipleQR(split_channel_image[2], rects); + for (const auto &ele: rects) + qr_codes.Insert(QRCode(ele), source_image); + return (qr_codes.Size() != 0); + } + + //մ洢 + void ColoredQrDecode::Clear() + { + qr_codes.Clear(); + } +}; + +uns::ColoredQrDecode global_colored_qr_decoder; +uns::QRCode::Color global_current_qr_color = uns::QRCode::Color::Invalidate; + +//ͼ +extern "C" JNIEXPORT +jboolean JNICALL +Java_com_uns_maincar_cpp_1interface_ColoredQRDecoder_ProcessColoredQR(JNIEnv *env, jclass _this, + jobject image, + jboolean rotate) +{ + cv::Mat img_input; + if (!BitmapToMat(env, image, img_input)) + return false; + global_colored_qr_decoder.Clear(); + return global_colored_qr_decoder.SplitQR(img_input, rotate); +} + +//ļǷһά뺯 +extern "C" JNIEXPORT +jboolean JNICALL +Java_com_uns_maincar_cpp_1interface_ColoredQRDecoder_HasNextQR(JNIEnv *env, jclass _this) +{ + return global_colored_qr_decoder.HasNextImage(); +} + +//ĻȡһάͼƬ +extern "C" JNIEXPORT +jobject JNICALL +Java_com_uns_maincar_cpp_1interface_ColoredQRDecoder_GetNextQR(JNIEnv *env, jclass _this) +{ + uns::SingleQRImage qr_img = global_colored_qr_decoder.GetNextImage(); + global_current_qr_color = qr_img.GetColor(); + cv::Mat next = qr_img.GetBinaryImage(); + jobject bmp = GenerateBitmap(env, next.cols, next.rows); + MatToBitmap(env, next, bmp); + return bmp; +} + +//ĻȡǰصĶάɫ +extern "C" JNIEXPORT +jint JNICALL +Java_com_uns_maincar_cpp_1interface_ColoredQRDecoder_GetCurrentQRColor(JNIEnv *env, jclass _this) +{ + return (jint) global_current_qr_color; +} + +//ĸָɫȡά뺯 +extern "C" JNIEXPORT +jobject JNICALL +Java_com_uns_maincar_cpp_1interface_ColoredQRDecoder_GetSpecColorQR(JNIEnv *env, jclass _this, + jint color) +{ + cv::Mat next = global_colored_qr_decoder.GetCodeByColor( + (uns::QRCode::Color) color).GetBinaryImage(); + jobject bmp = GenerateBitmap(env, next.cols, next.rows); + MatToBitmap(env, next, bmp); + return bmp; +} + +//ǿպ +extern "C" JNIEXPORT +void JNICALL +Java_com_uns_maincar_cpp_1interface_ColoredQRDecoder_ForceClear(JNIEnv *env, jclass _this) +{ + global_colored_qr_decoder.Clear(); +} \ No newline at end of file diff --git a/app/src/main/cpp/colored_qr_detect/colored_qr_decode.h b/app/src/main/cpp/colored_qr_detect/colored_qr_decode.h new file mode 100644 index 0000000..275bf93 --- /dev/null +++ b/app/src/main/cpp/colored_qr_detect/colored_qr_decode.h @@ -0,0 +1,61 @@ +#include +#include +#include +#include "QRCodeSet.h" +#include "../opencv_support.h" +#include +#include "../opencv_libqr/cv_qrcode.h" +#include + +namespace uns +{ + using RGBChannel = std::array; + + class SingleQRImage + { + private: + cv::Mat binary; + cv::Mat colored; + QRCode::Color color; + public: + SingleQRImage(); + + SingleQRImage(const SingleQRImage &img); + + SingleQRImage(const cv::Mat &clr_src, const RGBChannel &rgb_src, const QRCode &code); + + public: + bool DataValidate() const; + + QRCode::Color GetColor() const; + + cv::Mat GetBinaryImage() const; + + cv::Mat GetColoredImage() const; + }; + + class ColoredQrDecode + { + private: + QRCodeSet qr_codes; + cv::Mat source_image; + RGBChannel split_channel_image; + private: + RGBChannel SplitRGBChannel(const cv::Mat &obj); + + bool RotateImage(const cv::Mat &src, cv::Mat &dst, float angle); + + bool SplitMultipleQR(const cv::Mat &img, std::vector &rects); + + public: + void Clear(); + + bool HasNextImage(); + + SingleQRImage GetNextImage(); + + SingleQRImage GetCodeByColor(QRCode::Color color); + + bool SplitQR(const cv::Mat &img, bool rotate_45 = false); + }; +}; \ No newline at end of file diff --git a/app/src/main/java/com/uns/maincar/constants/Commands.java b/app/src/main/java/com/uns/maincar/constants/Commands.java index 138cdef..c34aef3 100644 --- a/app/src/main/java/com/uns/maincar/constants/Commands.java +++ b/app/src/main/java/com/uns/maincar/constants/Commands.java @@ -61,6 +61,17 @@ public class Commands public static byte OCR_TEXT_DATA = (byte) 0xD9; public static byte OCR_TEXT_FINISH = (byte) 0xE9; + //车型 + public static byte VEHICLE_SUCCESS = (byte) 0xAA; + public static byte VEHICLE_FAILURE = (byte) 0xBA; + public static byte VEHICLE_TYPE_BIKE = (byte) 0x0A; + public static byte VEHICLE_TYPE_MOTOR = (byte) 0x0B; + public static byte VEHICLE_TYPE_CAR = (byte) 0x0C; + public static byte VEHICLE_TYPE_TRUCK = (byte) 0x0D; + public static byte VEHICLE_TYPE_VAN = (byte) 0x0E; + public static byte VEHICLE_TYPE_BUS = (byte) 0x0F; + + //运行指定任务 public static byte RUN_SINGE_TASK = (byte) 0xA0; public static byte TASK_NUMBER_0 = 0x00; @@ -98,4 +109,5 @@ public class Commands public static final byte RECEIVE_TRAFFIC_SIGN = (byte) 0xA6; public static final byte RECEIVE_TEXT_OCR = (byte) 0xA7; public static final byte RECEIVE_OCR_DATA_OK = (byte) 0xB7; + public static final byte RECEIVE_VEHICLE = (byte) 0xA8; } diff --git a/app/src/main/java/com/uns/maincar/cpp_interface/ColoredQRDecoder.java b/app/src/main/java/com/uns/maincar/cpp_interface/ColoredQRDecoder.java new file mode 100644 index 0000000..c349c98 --- /dev/null +++ b/app/src/main/java/com/uns/maincar/cpp_interface/ColoredQRDecoder.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2023. UnknownNetworkService Group + * This file is created by UnknownObject at 2023 - 6 - 16 + */ + +package com.uns.maincar.cpp_interface; + +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_DECODE_FAILED; +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_IMAGE_GENERATOR_FAILED; +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_NATIVE_LIBRARY_FAILED; +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_NO_CODE_DETECTED; +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_NO_MORE_CODE; + +import android.graphics.Bitmap; + +import com.uns.maincar.constants.GlobalColor; +import com.zxingcpp.BarcodeReader; + +public class ColoredQRDecoder +{ + static + { + System.loadLibrary("colored_qr_code"); + } + + public static final String ERR_COLOR_INVALIDATE = "QR: Invalid Color Parameter."; + + private static native void ForceClear(); + + private static native Bitmap GetNextQR(); + + private static native boolean HasNextQR(); + + private static native int GetCurrentQRColor(); + + private static native Bitmap GetSpecColorQR(int color); + + private static native boolean ProcessColoredQR(Bitmap image, boolean rotate); + + public static boolean BeginQRDecode(Bitmap img, boolean rotate) + { + return ProcessColoredQR(img, rotate); + } + + public static boolean HasNextCode() + { + return HasNextQR(); + } + + public static String DecodeNextQR() + { + BarcodeReader reader = new BarcodeReader(); + if (HasNextQR()) + { + Bitmap bmp = GetNextQR(); + if (bmp == null) + return ERR_IMAGE_GENERATOR_FAILED; + BarcodeReader.Result result = reader.read(bmp, QRDecoder.GetRect(bmp), 0); + if (result != null) + return result.getText(); + else + return ERR_DECODE_FAILED; + } + else + return ERR_NO_MORE_CODE; + } + + public static GlobalColor GetThisQRColor() + { + switch (GetCurrentQRColor()) + { + case 0: + return GlobalColor.RED; + case 1: + return GlobalColor.GREEN; + case 2: + return GlobalColor.YELLOW; + case 3: + return GlobalColor.BLUE; + } + return GlobalColor.INVALIDATE; + } + + public static String DecodeSpecColorQR(GlobalColor color) + { + int i_color = -1; + switch (color) + { + case RED: + i_color = 0; + break; + case GREEN: + i_color = 1; + break; + case YELLOW: + i_color = 2; + break; + case BLUE: + i_color = 3; + break; + } + if (i_color <= -1) + return ERR_COLOR_INVALIDATE; + BarcodeReader reader = new BarcodeReader(); + Bitmap bmp = GetSpecColorQR(i_color); + if (bmp == null) + return ERR_IMAGE_GENERATOR_FAILED; + BarcodeReader.Result result = reader.read(bmp, QRDecoder.GetRect(bmp), 0); + if (result != null) + return result.getText(); + else + return ERR_DECODE_FAILED; + } + + public static String SelfTest(Bitmap img) + { + if (!ProcessColoredQR(img, false)) + return ERR_NATIVE_LIBRARY_FAILED; + if (!HasNextQR()) + return ERR_NO_CODE_DETECTED; + String result = DecodeNextQR(); + ForceClear(); + return result; + } +} diff --git a/app/src/main/java/com/uns/maincar/cpp_interface/QRDecoder.java b/app/src/main/java/com/uns/maincar/cpp_interface/QRDecoder.java index 733ba17..aa2b98c 100644 --- a/app/src/main/java/com/uns/maincar/cpp_interface/QRDecoder.java +++ b/app/src/main/java/com/uns/maincar/cpp_interface/QRDecoder.java @@ -29,9 +29,9 @@ public class QRDecoder private static native Bitmap GetNextQR(); private static native boolean ProcessQR(Bitmap image); - private static Rect GetRect(Bitmap img) + protected static Rect GetRect(Bitmap img) { - return new Rect(0,0,img.getWidth(),img.getHeight()); + return new Rect(0, 0, img.getWidth(), img.getHeight()); } public static boolean BeginQRDecode(Bitmap img) diff --git a/app/src/main/java/com/uns/maincar/gui/DebugPage.java b/app/src/main/java/com/uns/maincar/gui/DebugPage.java index e34b1f9..4574479 100644 --- a/app/src/main/java/com/uns/maincar/gui/DebugPage.java +++ b/app/src/main/java/com/uns/maincar/gui/DebugPage.java @@ -15,6 +15,8 @@ import androidx.appcompat.app.AppCompatActivity; import com.uns.maincar.R; import com.uns.maincar.constants.Flags; +import java.util.Objects; + public class DebugPage extends AppCompatActivity { @@ -27,6 +29,8 @@ public class DebugPage extends AppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_debug_page); + Objects.requireNonNull(getSupportActionBar()).setTitle("主车远端服务程序 - 调试"); + if (Parent == null) { Toast.makeText(this, "Context is null!", Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/com/uns/maincar/gui/MainActivity.java b/app/src/main/java/com/uns/maincar/gui/MainActivity.java index 5542950..040bd90 100644 --- a/app/src/main/java/com/uns/maincar/gui/MainActivity.java +++ b/app/src/main/java/com/uns/maincar/gui/MainActivity.java @@ -5,6 +5,10 @@ package com.uns.maincar.gui; +import static com.uns.maincar.cpp_interface.ColoredQRDecoder.ERR_COLOR_INVALIDATE; +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_DECODE_FAILED; +import static com.uns.maincar.cpp_interface.QRDecoder.ERR_IMAGE_GENERATOR_FAILED; + import android.annotation.SuppressLint; import android.app.Activity; import android.content.Context; @@ -36,15 +40,14 @@ import com.uns.maincar.communication.WifiTransferCore; import com.uns.maincar.constants.Commands; import com.uns.maincar.constants.Flags; import com.uns.maincar.constants.Flags.TrafficLightColors; -import com.uns.maincar.constants.GlobalSignType; +import com.uns.maincar.constants.GlobalColor; import com.uns.maincar.cpp_interface.CarLicense; +import com.uns.maincar.cpp_interface.ColoredQRDecoder; import com.uns.maincar.cpp_interface.EnvTest; import com.uns.maincar.cpp_interface.MainCarAES; import com.uns.maincar.cpp_interface.OCR; import com.uns.maincar.cpp_interface.QRDecoder; -import com.uns.maincar.cpp_interface.ShapeColor; import com.uns.maincar.cpp_interface.TrafficLight; -import com.uns.maincar.cpp_interface.TrafficSign; import com.uns.maincar.data_type.ShapeColorResult; import com.uns.maincar.open_source.shape.ShapeDetector; import com.uns.maincar.open_source.traffic_sign.YoloV5_tfLite_TSDetector; @@ -173,7 +176,8 @@ public class MainActivity extends AppCompatActivity break; //收到QR指令,开始识别二维码,回传识别成功的数据 case Commands.RECEIVE_QR: - byte[] cmd = RecognizeQrCode(); + //默认情况下使用黑白二维码识别 + byte[] cmd = RecognizeQrCode(false, GlobalColor.INVALIDATE); if (cmd[2] == Commands.QR_FAILED) dtc_client.Send(cmd); else @@ -204,7 +208,9 @@ public class MainActivity extends AppCompatActivity case Commands.RECEIVE_TEXT_OCR: OCRRecognizeText(); break; - //收到未知指令,回传异常指令,表示无法解析当前指令 + case Commands.RECEIVE_VEHICLE: + dtc_client.Send(RecognizeVehicle()); + //收到未知指令,回传异常指令,表示无法解析当前指令 default: CommandEncoder error = new CommandEncoder(); dtc_client.Send(error.GenerateCommand(Commands.CMD_NOT_MATCH, (byte) 0x00, (byte) 0x00, (byte) 0x00)); @@ -249,6 +255,16 @@ public class MainActivity extends AppCompatActivity return MainCarAES.CalcAES(validate_result); } + //处理彩色二维码数据,使用从C++代码中导出的算法 + private byte[] ProcessColoredQRData(String qr_data) + { + CommandEncoder encoder = new CommandEncoder(); + if (qr_data.equals("")) + return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); + else + return MainCarAES.CalcAES(qr_data); + } + //获取程序自检指令,根据自检状态返回成功或失败 private byte[] SystemStatusCommand() { @@ -260,20 +276,39 @@ public class MainActivity extends AppCompatActivity } //识别二维码 - private byte[] RecognizeQrCode() + private byte[] RecognizeQrCode(boolean colored, GlobalColor target_color) { CommandEncoder encoder = new CommandEncoder(); - ArrayList qr_result = new ArrayList<>(); - if (!QRDecoder.BeginQRDecode(currImage)) - return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); - while (QRDecoder.HasNextCode()) - qr_result.add(QRDecoder.DecodeNextQR()); - if (qr_result.size() <= 0) - return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); + if (!colored) + { + ArrayList qr_result = new ArrayList<>(); + if (!QRDecoder.BeginQRDecode(currImage)) + return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); + while (QRDecoder.HasNextCode()) + qr_result.add(QRDecoder.DecodeNextQR()); + if (qr_result.size() <= 0) + return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); + else + { + qr_result.forEach(qr -> ToastLog(qr, false, true)); + return ProcessQRData(qr_result); + } + } else { - qr_result.forEach(qr -> ToastLog(qr, false, true)); - return ProcessQRData(qr_result); + boolean success = ColoredQRDecoder.BeginQRDecode(currImage, false); + if (!success) + success = ColoredQRDecoder.BeginQRDecode(currImage, true); + if (!success) + return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); + String result = ColoredQRDecoder.DecodeSpecColorQR(target_color); + if (Objects.equals(result, ERR_COLOR_INVALIDATE) || Objects.equals(result, ERR_IMAGE_GENERATOR_FAILED) || Objects.equals(result, ERR_DECODE_FAILED)) + return encoder.GenerateCommand(Commands.QR_FAILED, (byte) 0, (byte) 0, (byte) 0); + else + { + ToastLog(target_color + ":" + result, false, true); + return ProcessColoredQRData(result); + } } } @@ -300,54 +335,20 @@ public class MainActivity extends AppCompatActivity private byte[] RecognizeShapeColor() { CommandEncoder encoder = new CommandEncoder(); - if (!ShapeColor.RecognizeEverything(currImage)) + /*if (!ShapeColor.RecognizeEverything(currImage)) return encoder.GenerateCommand(Commands.COLOR_SHAPE_FAILED, (byte) 0, (byte) 0, (byte) 0); else { byte a = 0, b = 0, c = 0; - //测试用输出 - /*ToastLog("C-R-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.RED), false, false); - ToastLog("C-BL-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.BLACK), false, false); - ToastLog("C-G-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.GREEN), false, false); - ToastLog("C-BU-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.BLUE), false, false); - ToastLog("C-C-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.CYAN), false, false); - ToastLog("C-P-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.PURPLE), false, false); - ToastLog("C-W-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.WHITE), false, false); - ToastLog("C-Y-"+ShapeColor.LookupResult(GlobalShape.CIRCLE, GlobalColor.YELLOW), false, false); - ToastLog("S-R-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.RED), false, false); - ToastLog("S-BL-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.BLACK), false, false); - ToastLog("S-G-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.GREEN), false, false); - ToastLog("S-BU-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.BLUE), false, false); - ToastLog("S-C-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.CYAN), false, false); - ToastLog("S-P-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.PURPLE), false, false); - ToastLog("S-W-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.WHITE), false, false); - ToastLog("S-Y-"+ShapeColor.LookupResult(GlobalShape.STAR, GlobalColor.YELLOW), false, false); - ToastLog("s-R-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.RED), false, false); - ToastLog("s-BL-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.BLACK), false, false); - ToastLog("s-G-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.GREEN), false, false); - ToastLog("s-BU-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.BLUE), false, false); - ToastLog("s-C-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.CYAN), false, false); - ToastLog("s-P-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.PURPLE), false, false); - ToastLog("s-W-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.WHITE), false, false); - ToastLog("s-Y-"+ShapeColor.LookupResult(GlobalShape.SQUARE, GlobalColor.YELLOW), false, false); - ToastLog("R-R-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.RED), false, false); - ToastLog("R-BL-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.BLACK), false, false); - ToastLog("R-G-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.GREEN), false, false); - ToastLog("R-BU-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.BLUE), false, false); - ToastLog("R-C-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.CYAN), false, false); - ToastLog("R-P-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.PURPLE), false, false); - ToastLog("R-W-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.WHITE), false, false); - ToastLog("R-Y-"+ShapeColor.LookupResult(GlobalShape.RECTANGLE, GlobalColor.YELLOW), false, false); - ToastLog("T-R-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.RED), false, false); - ToastLog("T-BL-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.BLACK), false, false); - ToastLog("T-G-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.GREEN), false, false); - ToastLog("T-BU-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.BLUE), false, false); - ToastLog("T-C-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.CYAN), false, false); - ToastLog("T-P-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.PURPLE), false, false); - ToastLog("T-W-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.WHITE), false, false); - ToastLog("T-Y-"+ShapeColor.LookupResult(GlobalShape.TRIANGLE, GlobalColor.YELLOW), false, false);*/ + return encoder.GenerateCommand(Commands.COLOR_SHAPE_SUCCESS, a, b, c); - } + }*/ + //形状颜色识别改用开源AI模型 + ShapeDetector detector = new ShapeDetector(); + detector.shapePicProcess(currImage); + ShapeColorResult result = detector.GetAllResult(); + ToastLog(result.toString(), false, true); + return encoder.GenerateCommand(); } //识别车牌 @@ -401,7 +402,7 @@ public class MainActivity extends AppCompatActivity private byte[] RecognizeTrafficSign() { CommandEncoder encoder = new CommandEncoder(); - GlobalSignType type = TrafficSign.SignRecognize(currImage); + /*GlobalSignType type = TrafficSign.SignRecognize(currImage); if (type == GlobalSignType.Failure) return encoder.GenerateCommand(Commands.TRAFFIC_SIGN_FAILED, (byte) 0, (byte) 0, (byte) 0); else @@ -429,7 +430,11 @@ public class MainActivity extends AppCompatActivity break; } return encoder.GenerateCommand(); - } + }*/ + //交通标志改用开源AI模型识别 + String result = TS_Detector.processImage(currImage); + ToastLog("Traffic Sign: " + result, false, true); + return encoder.GenerateCommand(); } //识别静态文本 @@ -468,6 +473,16 @@ public class MainActivity extends AppCompatActivity } } + //识别车型 + private byte[] RecognizeVehicle() + { + CommandEncoder encoder = new CommandEncoder(); + //使用开源AI模型识别车型 + String result = VID_Detector.processImage(currImage); + ToastLog("Vehicle: " + result, false, true); + return encoder.GenerateCommand(); + } + //数组转字符串,仅用于调试输出 private String ByteArray2String(byte[] arr) { @@ -570,19 +585,47 @@ public class MainActivity extends AppCompatActivity context.findViewById(R.id.btn_start_qr).setOnClickListener(view -> { ToastLog("QR Code Started", false, false); - ToastLog("QR Result: " + ByteArray2String(RecognizeQrCode()), false, false); + //默认情况下使用黑白二维码识别 + ToastLog("QR Result: " + ByteArray2String(RecognizeQrCode(false, GlobalColor.INVALIDATE)), false, false); + context.finish(); + }); + + context.findViewById(R.id.btn_start_red_qr).setOnClickListener(view -> + { + ToastLog("QR Code Started (Red)", false, false); + //针对彩色二维码单独测试二维码识别 + ToastLog("QR (Red) Result: " + ByteArray2String(RecognizeQrCode(true, GlobalColor.RED)), false, false); + context.finish(); + }); + + context.findViewById(R.id.btn_start_green_qr).setOnClickListener(view -> + { + ToastLog("QR Code Started (Green)", false, false); + //针对彩色二维码单独测试二维码识别 + ToastLog("QR (Green) Result: " + ByteArray2String(RecognizeQrCode(true, GlobalColor.GREEN)), false, false); + context.finish(); + }); + + context.findViewById(R.id.btn_start_yellow_qr).setOnClickListener(view -> + { + ToastLog("QR Code Started (Yellow)", false, false); + //针对彩色二维码单独测试二维码识别 + ToastLog("QR (Yellow) Result: " + ByteArray2String(RecognizeQrCode(true, GlobalColor.YELLOW)), false, false); + context.finish(); }); context.findViewById(R.id.btn_start_light).setOnClickListener(view -> { ToastLog("Traffic Light Started", false, false); ToastLog("TL Result: " + ByteArray2String(RecognizeTrafficLight()), false, false); + context.finish(); }); context.findViewById(R.id.btn_start_color_shape).setOnClickListener(view -> { ToastLog("Color Shape Started", false, false); ToastLog("CS Result: " + ByteArray2String(RecognizeShapeColor()), false, false); + context.finish(); }); context.findViewById(R.id.btn_start_car_id).setOnClickListener(view -> @@ -591,12 +634,14 @@ public class MainActivity extends AppCompatActivity Thread th_debug = new Thread(this::RecognizeCarID); th_debug.start(); ToastLog("CID Finished", false, false); + context.finish(); }); context.findViewById(R.id.btn_start_sign).setOnClickListener(view -> { ToastLog("Traffic Sign Started", false, false); ToastLog("TS Result: " + ByteArray2String(RecognizeTrafficSign()), false, false); + context.finish(); }); context.findViewById(R.id.btn_start_ocr).setOnClickListener(view -> @@ -604,6 +649,7 @@ public class MainActivity extends AppCompatActivity ToastLog("OCR Started", false, false); Thread th_debug = new Thread(this::OCRRecognizeText); th_debug.start(); + context.finish(); }); context.findViewById(R.id.btn_tft_page_down).setOnClickListener(view -> @@ -611,6 +657,7 @@ public class MainActivity extends AppCompatActivity CommandEncoder encoder = new CommandEncoder(); dtc_client.ThreadSend(encoder.GenerateCommand(Commands.TFT_PAGE_DOWN, (byte) 0, (byte) 0, (byte) 0)); ToastLog("TFT Page Down Command Send.", false, true); + context.finish(); }); context.findViewById(R.id.btn_movement_control).setOnClickListener(view -> startActivity(new Intent(this, MovementController.class))); @@ -629,18 +676,21 @@ public class MainActivity extends AppCompatActivity detector.shapePicProcess(currImage); ShapeColorResult result = detector.GetAllResult(); ToastLog(result.toString(), false, false); + context.finish(); }); context.findViewById(R.id.btn_os_trafficsign).setOnClickListener(view -> { String res = TS_Detector.processImage(currImage); ToastLog("Traffic Sign Result: " + res, false, false); + context.finish(); }); context.findViewById(R.id.btn_os_vehicle).setOnClickListener(view -> { String res = TS_Detector.processImage(currImage); ToastLog("Vehicle Result: " + res, false, false); + context.finish(); }); } //----------------------------------------到此处终止---------------------------------------- diff --git a/app/src/main/java/com/uns/maincar/gui/PermissionGetter.java b/app/src/main/java/com/uns/maincar/gui/PermissionGetter.java index e81f475..453fefd 100644 --- a/app/src/main/java/com/uns/maincar/gui/PermissionGetter.java +++ b/app/src/main/java/com/uns/maincar/gui/PermissionGetter.java @@ -22,6 +22,8 @@ import androidx.core.content.ContextCompat; import com.uns.maincar.R; +import java.util.Objects; + //Android的存储权限的获取 public class PermissionGetter extends AppCompatActivity { @@ -72,6 +74,8 @@ public class PermissionGetter extends AppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_permission_getter); + Objects.requireNonNull(getSupportActionBar()).setTitle("主车远端服务程序 - 权限获取"); + GetExternalStoragePrivilege(); } diff --git a/app/src/main/java/com/uns/maincar/gui/RaceTasks.java b/app/src/main/java/com/uns/maincar/gui/RaceTasks.java index 0a43972..750d40d 100644 --- a/app/src/main/java/com/uns/maincar/gui/RaceTasks.java +++ b/app/src/main/java/com/uns/maincar/gui/RaceTasks.java @@ -16,6 +16,8 @@ import com.uns.maincar.R; import com.uns.maincar.constants.Commands; import com.uns.maincar.constants.Flags; +import java.util.Objects; + public class RaceTasks extends AppCompatActivity { @@ -49,6 +51,8 @@ public class RaceTasks extends AppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_race_tasks); + Objects.requireNonNull(getSupportActionBar()).setTitle("主车远端服务程序 - 比赛任务"); + if (Parent == null) { Toast.makeText(this, "Context is null!", Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/com/uns/maincar/gui/SingleFunctionTest.java b/app/src/main/java/com/uns/maincar/gui/SingleFunctionTest.java index 757fb41..b15078b 100644 --- a/app/src/main/java/com/uns/maincar/gui/SingleFunctionTest.java +++ b/app/src/main/java/com/uns/maincar/gui/SingleFunctionTest.java @@ -15,6 +15,8 @@ import androidx.appcompat.app.AppCompatActivity; import com.uns.maincar.R; import com.uns.maincar.constants.Flags; +import java.util.Objects; + public class SingleFunctionTest extends AppCompatActivity { @@ -27,6 +29,8 @@ public class SingleFunctionTest extends AppCompatActivity super.onCreate(savedInstanceState); setContentView(R.layout.activity_single_function_test); + Objects.requireNonNull(getSupportActionBar()).setTitle("主车远端服务程序 - 独立测试"); + if (Parent == null) { Toast.makeText(this, "Context is null!", Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/res/layout/activity_single_function_test.xml b/app/src/main/res/layout/activity_single_function_test.xml index 6366afe..ad9e885 100644 --- a/app/src/main/res/layout/activity_single_function_test.xml +++ b/app/src/main/res/layout/activity_single_function_test.xml @@ -215,4 +215,34 @@ android:layout_weight="1" android:text="开源\n车型识别" /> + + + +