// Tencent is pleased to support the open source community by making ncnn available. // // Copyright (C) 2022 THL A29 Limited, a Tencent company. All rights reserved. // // Licensed under the BSD 3-Clause License (the "License"); you may not use this file except // in compliance with the License. You may obtain a copy of the License at // // https://opensource.org/licenses/BSD-3-Clause // // Unless required by applicable law or agreed to in writing, software distributed // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. #include "layer.h" #include "net.h" #include #include #include #include #include #include #include #include #include "math.h" #include #include #include "android/log.h" #include using namespace cv; using namespace std; std::vector prebLabel; std::vector alphabet; struct TextBlock { std::vector boxVertices; // 矩形框的顶点坐标 float angle; // 矩形框的角度 float boxScore; float blockTime; }; // 该函数计算二进制图像中指定轮廓的分数 float contourScore(cv::Mat& binary, std::vector& contour) { // 计算轮廓的边界矩形 Rect rect = boundingRect(contour); // 计算边界框在二进制图像中的有效范围 int xmin = max(rect.x, 0); int xmax = min(rect.x + rect.width, binary.cols - 1); int ymin = max(rect.y, 0); int ymax = min(rect.y + rect.height, binary.rows - 1); // 提取二进制图像中边界框的ROI(感兴趣区域) cv::Mat binROI = binary(Rect(xmin, ymin, xmax - xmin + 1, ymax - ymin + 1)); // 创建一个掩码,用于标识ROI中的像素 cv::Mat mask = cv::Mat::zeros(ymax - ymin + 1, xmax - xmin + 1, CV_8U); // 将轮廓中的点坐标调整为ROI内的坐标 std::vector roiContour; for (size_t i = 0; i < contour.size(); i++) { Point pt = Point(contour[i].x - xmin, contour[i].y - ymin); roiContour.push_back(pt); } // 使用填充多边形函数将ROI内的轮廓标记为1 std::vector> roiContours = { roiContour }; fillPoly(mask, roiContours, Scalar(1)); // 计算ROI内二进制图像的均值,以掩码为权重 float score = mean(binROI, mask).val[0]; return score; } void unclip(std::vector& inPoly, std::vector& outPoly) { float unclipRatio = 1.6; // 计算轮廓的面积 float area = contourArea(inPoly); float length = arcLength(inPoly, true); // 计算轮廓的周长 float distance = area * unclipRatio / length; // 计算解剪距离 // 获取输入轮廓的点数 size_t numPoints = inPoly.size(); // 存储新的轮廓线段 std::vector> newLines; // 遍历原始轮廓的每个点 for (size_t i = 0; i < numPoints; i++) { std::vector newLine; Point pt1 = inPoly[i]; Point pt2 = inPoly[(i - 1) % numPoints]; Point vec = pt1 - pt2; // 计算解剪距离 float unclipDis = (float)(distance / norm(vec)); // 计算旋转后的向量 Point2f rotateVec = Point2f(vec.y * unclipDis, -vec.x * unclipDis); // 添加旋转后的点到新线段 newLine.push_back(Point2f(pt1.x + rotateVec.x, pt1.y + rotateVec.y)); newLine.push_back(Point2f(pt2.x + rotateVec.x, pt2.y + rotateVec.y)); newLines.push_back(newLine); } // 获取新线段的数量 size_t numLines = newLines.size(); // 遍历新线段集合 for (size_t i = 0; i < numLines; i++) { Point2f a = newLines[i][0]; Point2f b = newLines[i][1]; Point2f c = newLines[(i + 1) % numLines][0]; Point2f d = newLines[(i + 1) % numLines][1]; Point2f pt; // 计算两向量的夹角余弦值 Point2f v1 = b - a; Point2f v2 = d - c; float cosAngle = (v1.x * v2.x + v1.y * v2.y) / (norm(v1) * norm(v2)); // 根据夹角余弦值判断旋转后的点位置 if (fabs(cosAngle) > 0.7) { pt.x = (b.x + c.x) * 0.5; pt.y = (b.y + c.y) * 0.5; } else { float denom = a.x * (float)(d.y - c.y) + b.x * (float)(c.y - d.y) + d.x * (float)(b.y - a.y) + c.x * (float)(a.y - b.y); float num = a.x * (float)(d.y - c.y) + c.x * (float)(a.y - d.y) + d.x * (float)(c.y - a.y); float s = num / denom; pt.x = a.x + s * (b.x - a.x); pt.y = a.y + s * (b.y - a.y); } // 将计算得到的点添加到输出轮廓 outPoly.push_back(pt); } } void GetTextBoxes(cv::Mat& binaryIN, cv::Mat& srcimgIN, std::vector &rsBoxes) { __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "begin get boxes"); float binaryThreshold = 0.3f; int maxCandidates = 1000; float polygonThreshold = 0.5; int longSideThresh = 3;//minBox 长边门限 // 获取图像的高度和宽度 int h = srcimgIN.rows; int w = srcimgIN.cols; // 二值化处理 Mat bitmap; threshold(binaryIN, bitmap, binaryThreshold, 255, THRESH_BINARY); //// 计算图像缩放比例 float scaleHeight = (float)(h) / (float)(binaryIN.size[0]); float scaleWidth = (float)(w) / (float)(binaryIN.size[1]); // 寻找轮廓 vector< vector > contours; bitmap.convertTo(bitmap, CV_8UC1); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "3 %d %d %d", bitmap.rows, bitmap.cols, bitmap.channels()); findContours(bitmap, contours, RETR_LIST, CHAIN_APPROX_SIMPLE); // 限制候选框的数量 size_t numCandidate = min(contours.size(), (size_t)(maxCandidates > 0 ? maxCandidates : INT_MAX)); vector confidences; // 遍历每个候选框 for (size_t i = 0; i < numCandidate; i++) { vector& contour = contours[i]; // 计算文本轮廓分数 float score = contourScore(binaryIN, contour); float boxScore = 0.0f; if (score < polygonThreshold) { boxScore = score; continue; } // 对轮廓进行缩放 vector contourScaled; contourScaled.reserve(contour.size()); for (size_t j = 0; j < contour.size(); j++) { contourScaled.push_back(Point(int(contour[j].x * scaleWidth), int(contour[j].y * scaleHeight))); } // 检查坐标是否有效 bool coordinatesValid = true; for (size_t j = 0; j < contourScaled.size(); j++) { if (contourScaled[j].x < 0 || contourScaled[j].y < 0 || contourScaled[j].x >= w || contourScaled[j].y >= h) { coordinatesValid = false; break; } } // 如果坐标有效,则处理该结果 if (coordinatesValid) { TextBlock detectedBox; // 解除裁剪 RotatedRect box = minAreaRect(contourScaled); float longSide = std::max(box.size.width, box.size.height); if (longSide < longSideThresh) { continue; } // minArea() rect is not normalized, it may return rectangles with angle=-90 or height < width const float angle_threshold = 60; // do not expect vertical text, TODO detection algo property bool swap_size = false; if (box.size.width < box.size.height) // horizontal-wide text area is expected swap_size = true; else if (fabs(box.angle) >= angle_threshold) // don't work with vertical rectangles swap_size = true; if (swap_size) { swap(box.size.width, box.size.height); if (box.angle < 0) box.angle += 90; else if (box.angle > 0) box.angle -= 90; } Point2f vertex[4]; box.points(vertex); // order: bl, tl, tr, br vector approx; for (int j = 0; j < 4; j++) approx.emplace_back(vertex[j]); vector polygon; unclip(approx, polygon); box = minAreaRect(polygon); longSide = std::max(box.size.width, box.size.height); if (longSide < longSideThresh + 2) { continue; } detectedBox.boxVertices = polygon; detectedBox.angle = box.angle; detectedBox.boxScore = boxScore; rsBoxes.push_back(detectedBox); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "box is (%f %f) (%f %f) (%f %f) (%f %f)", detectedBox.boxVertices[0].x, detectedBox.boxVertices[0].y, detectedBox.boxVertices[1].x, detectedBox.boxVertices[1].y, detectedBox.boxVertices[2].x, detectedBox.boxVertices[2].y, detectedBox.boxVertices[3].x, detectedBox.boxVertices[3].y); } } confidences = vector(contours.size(), 1.0f); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "finish run detect %d", rsBoxes.size()); // 对 results 进行倒序处理 std::reverse(rsBoxes.begin(), rsBoxes.end()); } // 该函数用于对输入图像进行预处理,包括颜色空间转换和图像缩放 cv::Mat preprocess(cv::Mat srcimg) { int shortSize = 736; cv::Mat dstimg; // 将输入图像从BGR颜色空间转换为RGB颜色空间 cvtColor(srcimg, dstimg, COLOR_BGR2RGB); int h = srcimg.rows; int w = srcimg.cols; // 初始化高度和宽度的缩放比例 float scale_h = 1; float scale_w = 1; // 根据图像的高度和宽度选择缩放比例 if (h < w) { // 如果图像高度小于宽度 计算高度缩放比例 scale_h = (float)shortSize / (float)h; float tar_w = (float)w * scale_h; tar_w = tar_w - (int)tar_w % 32; tar_w = max((float)32, tar_w); scale_w = tar_w / (float)w; } else { // 如果图像宽度小于等于高度 计算宽度缩放比例 scale_w = (float)shortSize / (float)w; float tar_h = (float)h * scale_w; tar_h = tar_h - (int)tar_h % 32; tar_h = max((float)32, tar_h); scale_h = tar_h / (float)h; } // 使用线性插值对图像进行缩放,以调整到目标尺寸 resize(dstimg, dstimg, Size(int(scale_w * dstimg.cols), int(scale_h * dstimg.rows)), INTER_LINEAR); return dstimg; } std::vector normalize_(cv::Mat img) { float meanValues[3] = { 0.485, 0.456, 0.406 }; float normValues[3] = { 0.229, 0.224, 0.225 }; std::vector input_image; // img.convertTo(img, CV_32F); int row = img.rows; int col = img.cols; input_image.resize(row * col * img.channels()); for (int c = 0; c < 3; c++) { for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { float pix = img.ptr(i)[j * 3 + c]; input_image[c * row * col + i * col + j] = (pix / 255.0 - meanValues[c]) / normValues[c]; } } } return input_image; } cv::Mat getRotateCropImage(cv::Mat& frame, std::vector vertices) { // 计算包围轮廓的最小矩形 Rect rect = boundingRect(cv::Mat(vertices)); // 从原始图像中提取感兴趣区域(ROI) cv::Mat crop_img = frame(rect); // 设置输出图像的大小为矩形的宽度和高度 const Size outputSize = Size(rect.width, rect.height); // 定义目标矩形的四个顶点坐标 std::vector targetVertices{ Point2f(0, outputSize.height), Point2f(0, 0), Point2f(outputSize.width, 0), Point2f(outputSize.width, outputSize.height) }; // 将原始轮廓的顶点坐标调整为在裁剪后的图像中的坐标 for (int i = 0; i < 4; i++) { vertices[i].x -= rect.x; vertices[i].y -= rect.y; } // 计算透视变换矩阵,将原始轮廓映射到目标矩形 //Mat rotationcv::Matrix = getPerspectiveTransform(vertices, targetVertices); cv::Mat rotationMatrix = getPerspectiveTransform(vertices, targetVertices); // 应用透视变换,旋转和裁剪原始图像的感兴趣区域 Mat result; //warpPerspective(crop_img, result, rotationcv::Matrix, outputSize, cv::BORDER_REPLICATE); warpPerspective(crop_img, result, rotationMatrix, outputSize, cv::BORDER_REPLICATE); return result; } string PostProcess(int wIn, int hIn, float* pdataIn) { int i = 0, j = 0; // 遍历输出,获取每列的最大值的索引作为标签 for (i = 0; i < wIn; i++) { int one_label_idx = 0; float max_data = -10000; for (j = 0; j < hIn; j++) { float data_ = pdataIn[i * hIn + j]; if (data_ > max_data) { max_data = data_; one_label_idx = j; } } prebLabel[i] = one_label_idx; } // 存储去重后的非空白标签 std::vector no_repeat_blank_label; for (size_t elementIndex = 0; elementIndex < wIn; ++elementIndex) { if (prebLabel[elementIndex] != 0 && !(elementIndex > 0 && prebLabel[elementIndex - 1] == prebLabel[elementIndex])) { no_repeat_blank_label.push_back(prebLabel[elementIndex] - 1); } } // 构建最终的预测文本 int len_s = no_repeat_blank_label.size(); std::string plate_text; // todo for (i = 0; i < len_s; i++) { plate_text += alphabet[no_repeat_blank_label[i]]; } return plate_text; } int inpWidth = 320; int inpHeight = 48; //cv::Mat preprocessRec(cv::Mat srcimg) //{ // cv::Mat dstimg; // int h = srcimg.rows; // int w = srcimg.cols; // const float ratio = w / float(h); // int resized_w = int(ceil((float)inpHeight * ratio)); // if (ceil(inpHeight * ratio) > inpWidth) // { // resized_w = inpWidth; // } // // resize(srcimg, dstimg, Size(resized_w, inpHeight), INTER_LINEAR); // return dstimg; //} ////std::vector normalizeRec(cv::Mat img) ////{ //// //// std::vector input_image; //// // img.convertTo(img, CV_32F); //// int row = img.rows; //// int col = img.cols; //// input_image.resize(inpHeight * inpWidth * img.channels()); //// for (int c = 0; c < 3; c++) //// { //// for (int i = 0; i < row; i++) //// { //// for (int j = 0; j < inpWidth; j++) //// { //// if (j < col) //// { //// float pix = img.ptr(i)[j * 3 + c]; //// //input_image[c * row * inpWidth + i * inpWidth + j] = (pix / 255.0 - 0.5) / 0.5; //// input_image[c * row * inpWidth + i * inpWidth + j] = (pix); //// } //// else //// { //// input_image[c * row * inpWidth + i * inpWidth + j] = 0; //// } //// } //// } //// } //// return input_image; ////} //struct TextBox { // std::vector boxPoint; // float score; // std::string text; //}; //bool compareBoxWidth(const TextBox& a, const TextBox& b) //{ // return abs(a.boxPoint[0].x - a.boxPoint[1].x) > abs(b.boxPoint[0].x - b.boxPoint[1].x); //} //cv::Mat getRotateCropImage2(const cv::Mat& src, std::vector box) { // cv::Mat image; // src.copyTo(image); // std::vector points = box; // // int collectX[4] = { box[0].x, box[1].x, box[2].x, box[3].x }; // int collectY[4] = { box[0].y, box[1].y, box[2].y, box[3].y }; // int left = int(*std::min_element(collectX, collectX + 4)); // int right = int(*std::max_element(collectX, collectX + 4)); // int top = int(*std::min_element(collectY, collectY + 4)); // int bottom = int(*std::max_element(collectY, collectY + 4)); // // cv::Mat imgCrop; // image(cv::Rect(left, top, right - left, bottom - top)).copyTo(imgCrop); // // for (int i = 0; i < points.size(); i++) { // points[i].x -= left; // points[i].y -= top; // } // // // int imgCropWidth = int(sqrt(pow(points[0].x - points[1].x, 2) + // pow(points[0].y - points[1].y, 2))); // int imgCropHeight = int(sqrt(pow(points[0].x - points[3].x, 2) + // pow(points[0].y - points[3].y, 2))); // // cv::Point2f ptsDst[4]; // ptsDst[0] = cv::Point2f(0., 0.); // ptsDst[1] = cv::Point2f(imgCropWidth, 0.); // ptsDst[2] = cv::Point2f(imgCropWidth, imgCropHeight); // ptsDst[3] = cv::Point2f(0.f, imgCropHeight); // // cv::Point2f ptsSrc[4]; // ptsSrc[0] = cv::Point2f(points[0].x, points[0].y); // ptsSrc[1] = cv::Point2f(points[1].x, points[1].y); // ptsSrc[2] = cv::Point2f(points[2].x, points[2].y); // ptsSrc[3] = cv::Point2f(points[3].x, points[3].y); // // cv::Mat M = cv::getPerspectiveTransform(ptsSrc, ptsDst); // // cv::Mat partImg; // cv::warpPerspective(imgCrop, partImg, M, // cv::Size(imgCropWidth, imgCropHeight), // cv::BORDER_REPLICATE); // // if (float(partImg.rows) >= float(partImg.cols) * 1.5) { // cv::Mat srcCopy = cv::Mat(partImg.rows, partImg.cols, partImg.depth()); // cv::transpose(partImg, srcCopy); // cv::flip(srcCopy, srcCopy, 0); // return srcCopy; // } // else { // return partImg; // } //} //std::vector getPartImages(const cv::Mat& src, std::vector& textBoxes) //{ // std::sort(textBoxes.begin(), textBoxes.end(), compareBoxWidth); // std::vector partImages; // if (textBoxes.size() > 0) // { // for (int i = 0; i < textBoxes.size(); ++i) // { // cv::Mat partImg = getRotateCropImage2(src, textBoxes[i].boxPoint); // partImages.emplace_back(partImg); // } // } // // return partImages; //} //struct TextLine { // std::string text; // std::vector charScores; //}; // //template //inline static size_t argmax(ForwardIterator first, ForwardIterator last) { // return std::distance(first, std::max_element(first, last)); //} // //TextLine scoreToTextLine(const std::vector& outputData, int h, int w) //{ // int keySize = alphabet.size(); // std::string strRes; // std::vector scores; // int lastIndex = 0; // int maxIndex; // float maxValue; // // for (int i = 0; i < h; i++) // { // maxIndex = 0; // maxValue = -1000.f; // // maxIndex = int(argmax(outputData.begin() + i * w, outputData.begin() + i * w + w)); // maxValue = float(*std::max_element(outputData.begin() + i * w, outputData.begin() + i * w + w));// / partition; // if (maxIndex > 0 && maxIndex < keySize && (!(i > 0 && maxIndex == lastIndex))) { // scores.emplace_back(maxValue); // strRes.append(alphabet[maxIndex - 1]); // } // lastIndex = maxIndex; // } // return { strRes, scores }; //} static int ocrProcess(ncnn::Net* dectModel, ncnn::Net* recModel, const cv::Mat& bgr, std::vector& textList) { __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "start"); prebLabel.clear(); const float prob_threshold = 0.25f; const float nms_threshold = 0.45f; int img_w = bgr.cols; int img_h = bgr.rows; cv::Mat dstimg = preprocess(bgr); float pix = dstimg.ptr(200)[200 * 3 + 1]; std::vectorresult = normalize_(dstimg); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "(200, 200, 1))%f ", pix); ncnn::Mat in_pad(dstimg.cols, dstimg.rows, 3, (void*)result.data()); ncnn::Extractor dect = dectModel->create_extractor(); dect.input("x", in_pad); ncnn::Mat out; dect.extract("sigmoid_0.tmp_0", out); Mat binary(out.h, out.w, CV_32FC1); memcpy(binary.data, out.data, out.w* out.h * sizeof(float)); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "dect out %d %d ", out.w , out.h); std::vector textBlocks; GetTextBoxes(binary, const_cast(bgr), textBlocks); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "finish get boxes"); for (size_t i = 0; i < textBlocks.size(); i++) { cv::Mat textimg = getRotateCropImage(const_cast(bgr), textBlocks[i].boxVertices); const float mean_vals[3] = { 127.5, 127.5, 127.5 }; const float norm_vals[3] = { 1.0 / 127.5, 1.0 / 127.5, 1.0 / 127.5 }; float scale = (float)inpHeight / (float)textimg.rows; int dstWidth = int((float)textimg.cols * scale); cv::Mat srcResize; cv::resize(textimg, srcResize, cv::Size(dstWidth, inpHeight)); //if you use PP-OCRv3 you should change PIXEL_RGB to PIXEL_RGB2BGR ncnn::Mat input = ncnn::Mat::from_pixels(srcResize.data, ncnn::Mat::PIXEL_RGB2BGR, srcResize.cols, srcResize.rows); input.substract_mean_normalize(mean_vals, norm_vals); ncnn::Mat in_pack3; ncnn::convert_packing(input, in_pack3, 3); cv::Mat aa(input.h, input.w, CV_32FC3); memcpy((uchar*)aa.data, in_pack3.data, input.w * input.h * 3 * sizeof(float)); ncnn::Extractor rec = recModel->create_extractor(); rec.input("input", input); ncnn::Mat out; rec.extract("out", out); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "finish run rec, out %d %d", out.w, out.h); float* floatArray = (float*)out.data; std::vector outputData(floatArray, floatArray + out.h * out.w); string textResult = ""; // 存储预测的标签 prebLabel.resize(out.h); string text = PostProcess(out.h, out.w, floatArray); // 删除标点符号和空格 text.erase(std::remove_if(text.begin(), text.end(), [](unsigned char c) { return std::ispunct(c) || std::isspace(c); }), text.end()); // 如果文本内容不为空,则处理结果 if (!text.empty()) { textResult = text; } textList.push_back(textResult); } for (int i=0; i< textBlocks.size(); ++i) { textBlocks[i].boxVertices.clear(); } textBlocks.clear(); textBlocks.shrink_to_fit(); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "End"); return 0; } extern "C" void CreateNet(void** detectModel, void** recModel, char* decBinMem, char* decParamMem, char* recBinMem, char* recParamMem, const void* modelDataKeys, size_t modelDataLengthKeys) { const auto resultDetect = new ncnn::Net; *detectModel = resultDetect; const auto resultDetectRec = new ncnn::Net; *recModel = resultDetectRec; int ret[4] = {-2}; const unsigned char* dectBinMemP = reinterpret_cast(decBinMem); ncnn::DataReaderFromMemory drDect(dectBinMemP); const unsigned char* recBinMemP = reinterpret_cast(recBinMem); ncnn::DataReaderFromMemory drRec(recBinMemP); resultDetect->opt.use_vulkan_compute = false; ret[0] = resultDetect->load_param_mem(decParamMem); ret[1] = resultDetect->load_model(drDect); ret[2] = resultDetectRec->load_param_mem(recParamMem); ret[3] = resultDetectRec->load_model(drRec); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "LoadDectBin %d %d %d %d", ret[0], ret[1], ret[2], ret[3]); //string keys_path = keysName; //ifstream ifs(keys_path); //std::string line; //while (getline(ifs, line)) //{ // alphabet.push_back(line); //} //alphabet.push_back(" "); // 将字节数据转换为字符串 std::string text(reinterpret_cast(modelDataKeys), modelDataLengthKeys); // 使用字符串流处理字符串 std::istringstream iss(text); std::string line; // 逐行读取并添加到 alphabet 中 while (std::getline(iss, line)) { alphabet.push_back(line); } alphabet.push_back(" "); } extern "C" void FreeNet(void* detectModel, void* recModel) { const auto yolo = static_cast(detectModel); yolo->clear(); const auto rec = static_cast(recModel); rec->clear(); delete yolo; delete rec; } typedef struct _StructStringInfo { char* result; int bufferSize; }StructStringInfo; extern "C" void Test(void* detectModel, void* recModel, unsigned char* input, int iw, int ih, StructStringInfo*& ob, int* cnt) { ncnn::Net* dect = static_cast(detectModel); ncnn::Net* rec = static_cast(recModel); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "image in"); cv::Mat m = cv::Mat(ih, iw, CV_8UC4, (void*)input); cvtColor(m, m, cv::COLOR_RGBA2BGR); __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "image cvt out"); // 获取图像在指定坐标(x, y)处的像素值 cv::Vec3b pixel = m.at(100, 100); // 输出像素值到Android Logcat __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "Pixel value at (%d, %d): B=%d, G=%d, R=%d", 100, 100, pixel[0], pixel[1], pixel[2]); // 获取图像在指定坐标(x, y)处的像素值 pixel = m.at(200, 200); // 输出像素值到Android Logcat __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "Pixel value at (%d, %d): B=%d, G=%d, R=%d", 200, 200, pixel[0], pixel[1], pixel[2]); // 获取图像在指定坐标(x, y)处的像素值 pixel = m.at(300, 300); // 输出像素值到Android Logcat __android_log_print(ANDROID_LOG_INFO, "EvaluateOneImage", "Pixel value at (%d, %d): B=%d, G=%d, R=%d", 300, 300, pixel[0], pixel[1], pixel[2]); std::vector textLists; ocrProcess(dect, rec, m, textLists); int count = static_cast(textLists.size()); if(count > 100) { count = 100; } *cnt = count; ob = new StructStringInfo[count]; for (int i=0; i< count; ++i) { ob[i].result = new char[textLists[i].length()+1]; std::copy(textLists[i].begin(), textLists[i].end(), ob[i].result); ob[i].result[textLists[i].length()] = '\0'; // 添加null终止符 } textLists.clear(); }