import re, sys CPP = r'C:\Projects\ANLS\ANSLIB\ANSODEngine\ANSTENSORRTCL.cpp' with open(CPP, 'rb') as f: data = f.read() print('File size:', len(data)) CRLF = b'\r\n' in data print('CRLF:', CRLF) errors = [] def replace_once(data, old, new, label): count = data.count(old) if count != 1: errors.append(f'{label}: found {count}, expected 1') return data return data.replace(old, new) def replace_all(data, old, new, expected, label): count = data.count(old) if count != expected: errors.append(f'{label}: found {count}, expected {expected}') return data return data.replace(old, new) def replace_slice(data, start_anchor, end_anchor, new_content, label): si = data.find(start_anchor) if si == -1: errors.append(f'{label}: start anchor not found: {start_anchor[:60]}') return data ei = data.find(end_anchor, si + len(start_anchor)) if ei == -1: errors.append(f'{label}: end anchor not found: {end_anchor[:60]}') return data print(f' {label}: [{si}:{ei}] ({ei-si} bytes) -> {len(new_content)} bytes') return data[:si] + new_content + data[ei:] # ────────────────────────────────────────────────────────────────────────────── # Step 1: Add #include # ────────────────────────────────────────────────────────────────────────────── data = replace_once(data, b'#include \r\n', b'#include \r\n#include \r\n', 'Step 1: Add #include ') print('Step 1: done') # ────────────────────────────────────────────────────────────────────────────── # Step 2: buildLoadNetwork — add m_maxSlotsPerGpu (3 calls) # ────────────────────────────────────────────────────────────────────────────── data = replace_all(data, b'buildLoadNetwork(_modelFilePath, SUB_VALS, DIV_VALS, NORMALIZE);', b'buildLoadNetwork(_modelFilePath, SUB_VALS, DIV_VALS, NORMALIZE, m_maxSlotsPerGpu);', 3, 'Step 2: buildLoadNetwork add m_maxSlotsPerGpu') print('Step 2: done') # ────────────────────────────────────────────────────────────────────────────── # Step 2b: Remove _isFixedBatch = false; from LoadModel and LoadModelFromFolder # Use surrounding context to avoid matching inside 3-tab Initialize line # ────────────────────────────────────────────────────────────────────────────── data = replace_all(data, b'\t\t_isFixedBatch = false;\r\n\t\tstd::lock_guard', b'\t\tstd::lock_guard', 2, 'Step 2b: Remove _isFixedBatch=false in LoadModel/LoadModelFromFolder') print('Step 2b: done') # ────────────────────────────────────────────────────────────────────────────── # Step 2c: Remove _isFixedBatch = false; from Initialize (3-tab indent) # ────────────────────────────────────────────────────────────────────────────── data = replace_all(data, b'\t\t\t_isFixedBatch = false;\r\n', b'', 1, 'Step 2c: Remove _isFixedBatch=false in Initialize') print('Step 2c: done') # ────────────────────────────────────────────────────────────────────────────── # Step 3: RunInference(camera_id) — release mutex before DetectObjects # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::RunInference(const cv::Mat& inputImgBGR,const std::string& camera_id)\r\n', b'\tstd::vector> TENSORRTCL::RunInferencesBatch(', b'TENSORRTCL::RunInference(const cv::Mat& inputImgBGR,const std::string& camera_id)\r\n' b'\t{\r\n' b'\t\t// Validate state under brief lock\r\n' b'\t\t{\r\n' b'\t\t\tstd::lock_guard lock(_mutex);\r\n' b'\t\t\tif (!_modelLoadValid) {\r\n' b'\t\t\t\t_logger.LogError("TENSORRTCL::RunInference",\r\n' b'\t\t\t\t\t"Cannot load the TensorRT model. Please check if it exists",\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (!_licenseValid) {\r\n' b'\t\t\t\t_logger.LogError("TENSORRTCL::RunInference",\r\n' b'\t\t\t\t\t"Runtime license is not valid or expired. Please contact ANSCENTER",\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (!_isInitialized) {\r\n' b'\t\t\t\t_logger.LogError("TENSORRTCL::RunInference",\r\n' b'\t\t\t\t\t"Model is not initialized",\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (inputImgBGR.empty() || inputImgBGR.cols < 5 || inputImgBGR.rows < 5) {\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\t\t}\r\n' b'\t\ttry {\r\n' b'\t\t\treturn DetectObjects(inputImgBGR, camera_id);\r\n' b'\t\t}\r\n' b'\t\tcatch (const std::exception& e) {\r\n' b'\t\t\t_logger.LogFatal("TENSORRTCL::RunInference", e.what(), __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\t}\r\n' b'\t', 'Step 3: RunInference brief lock') print('Step 3: done') # ────────────────────────────────────────────────────────────────────────────── # Step 4: RunInferencesBatch — brief lock, remove _isFixedBatch fallback # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::RunInferencesBatch(const std::vector& inputs, const std::string& camera_id) {\r\n', b'\tTENSORRTCL::~TENSORRTCL() {', b'TENSORRTCL::RunInferencesBatch(const std::vector& inputs, const std::string& camera_id) {\r\n' b'\t\t// Validate state under brief lock\r\n' b'\t\t{\r\n' b'\t\t\tstd::lock_guard lock(_mutex);\r\n' b'\t\t\tif (!_modelLoadValid) {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::RunInferencesBatch",\r\n' b'\t\t\t\t\t"Cannot load the TensorRT model. Please check if it exists", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (!_licenseValid) {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::RunInferencesBatch",\r\n' b'\t\t\t\t\t"Runtime license is not valid or expired. Please contact ANSCENTER", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (!_isInitialized) {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::RunInferencesBatch",\r\n' b'\t\t\t\t\t"Engine not initialized", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (inputs.empty()) return {};\r\n' b'\t\t}\r\n' b'\t\ttry {\r\n' b'\t\t\treturn DetectObjectsBatch(inputs, camera_id);\r\n' b'\t\t}\r\n' b'\t\tcatch (const std::exception& e) {\r\n' b'\t\t\tthis->_logger.LogFatal("TENSORRTCL::RunInferencesBatch", e.what(), __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\t};\r\n' b'\t', 'Step 4: RunInferencesBatch brief lock') print('Step 4: done') # ────────────────────────────────────────────────────────────────────────────── # Step 5: DetectObjects — three-phase mutex # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::DetectObjects(const cv::Mat& inputImage, const std::string& camera_id) {\r\n', b'\tstd::vector> TENSORRTCL::Preprocess(', b'TENSORRTCL::DetectObjects(const cv::Mat& inputImage, const std::string& camera_id) {\r\n' b'\t\t// Phase 1: Preprocess under brief lock\r\n' b'\t\tImageMetadata meta;\r\n' b'\t\tstd::vector> input;\r\n' b'\t\t{\r\n' b'\t\t\tstd::lock_guard lock(_mutex);\r\n' b'\t\t\tinput = Preprocess(inputImage, meta);\r\n' b'\t\t}\r\n' b'\t\tif (input.empty()) return {};\r\n' b'\r\n' b'\t\t// Phase 2: Inference \xe2\x80\x94 mutex released; pool dispatches to idle GPU slot\r\n' b'\t\tstd::vector>> featureVectors;\r\n' b'\t\tauto succ = m_trtEngine->runInference(input, featureVectors);\r\n' b'\t\tif (!succ) {\r\n' b'\t\t\tthis->_logger.LogFatal("TENSORRTCL::DetectObjects", "Error running inference", __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\r\n' b'\t\t// Phase 3: Postprocess under brief lock\r\n' b'\t\tstd::lock_guard lock(_mutex);\r\n' b'\t\tstd::vector featureVector;\r\n' b'\t\tEngine::transformOutput(featureVectors, featureVector);\r\n' b'\t\treturn Postprocess(featureVector, camera_id, meta);\r\n' b'\t}\r\n' b'\t', 'Step 5: DetectObjects three-phase mutex') print('Step 5: done') # ────────────────────────────────────────────────────────────────────────────── # Step 6: Preprocess — add outMeta param, remove lock, replace member writes # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::Preprocess(const cv::Mat& inputImage) {\r\n', b'\tstd::vector TENSORRTCL::Postprocess(', b'TENSORRTCL::Preprocess(const cv::Mat& inputImage, ImageMetadata& outMeta) {\r\n' b'\t\ttry {\r\n' b'\t\t\tif (!_licenseValid) {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::Preprocess", "Invalid license", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif (inputImage.empty()) {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::Preprocess", "Input image is empty", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tif ((inputImage.cols < 5) || (inputImage.rows < 5)) {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::Preprocess",\r\n' b'\t\t\t\t\t"Input image is too small (Width: " + std::to_string(inputImage.cols) +\r\n' b'\t\t\t\t\t", Height: " + std::to_string(inputImage.rows) + ")",\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Populate the input vectors\r\n' b'\t\t\tconst auto& inputDims = m_trtEngine->getInputDims();\r\n' b'\t\t\tconst int inputH = inputDims[0].d[1];\r\n' b'\t\t\tconst int inputW = inputDims[0].d[2];\r\n' b'\r\n' b'\t\t\t// Upload the image to GPU memory\r\n' b'\t\t\tcv::cuda::Stream stream; // Create a custom stream\r\n' b'\t\t\tcv::cuda::GpuMat img;\r\n' b'\r\n' b'\t\t\tif (inputImage.channels() == 1) {\r\n' b'\t\t\t\t// Convert grayscale to 3-channel BGR before uploading\r\n' b'\t\t\t\tcv::Mat img3Channel;\r\n' b'\t\t\t\tcv::cvtColor(inputImage, img3Channel, cv::COLOR_GRAY2BGR);\r\n' b'\t\t\t\timg.upload(img3Channel, stream);\r\n' b'\t\t\t}\r\n' b'\t\t\telse {\r\n' b'\t\t\t\timg.upload(inputImage, stream);\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Convert BGR to RGB\r\n' b'\t\t\tcv::cuda::GpuMat imgRGB;\r\n' b'\t\t\tcv::cuda::cvtColor(img, imgRGB, cv::COLOR_BGR2RGB, 0, stream);\r\n' b'\t\t\tstream.waitForCompletion();\r\n' b'\r\n' b'\t\t\t// These parameters will be used in the post-processing stage\r\n' b'\t\t\toutMeta.imgHeight = imgRGB.rows;\r\n' b'\t\t\toutMeta.imgWidth = imgRGB.cols;\r\n' b'\r\n' b'\t\t\tif (outMeta.imgHeight <= 0 || outMeta.imgWidth <= 0) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::Preprocess", "Image height or width is zero", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tint maxImageSize = std::max(outMeta.imgHeight, outMeta.imgWidth);\r\n' b'\t\t\tcv::cuda::GpuMat processed = imgRGB;\r\n' b'\r\n' b'\t\t\tif (outMeta.imgHeight > 0 && outMeta.imgWidth > 0) {\r\n' b'\t\t\t\toutMeta.ratio = 1.f / std::min(inputDims[0].d[2] / static_cast(imgRGB.cols),\r\n' b'\t\t\t\t\tinputDims[0].d[1] / static_cast(imgRGB.rows));\r\n' b'\r\n' b'\t\t\t\tcv::cuda::GpuMat resized = imgRGB;\r\n' b'\r\n' b'\t\t\t\t// Resize to the model\'s expected input size while maintaining aspect ratio with padding\r\n' b'\t\t\t\tif (resized.rows != inputDims[0].d[1] || resized.cols != inputDims[0].d[2]) {\r\n' b'\t\t\t\t\tresized = Engine::resizeKeepAspectRatioPadRightBottom(imgRGB, inputDims[0].d[1], inputDims[0].d[2]);\r\n' b'\t\t\t\t}\r\n' b'\r\n' b'\t\t\t\t// Convert to format expected by our inference engine\r\n' b'\t\t\t\tstd::vector input{ std::move(resized) };\r\n' b'\t\t\t\tstd::vector> inputs{ std::move(input) };\r\n' b'\t\t\t\treturn inputs;\r\n' b'\t\t\t}\r\n' b'\t\t\telse {\r\n' b'\t\t\t\tthis->_logger.LogFatal("TENSORRTCL::Preprocess",\r\n' b'\t\t\t\t\t"Image height or width is zero after processing (Width: " + std::to_string(outMeta.imgWidth) +\r\n' b'\t\t\t\t\t", Height: " + std::to_string(outMeta.imgHeight) + ")",\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\t\t}\r\n' b'\t\tcatch (const std::exception& e) {\r\n' b'\t\t\tthis->_logger.LogFatal("TENSORRTCL::Preprocess", e.what(), __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\t}\r\n' b'\t', 'Step 6: Preprocess outMeta') print('Step 6: done') # ────────────────────────────────────────────────────────────────────────────── # Step 7: Postprocess — add meta param, remove lock, replace member refs # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::Postprocess(std::vector& featureVector, const std::string& camera_id) {\r\n', b'\tstd::vector> TENSORRTCL::DetectObjectsBatch(', b'TENSORRTCL::Postprocess(std::vector& featureVector, const std::string& camera_id, const ImageMetadata& meta) {\r\n' b'\t\tstd::vector outputs;\r\n' b'\t\ttry {\r\n' b'\t\t\tauto max_idx = std::max_element(featureVector.begin(), featureVector.end());\r\n' b'\t\t\tint class_id = static_cast(std::distance(featureVector.begin(), max_idx));\r\n' b'\t\t\tfloat score = *max_idx;\r\n' b'\t\t\tint classNameSize = _classes.size();\r\n' b'\t\t\tObject clsResult;\r\n' b'\t\t\tclsResult.classId = class_id;\r\n' b'\t\t\tif (!_classes.empty()) {\r\n' b'\t\t\t\tif (clsResult.classId < classNameSize) {\r\n' b'\t\t\t\t\tclsResult.className = _classes[clsResult.classId];\r\n' b'\t\t\t\t}\r\n' b'\t\t\t\telse {\r\n' b'\t\t\t\t\tclsResult.className = _classes[classNameSize - 1]; // Use last valid class name if out of range\r\n' b'\t\t\t\t}\r\n' b'\t\t\t}\r\n' b'\t\t\telse {\r\n' b'\t\t\t\tclsResult.className = "Unknown"; // Fallback if _classes is empty\r\n' b'\t\t\t}\r\n' b'\r\n' b'\r\n' b'\t\t\tclsResult.confidence = score;\r\n' b'\t\t\tif (meta.imgWidth > 20 && meta.imgHeight > 20) {\r\n' b'\t\t\t\tclsResult.box = cv::Rect(10, 10, meta.imgWidth - 20, meta.imgHeight - 20);\r\n' b'\t\t\t}\r\n' b'\t\t\telse {\r\n' b'\t\t\t\tclsResult.box = cv::Rect(0, 0, meta.imgWidth, meta.imgHeight);\r\n' b'\t\t\t}\r\n' b'\t\t\tclsResult.polygon = ANSUtilityHelper::RectToNormalizedPolygon(clsResult.box, meta.imgWidth, meta.imgHeight);\r\n' b'\t\t\tclsResult.cameraId = camera_id;\r\n' b'\t\t\toutputs.push_back(clsResult);\r\n' b'\t\t\treturn outputs;\r\n' b'\t\t\t//EnqueueDetection(objects, camera_id);\r\n' b'\t\t}\r\n' b'\t\tcatch (std::exception& e) {\r\n' b'\t\t\tthis->_logger.LogFatal("TENSORRTCL::Postproces", e.what(), __FILE__, __LINE__);\r\n' b'\t\t\treturn outputs;\r\n' b'\t\t}\r\n' b'\r\n' b'\t}\r\n' b'\t', 'Step 7: Postprocess meta') print('Step 7: done') # ────────────────────────────────────────────────────────────────────────────── # Step 8: DetectObjectsBatch — auto-split + parallel postprocess, no _isFixedBatch # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::DetectObjectsBatch(const std::vector& inputImages,const std::string& camera_id)\r\n', b'\tstd::vector> TENSORRTCL::PreprocessBatch(', b'TENSORRTCL::DetectObjectsBatch(const std::vector& inputImages, const std::string& camera_id)\r\n' b'\t{\r\n' b'\t\t// Validate under brief lock\r\n' b'\t\t{\r\n' b'\t\t\tstd::lock_guard lock(_mutex);\r\n' b'\t\t\tif (inputImages.empty()) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::DetectObjectsBatch",\r\n' b'\t\t\t\t\t"Empty input images vector", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\t\t}\r\n' b'\r\n' b'\t\t// Auto-split if batch exceeds engine capacity\r\n' b'\t\tconst int maxBatch = m_options.maxBatchSize > 0 ? m_options.maxBatchSize : 1;\r\n' b'\t\tif (static_cast(inputImages.size()) > maxBatch) {\r\n' b'\t\t\tconst size_t numImages = inputImages.size();\r\n' b'\t\t\tstd::vector> chunks;\r\n' b'\t\t\tfor (size_t start = 0; start < numImages; start += static_cast(maxBatch)) {\r\n' b'\t\t\t\tconst size_t end = std::min(start + static_cast(maxBatch), numImages);\r\n' b'\t\t\t\tchunks.emplace_back(inputImages.begin() + start, inputImages.begin() + end);\r\n' b'\t\t\t}\r\n' b'\t\t\tstd::vector>>> futures;\r\n' b'\t\t\tfutures.reserve(chunks.size());\r\n' b'\t\t\tfor (size_t i = 0; i < chunks.size(); ++i) {\r\n' b'\t\t\t\tfutures.push_back(std::async(std::launch::async,\r\n' b'\t\t\t\t\t[this, c = chunks[i], cid = camera_id]() {\r\n' b'\t\t\t\t\t\treturn DetectObjectsBatch(c, cid);\r\n' b'\t\t\t\t\t}));\r\n' b'\t\t\t}\r\n' b'\t\t\tstd::vector> allResults;\r\n' b'\t\t\tallResults.reserve(numImages);\r\n' b'\t\t\tfor (auto& fut : futures) {\r\n' b'\t\t\t\tauto chunkResults = fut.get();\r\n' b'\t\t\t\tfor (auto& r : chunkResults) allResults.push_back(std::move(r));\r\n' b'\t\t\t}\r\n' b'\t\t\treturn allResults;\r\n' b'\t\t}\r\n' b'\r\n' b'\t\t_logger.LogInfo("TENSORRTCL::DetectObjectsBatch",\r\n' b'\t\t\t"Processing batch of " + std::to_string(inputImages.size()) + " images",\r\n' b'\t\t\t__FILE__, __LINE__);\r\n' b'\r\n' b'\t\t// Phase 1: Preprocess under brief lock\r\n' b'\t\tBatchMetadata metadata;\r\n' b'\t\tstd::vector> inputs;\r\n' b'\t\t{\r\n' b'\t\t\tstd::lock_guard lock(_mutex);\r\n' b'\t\t\tinputs = PreprocessBatch(inputImages, metadata);\r\n' b'\t\t}\r\n' b'\t\tif (inputs.empty() || inputs[0].empty()) {\r\n' b'\t\t\t_logger.LogFatal("TENSORRTCL::DetectObjectsBatch",\r\n' b'\t\t\t\t"Preprocessing failed", __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\r\n' b'\t\t// Phase 2: Inference \xe2\x80\x94 mutex released; pool dispatches to idle GPU slot\r\n' b'\t\tstd::vector>> featureVectors;\r\n' b'\t\tbool succ = m_trtEngine->runInference(inputs, featureVectors);\r\n' b'\t\tif (!succ) {\r\n' b'\t\t\t_logger.LogFatal("TENSORRTCL::DetectObjectsBatch",\r\n' b'\t\t\t\t"Error running batch inference", __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\r\n' b'\t\t// Phase 3: Parallel postprocessing\r\n' b'\t\tconst size_t numBatch = featureVectors.size();\r\n' b'\t\tstd::vector> batchDetections(numBatch);\r\n' b'\t\tstd::vector>> postFutures;\r\n' b'\t\tpostFutures.reserve(numBatch);\r\n' b'\r\n' b'\t\tfor (size_t batchIdx = 0; batchIdx < numBatch; ++batchIdx) {\r\n' b'\t\t\tconst auto& batchOutput = featureVectors[batchIdx];\r\n' b'\t\t\tstd::vector fv = batchOutput.empty() ? std::vector{} : batchOutput[0];\r\n' b'\t\t\tpostFutures.push_back(std::async(std::launch::async,\r\n' b'\t\t\t\t[this, fv = std::move(fv), cid = camera_id, idx = batchIdx, &metadata]() mutable {\r\n' b'\t\t\t\t\treturn PostprocessBatch(fv, cid, idx, metadata);\r\n' b'\t\t\t\t}));\r\n' b'\t\t}\r\n' b'\t\tfor (size_t i = 0; i < numBatch; ++i)\r\n' b'\t\t\tbatchDetections[i] = postFutures[i].get();\r\n' b'\r\n' b'\t\t_logger.LogInfo("TENSORRTCL::DetectObjectsBatch",\r\n' b'\t\t\t"Batch processing complete. Images: " + std::to_string(numBatch),\r\n' b'\t\t\t__FILE__, __LINE__);\r\n' b'\t\treturn batchDetections;\r\n' b'\t}\r\n' b'\t', 'Step 8: DetectObjectsBatch rewrite') print('Step 8: done') # ────────────────────────────────────────────────────────────────────────────── # Step 9: PreprocessBatch — add outMetadata param, remove lock, rename members # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::PreprocessBatch(const std::vector& inputImages)\r\n', b'\tstd::vector TENSORRTCL::PostprocessBatch(', b'TENSORRTCL::PreprocessBatch(const std::vector& inputImages, BatchMetadata& outMetadata)\r\n' b'\t{\r\n' b'\t\ttry {\r\n' b'\t\t\t// Validate license\r\n' b'\t\t\tif (!_licenseValid) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\t\t"Invalid license", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Validate input\r\n' b'\t\t\tif (inputImages.empty()) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\t\t"Input images vector is empty", __FILE__, __LINE__);\r\n' b'\t\t\t\treturn {};\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tsize_t batchSize = inputImages.size();\r\n' b'\r\n' b'\t\t\t// Get model input dimensions\r\n' b'\t\t\tconst auto& inputDims = m_trtEngine->getInputDims();\r\n' b'\t\t\tconst int inputH = inputDims[0].d[1];\r\n' b'\t\t\tconst int inputW = inputDims[0].d[2];\r\n' b'\r\n' b'\t\t\t_logger.LogInfo("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\t"Preprocessing " + std::to_string(batchSize) + " images to " +\r\n' b'\t\t\t\tstd::to_string(inputW) + "x" + std::to_string(inputH),\r\n' b'\t\t\t\t__FILE__, __LINE__);\r\n' b'\r\n' b'\t\t\t// Create CUDA stream for async operations\r\n' b'\t\t\tcv::cuda::Stream stream;\r\n' b'\r\n' b'\t\t\t// Store ALL images in a SINGLE batch vector\r\n' b'\t\t\tstd::vector batchedImages;\r\n' b'\t\t\tbatchedImages.reserve(batchSize);\r\n' b'\r\n' b'\t\t\t// Store image dimensions for postprocessing\r\n' b'\t\t\toutMetadata.imgHeights.clear();\r\n' b'\t\t\toutMetadata.imgWidths.clear();\r\n' b'\t\t\toutMetadata.ratios.clear();\r\n' b'\t\t\toutMetadata.imgHeights.reserve(batchSize);\r\n' b'\t\t\toutMetadata.imgWidths.reserve(batchSize);\r\n' b'\t\t\toutMetadata.ratios.reserve(batchSize);\r\n' b'\r\n' b'\t\t\t// Process each image\r\n' b'\t\t\tfor (size_t i = 0; i < batchSize; ++i) {\r\n' b'\t\t\t\tconst cv::Mat& inputImage = inputImages[i];\r\n' b'\r\n' b'\t\t\t\t// Validate individual image\r\n' b'\t\t\t\tif (inputImage.empty()) {\r\n' b'\t\t\t\t\t_logger.LogFatal("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\t\t\t"Input image at index " + std::to_string(i) + " is empty",\r\n' b'\t\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\t\treturn {};\r\n' b'\t\t\t\t}\r\n' b'\r\n' b'\t\t\t\tif (inputImage.cols < 5 || inputImage.rows < 5) {\r\n' b'\t\t\t\t\t_logger.LogFatal("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\t\t\t"Image at index " + std::to_string(i) +\r\n' b'\t\t\t\t\t\t" is too small (Width: " + std::to_string(inputImage.cols) +\r\n' b'\t\t\t\t\t\t", Height: " + std::to_string(inputImage.rows) + ")",\r\n' b'\t\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\t\treturn {};\r\n' b'\t\t\t\t}\r\n' b'\r\n' b'\t\t\t\t// Upload to GPU\r\n' b'\t\t\t\tcv::cuda::GpuMat img;\r\n' b'\t\t\t\tif (inputImage.channels() == 1) {\r\n' b'\t\t\t\t\t// Convert grayscale to BGR\r\n' b'\t\t\t\t\tcv::Mat img3Channel;\r\n' b'\t\t\t\t\tcv::cvtColor(inputImage, img3Channel, cv::COLOR_GRAY2BGR);\r\n' b'\t\t\t\t\timg.upload(img3Channel, stream);\r\n' b'\t\t\t\t}\r\n' b'\t\t\t\telse {\r\n' b'\t\t\t\t\timg.upload(inputImage, stream);\r\n' b'\t\t\t\t}\r\n' b'\r\n' b'\t\t\t\t// Convert BGR to RGB\r\n' b'\t\t\t\tcv::cuda::GpuMat imgRGB;\r\n' b'\t\t\t\tcv::cuda::cvtColor(img, imgRGB, cv::COLOR_BGR2RGB, 0, stream);\r\n' b'\r\n' b'\t\t\t\t// Store original dimensions\r\n' b'\t\t\t\tint imgHeight = imgRGB.rows;\r\n' b'\t\t\t\tint imgWidth = imgRGB.cols;\r\n' b'\r\n' b'\t\t\t\tif (imgHeight <= 0 || imgWidth <= 0) {\r\n' b'\t\t\t\t\t_logger.LogFatal("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\t\t\t"Image at index " + std::to_string(i) + " has zero height or width",\r\n' b'\t\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\t\treturn {};\r\n' b'\t\t\t\t}\r\n' b'\r\n' b'\t\t\t\toutMetadata.imgHeights.push_back(imgHeight);\r\n' b'\t\t\t\toutMetadata.imgWidths.push_back(imgWidth);\r\n' b'\r\n' b'\t\t\t\t// Calculate resize ratio\r\n' b'\t\t\t\tfloat ratio = 1.f / std::min(\r\n' b'\t\t\t\t\tinputDims[0].d[2] / static_cast(imgRGB.cols),\r\n' b'\t\t\t\t\tinputDims[0].d[1] / static_cast(imgRGB.rows)\r\n' b'\t\t\t\t);\r\n' b'\t\t\t\toutMetadata.ratios.push_back(ratio);\r\n' b'\r\n' b'\t\t\t\t// Resize maintaining aspect ratio with padding\r\n' b'\t\t\t\tcv::cuda::GpuMat resized;\r\n' b'\t\t\t\tif (imgRGB.rows != inputDims[0].d[1] || imgRGB.cols != inputDims[0].d[2]) {\r\n' b'\t\t\t\t\tresized = Engine::resizeKeepAspectRatioPadRightBottom(\r\n' b'\t\t\t\t\t\timgRGB, inputDims[0].d[1], inputDims[0].d[2]\r\n' b'\t\t\t\t\t);\r\n' b'\t\t\t\t}\r\n' b'\t\t\t\telse {\r\n' b'\t\t\t\t\tresized = imgRGB;\r\n' b'\t\t\t\t}\r\n' b'\r\n' b'\t\t\t\t// Add to batch\r\n' b'\t\t\t\tbatchedImages.push_back(std::move(resized));\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Wait for all GPU operations to complete\r\n' b'\t\t\tstream.waitForCompletion();\r\n' b'\r\n' b'\t\t\t// Return as single batched input\r\n' b'\t\t\tstd::vector> result;\r\n' b'\t\t\tresult.push_back(std::move(batchedImages));\r\n' b'\r\n' b'\t\t\treturn result;\r\n' b'\t\t}\r\n' b'\t\tcatch (const std::exception& e) {\r\n' b'\t\t\t_logger.LogFatal("TENSORRTCL::PreprocessBatch",\r\n' b'\t\t\t\te.what(), __FILE__, __LINE__);\r\n' b'\t\t\treturn {};\r\n' b'\t\t}\r\n' b'\t}\r\n' b'\t', 'Step 9: PreprocessBatch outMetadata') print('Step 9: done') # ────────────────────────────────────────────────────────────────────────────── # Step 10: PostprocessBatch — add metadata param, remove lock, rename members # ────────────────────────────────────────────────────────────────────────────── data = replace_slice(data, b'TENSORRTCL::PostprocessBatch(std::vector& featureVector,const std::string& camera_id,size_t batchIdx)\r\n', b'\t\r\n}', b'TENSORRTCL::PostprocessBatch(std::vector& featureVector, const std::string& camera_id, size_t batchIdx, const BatchMetadata& metadata)\r\n' b'\t{\r\n' b'\t\tstd::vector outputs;\r\n' b'\r\n' b'\t\ttry {\r\n' b'\t\t\t// Validate batch index\r\n' b'\t\t\tif (batchIdx >= metadata.imgHeights.size() ||\r\n' b'\t\t\t\tbatchIdx >= metadata.imgWidths.size()) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::PostprocessBatch",\r\n' b'\t\t\t\t\t"Batch index " + std::to_string(batchIdx) +\r\n' b'\t\t\t\t\t" out of range (stored " + std::to_string(metadata.imgHeights.size()) + " images)",\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn outputs;\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Validate feature vector\r\n' b'\t\t\tif (featureVector.empty()) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::PostprocessBatch",\r\n' b'\t\t\t\t\t"Feature vector is empty for batch index " + std::to_string(batchIdx),\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn outputs;\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Get image dimensions for this batch index\r\n' b'\t\t\tint imgHeight = metadata.imgHeights[batchIdx];\r\n' b'\t\t\tint imgWidth = metadata.imgWidths[batchIdx];\r\n' b'\r\n' b'\t\t\t// Find max element (classification result)\r\n' b'\t\t\tauto max_idx = std::max_element(featureVector.begin(), featureVector.end());\r\n' b'\t\t\tif (max_idx == featureVector.end()) {\r\n' b'\t\t\t\t_logger.LogFatal("TENSORRTCL::PostprocessBatch",\r\n' b'\t\t\t\t\t"Failed to find max element in feature vector for batch index " +\r\n' b'\t\t\t\t\tstd::to_string(batchIdx),\r\n' b'\t\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\t\treturn outputs;\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tint class_id = static_cast(std::distance(featureVector.begin(), max_idx));\r\n' b'\t\t\tfloat score = *max_idx;\r\n' b'\r\n' b'\t\t\t// Create object result\r\n' b'\t\t\tObject clsResult;\r\n' b'\t\t\tclsResult.classId = class_id;\r\n' b'\r\n' b'\t\t\t// Get class name\r\n' b'\t\t\tint classNameSize = static_cast(_classes.size());\r\n' b'\t\t\tif (!_classes.empty()) {\r\n' b'\t\t\t\tif (class_id >= 0 && class_id < classNameSize) {\r\n' b'\t\t\t\t\tclsResult.className = _classes[class_id];\r\n' b'\t\t\t\t}\r\n' b'\t\t\t\telse {\r\n' b'\t\t\t\t\tclsResult.className = _classes[classNameSize - 1];\r\n' b'\r\n' b'\t\t\t\t}\r\n' b'\t\t\t}\r\n' b'\t\t\telse {\r\n' b'\t\t\t\tclsResult.className = "Unknown";\r\n' b'\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\tclsResult.confidence = score;\r\n' b'\r\n' b'\t\t\t// Create bounding box with margins\r\n' b'\t\t\tif (imgWidth > 20 && imgHeight > 20) {\r\n' b'\t\t\t\tclsResult.box = cv::Rect(10, 10, imgWidth - 20, imgHeight - 20);\r\n' b'\t\t\t}\r\n' b'\t\t\telse {\r\n' b'\t\t\t\tclsResult.box = cv::Rect(0, 0, imgWidth, imgHeight);\r\n' b'\t\t\t}\r\n' b'\r\n' b'\t\t\t// Convert to normalized polygon\r\n' b'\t\t\tclsResult.polygon = ANSUtilityHelper::RectToNormalizedPolygon(\r\n' b'\t\t\t\tclsResult.box, imgWidth, imgHeight\r\n' b'\t\t\t);\r\n' b'\r\n' b'\t\t\tclsResult.cameraId = camera_id;\r\n' b'\r\n' b'\t\t\toutputs.push_back(std::move(clsResult));\r\n' b'\r\n' b'\t\t\treturn outputs;\r\n' b'\t\t}\r\n' b'\t\tcatch (const std::exception& e) {\r\n' b'\t\t\t_logger.LogFatal("TENSORRTCL::PostprocessBatch",\r\n' b'\t\t\t\t"Error for batch index " + std::to_string(batchIdx) + ": " + e.what(),\r\n' b'\t\t\t\t__FILE__, __LINE__);\r\n' b'\t\t\treturn outputs;\r\n' b'\t\t}\r\n' b'\t}\r\n', 'Step 10: PostprocessBatch metadata') print('Step 10: done') # ────────────────────────────────────────────────────────────────────────────── # Verification: check no old member names remain # ────────────────────────────────────────────────────────────────────────────── stale = [b'm_ratio', b'm_imgWidth', b'm_imgHeight', b'm_batchImgHeights', b'm_batchImgWidths', b'm_batchRatios', b'_isFixedBatch'] for s in stale: count = data.count(s) if count > 0: errors.append(f'Stale member {s.decode()} still appears {count}x') # ────────────────────────────────────────────────────────────────────────────── # Result # ────────────────────────────────────────────────────────────────────────────── if errors: print('\nERRORS:') for e in errors: print(' ', e) print('\nFile NOT written.') sys.exit(1) else: with open(CPP, 'wb') as f: f.write(data) print(f'\nAll OK. File written ({len(data)} bytes).')