|
|
1. tensorrt를 사용하여 추론시간 개선 (기존 33ms~40ms)->(16ms~20ms)
작업 환경
wsl2 :ubuntu 22.04
GPU 사양:RTX3070
CUDA 버전:11.8
cudnn버전:8.9.7
opencv버전:4.9.0
모델 DeeplabV3
TensorRT 를 사용해 Segmentation 추론수행 코드 참고 github
https://github.com/yannTrm/DeepLabV3-TensorRT-Segmentation
헤더 파일 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | #ImageProcessor.hpp #pragma once #include <string> #include <opencv2/opencv.hpp> class ImageProcessor { public: // 이미지 파일 경로를 인자로 받는 기존 함수 //static bool preprocessImage(const std::string& imageFile, float* buffer, int inputH, int inputW); //static void createSegmentationMask(float* outputBuffer, int inputH, int inputW, int numClasses); // cv::Mat 객체를 인자로 받는 새 함수 static bool preprocessImage(const cv::Mat& image, float* buffer, int inputH, int inputW); // cv::Mat 타입의 마스크를 반환하도록 수정된 함수 static cv::Mat createSegmentationMask(float* outputBuffer, int inputH, int inputW, int numClasses); private: // 후처리 함수에서 사용할 소프트맥스 함수 static void softmax(float* input, int size); }; #TensorRTEngine.hpp #pragma once #include <string> #include <vector> #include "NvInfer.h" #include "NvOnnxParser.h" #include <chrono> #include <opencv2/opencv.hpp> #include <filesystem> // C++17에서 std::filesystem 사용을 위한 헤더 #include <fstream> class Logger : public nvinfer1::ILogger { public: void log(nvinfer1::ILogger::Severity severity, const char* msg) noexcept override; }; class TensorRTEngine { public: TensorRTEngine(); ~TensorRTEngine(); // 엔진을 생성하는 멤버함수 bool buildEngine(const std::string& onnxFile, const std::string& engineFileName); // 생성된 엔진으로 추론을 수행하는 멤버함수 //bool runInference(const std::string& imageFile); //동영상으로 추론을 진행하는 멤버함수 원본 //bool runInference(cv::Mat& frame,cv::Mat& result); //비동기 추론 모드함수 bool runInference(cv::Mat& frame, cv::Mat& result, cudaStream_t stream); bool processVideo(const std::string& videoFile);//동영상 불러오기 private: Logger logger; nvinfer1::IRuntime* runtime; nvinfer1::ICudaEngine* engine; nvinfer1::IExecutionContext* context; std::vector<void*> buffers; std::vector<int64_t> bufferSizes; int inputH, inputW, numClasses; void readFile(const std::string& fileName, std::vector<char>& buffer); void cleanup(); }; | cs |
소스 파일 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | #ImageProcessor.cpp #include "my_ImageProcessor.hpp" #include <iostream> #include <algorithm> #include <cmath> #include <vector> // 소프트맥스 함수: 입력 배열을 확률로 변환하는 함수 void ImageProcessor::softmax(float* input, int size) { float max = *std::max_element(input, input + size); // 입력 배열에서 최대값을 찾아서 변수 max에 저장 float sum = 0; // 소프트맥스 계산을 위한 합 변수 초기화 for (int i = 0; i < size; i++) { // 입력 배열의 각 원소에 대해 반복 input[i] = std::exp(input[i] - max); // 입력 값에서 최대값을 뺀 후 지수 함수 적용 sum += input[i]; // 지수 함수 결과를 sum에 추가하여 합 계산 } for (int i = 0; i < size; i++) { // 다시 입력 배열의 각 원소에 대해 반복 input[i] /= sum; // 각 원소를 합으로 나누어 확률로 변환 } } // 새로 추가된 cv::Mat 객체를 인자로 받는 함수 정의 bool ImageProcessor::preprocessImage(const cv::Mat& image, float* buffer, int inputH, int inputW) { if (image.empty()) { std::cerr << "Image is empty." << std::endl; return false; } //cv::imshow("src",image); //cv::waitKey(0); // 입력 크기가 0이 아닌지 확인 if (inputW <= 0 || inputH <= 0) { std::cerr << "Invalid input dimensions: " << inputW << "x" << inputH << std::endl; return false; } cv::Mat resized; cv::resize(image, resized, cv::Size(inputW, inputH)); // 이미지 크기 조정 resized.convertTo(resized, CV_32FC3, 1.0f / 255.0f); // 0-255 범위를 0-1로 정규화 //cv::imshow("src",resized); //cv::waitKey(0); std::vector<cv::Mat> channels(3); cv::split(resized, channels); for (int i = 0; i < 3; ++i) { std::memcpy(buffer + i * inputH * inputW, channels[i].data, inputH * inputW * sizeof(float)); } //cv::imshow("src",resized); //cv::waitKey(0); return true; } cv::Mat ImageProcessor::createSegmentationMask(float* outputBuffer, int inputH, int inputW, int numClasses) { cv::Mat segmentationMask(inputH, inputW, CV_8UC1); for (int y = 0; y < inputH; ++y) { for (int x = 0; x < inputW; ++x) { int maxClass = 0; float maxProb = outputBuffer[y * inputW + x]; for (int c = 1; c < numClasses; ++c) { float prob = outputBuffer[(c * inputH * inputW) + (y * inputW) + x]; if (prob > maxProb) { maxClass = c; maxProb = prob; } } segmentationMask.at<uchar>(y, x) = static_cast<uchar>(maxClass * 255 / (numClasses - 1)); } } cv::Mat colorMask; cv::applyColorMap(segmentationMask, colorMask, cv::COLORMAP_JET); return colorMask; } #TensorRTEngine.cpp #include "my_TensorRTEngine.hpp" // TensorRTEngine 클래스의 선언을 포함하는 헤더 파일 #include "my_ImageProcessor.hpp" // 이미지 전처리 및 후처리를 위한 유틸리티 함수들이 정의된 헤더 파일 #include <fstream> // 파일 입출력을 위한 헤더 파일 #include <iostream> // 표준 입출력 사용을 위한 헤더 파일 #include "cuda_runtime_api.h" // CUDA 런타임 API 사용을 위한 헤더 파일 // Logger 클래스의 log 함수 구현: 주어진 심각도에 따라 메시지를 출력 void Logger::log(Severity severity, const char* msg) noexcept { if (severity <= Severity::kWARNING) // 경고 이상의 심각도인 경우 메시지를 출력 std::cout << msg << std::endl; } // TensorRTEngine 클래스 생성자: 멤버 변수 초기화 TensorRTEngine::TensorRTEngine() : runtime(nullptr), engine(nullptr), context(nullptr), inputH(0), inputW(0), numClasses(0) {} // TensorRTEngine 클래스 소멸자: 자원을 해제하는 cleanup 함수 호출 TensorRTEngine::~TensorRTEngine() { cleanup(); } // 파일을 읽어 벡터에 데이터를 저장하는 함수 void TensorRTEngine::readFile(const std::string& fileName, std::vector<char>& buffer) { std::ifstream file(fileName, std::ios::binary | std::ios::ate); // 파일을 바이너리 모드로 열고 끝으로 이동 if (!file.is_open()) { // 파일 열기에 실패한 경우 에러 메시지 출력 후 프로그램 종료 std::cerr << "파일 열기 실패" << fileName << std::endl; exit(EXIT_FAILURE); } std::streamsize size = file.tellg(); // 파일 크기 가져오기 file.seekg(0, std::ios::beg); // 파일의 시작으로 이동 buffer.resize(size); // 버퍼의 크기를 파일 크기로 설정 if (!file.read(buffer.data(), size)) { // 파일 내용을 버퍼에 읽어오기 std::cerr << "파일 읽기 실패 " << fileName << std::endl; exit(EXIT_FAILURE); } } //엔진 생성하여 저장, 기존 엔진이 있는경우 로드 (FP16으로 최적화) bool TensorRTEngine::buildEngine(const std::string& onnxFile, const std::string& engineFileName) { // Step 1: 엔진 파일이 이미 존재하는지 확인 if (std::filesystem::exists(engineFileName)) { // 엔진 파일이 존재하면, 빌드하지 않고 로드 std::ifstream engineFile(engineFileName, std::ios::binary); if (!engineFile.is_open()) { std::cerr << "엔진 파일 열기 실패: " << engineFileName << std::endl; return false; } // 엔진 파일의 크기를 얻고 데이터를 읽음 engineFile.seekg(0, std::ios::end); std::streamsize engineSize = engineFile.tellg(); engineFile.seekg(0, std::ios::beg); // 엔진 파일을 메모리에 읽어옴 std::vector<char> engineData(engineSize); if (!engineFile.read(engineData.data(), engineSize)) { std::cerr << "엔진 파일 읽기 실패: " << engineFileName << std::endl; return false; } engineFile.close(); // 엔진 데이터를 이용해 CUDA 엔진을 디시리얼라이즈 runtime = nvinfer1::createInferRuntime(logger); engine = runtime->deserializeCudaEngine(engineData.data(), engineSize); if (!engine) { std::cerr << "엔진 파일에서 CUDA 엔진 생성 실패" << std::endl; return false; } // 컨텍스트와 입력, 출력 차원 정보 설정 context = engine->createExecutionContext(); nvinfer1::Dims inputDims = engine->getTensorShape(engine->getIOTensorName(0)); inputH = inputDims.d[2]; inputW = inputDims.d[3]; nvinfer1::Dims outputDims = engine->getTensorShape(engine->getIOTensorName(1)); numClasses = outputDims.d[1]; return true; // 엔진 파일에서 성공적으로 로드 } // Step 2: 엔진 파일이 없을 경우, ONNX 파일로부터 새 엔진을 빌드 nvinfer1::IBuilder* builder = nvinfer1::createInferBuilder(logger); nvinfer1::INetworkDefinition* network = builder->createNetworkV2(0); nvonnxparser::IParser* parser = nvonnxparser::createParser(*network, logger); std::vector<char> modelData; readFile(onnxFile, modelData); if (!parser->parse(modelData.data(), modelData.size())) { std::cerr << "ONNX 모델 파서 실패" << std::endl; return false; } nvinfer1::IBuilderConfig* config = builder->createBuilderConfig(); config->setMemoryPoolLimit(nvinfer1::MemoryPoolType::kWORKSPACE, 1ULL << 30); // FP16 모드 활성화 if (builder->platformHasFastFp16()) { config->setFlag(nvinfer1::BuilderFlag::kFP16); } nvinfer1::IHostMemory* serializedEngine = builder->buildSerializedNetwork(*network, *config); if (!serializedEngine) { std::cerr << "엔진 빌드 실패" << std::endl; return false; } std::ofstream engineFile(engineFileName, std::ios::binary); engineFile.write(static_cast<const char*>(serializedEngine->data()), serializedEngine->size()); engineFile.close(); runtime = nvinfer1::createInferRuntime(logger); engine = runtime->deserializeCudaEngine(serializedEngine->data(), serializedEngine->size()); if (!engine) { std::cerr << "CUDA 엔진 생성 실패" << std::endl; return false; } // 컨텍스트와 입력, 출력 차원 정보 설정 context = engine->createExecutionContext(); nvinfer1::Dims inputDims = engine->getTensorShape(engine->getIOTensorName(0)); inputH = inputDims.d[2]; inputW = inputDims.d[3]; nvinfer1::Dims outputDims = engine->getTensorShape(engine->getIOTensorName(1)); numClasses = outputDims.d[1]; // 자원 해제 delete serializedEngine; delete config; delete parser; delete network; delete builder; return true; // 엔진 빌드 성공 시 true 반환 } //수정된 추론함수(비동기 모드)GPU CPU 따로 작업 bool TensorRTEngine::runInference(cv::Mat& frame, cv::Mat& result, cudaStream_t stream){ int32_t nIO = engine->getNbIOTensors(); std::vector<std::string> vTensorName(nIO); buffers.resize(nIO); bufferSizes.resize(nIO); for (int i = 0; i < nIO; ++i) { vTensorName[i] = std::string(engine->getIOTensorName(i)); nvinfer1::Dims dims = context->getTensorShape(engine->getIOTensorName(i)); int64_t size = 1; for (int j = 0; j < dims.nbDims; ++j) { size *= dims.d[j]; } bufferSizes[i] = size * sizeof(float); cudaMalloc(&buffers[i], bufferSizes[i]); } // 이미지 전처리 수행 float* inputBuffer = new float[bufferSizes[0] / sizeof(float)]; if (!ImageProcessor::preprocessImage(frame, inputBuffer, inputH, inputW)) { std::cerr << "Image preprocessing failed." << std::endl; delete[] inputBuffer; return false; } // 입력 데이터를 GPU 메모리로 비동기 복사 cudaMemcpyAsync(buffers[0], inputBuffer, bufferSizes[0], cudaMemcpyHostToDevice, stream); for (int i = 0; i < nIO; ++i) { context->setTensorAddress(vTensorName[i].c_str(), buffers[i]); } // 비동기 추론 수행 bool status = context->enqueueV3(stream); if (!status) { std::cerr << "Inference failed!" << std::endl; delete[] inputBuffer; return false; } // 출력 데이터를 비동기로 CPU 메모리로 복사 float* outputBuffer = new float[bufferSizes[1] / sizeof(float)]; cudaMemcpyAsync(outputBuffer, buffers[1], bufferSizes[1], cudaMemcpyDeviceToHost, stream); // GPU 작업이 완료될 때까지 기다리도록 동기화 (메인 코드에서) // cudaStreamSynchronize(stream); // 이 코드는 메인에서 처리 // 세그멘테이션 마스크 생성 result = ImageProcessor::createSegmentationMask(outputBuffer, inputH, inputW, numClasses); // 메모리 해제 delete[] inputBuffer; delete[] outputBuffer; for (void* buffer : buffers) { cudaFree(buffer); } return true; } // 할당된 자원을 해제하는 함수 void TensorRTEngine::cleanup() { if (context) { delete context; // 컨텍스트 해제 context = nullptr; } if (engine) { delete engine; // 엔진 해제 engine = nullptr; } if (runtime) { delete runtime; // 런타임 해제 runtime = nullptr; } } | cs |
메인 코드
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 | #main.cpp #include "my_TensorRTEngine.hpp" #include <iostream> #include <opencv2/opencv.hpp> #include <unistd.h> #include <sys/time.h> /*int main(int argc, char** argv) { if (argc != 4) { std::cerr << "Usage: " << argv[0] << " <onnx_model> <video_file> <engine_file>" << std::endl; return EXIT_FAILURE; } const std::string onnxFile = argv[1]; const std::string videoFile = argv[2]; const std::string engineFileName = argv[3]; TensorRTEngine engine; //cv::Mat src=cv::imread("test0.jpg"); // Step 1: TensorRT 엔진 빌드 if (!engine.buildEngine(onnxFile, engineFileName)) { std::cerr << "Failed to build engine" << std::endl; return EXIT_FAILURE; } //std::string imageFile="/home/linux/DeepLabV3-TensorRT-Segmentation/data/test0.jpg"; std::string file_path="/home/linux/DeepLabV3-TensorRT-Segmentation/data/test.mp4"; //for(int i=0;i<100;i++){ // if (!engine.runInference(imageFile)) { // std::cerr << "Failed to run inference" << std::endl; // return EXIT_FAILURE; //} //} // Step 2: 동영상 파일 열기 cv::VideoCapture cap(file_path); if (!cap.isOpened()) { std::cerr << "Failed to open video file: " << file_path << std::endl; return EXIT_FAILURE; } cv::Mat frame,mask; while (true) { cap >> frame; //frame=src; if (frame.empty()) { std::cerr << "End of video or failed to grab frame" << std::endl; break; } auto start = std::chrono::high_resolution_clock::now(); // Step 3: 각 프레임에 대해 추론 수행 engine.runInference(frame,mask); if (mask.empty()) { std::cerr << "Inference failed on the current frame" << std::endl; return -1; } auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> inferenceTime = end - start; std::cout << "추론 시간: " << inferenceTime.count() << " seconds." << std::endl; // Step 4: 원본 프레임 및 추론된 마스크를 화면에 표시 cv::imshow("Input Frame", frame); cv::imshow("Segmentation Mask", mask); // ESC 키가 눌리면 루프 종료 if (cv::waitKey(1) == 27) { break; } //usleep(27000); } cap.release(); return EXIT_SUCCESS; }*/ int main(int argc, char** argv) { if (argc != 4) { std::cerr << "Usage: " << argv[0] << " <onnx_model> <video_file> <engine_file>" << std::endl; return EXIT_FAILURE; } const std::string onnxFile = argv[1]; const std::string videoFile = argv[2]; const std::string engineFileName = argv[3]; TensorRTEngine engine; if (!engine.buildEngine(onnxFile, engineFileName)) { std::cerr << "Failed to build engine" << std::endl; return EXIT_FAILURE; } std::string file_path="/home/linux/DeepLabV3-TensorRT-Segmentation/data/test.mp4"; cv::VideoCapture cap(file_path); if (!cap.isOpened()) { std::cerr << "Failed to open video file: " << file_path << std::endl; return EXIT_FAILURE; } cv::Mat frame, mask; cudaStream_t stream; cudaStreamCreate(&stream); // CUDA 스트림 생성 while (true) { cap >> frame; if (frame.empty()) { std::cerr << "End of video or failed to grab frame" << std::endl; break; } auto start = std::chrono::high_resolution_clock::now(); // 비동기 추론 수행 engine.runInference(frame, mask, stream); // GPU 작업이 완료될 때까지 동기화 (추론이 끝났을 때) cudaStreamSynchronize(stream); auto end = std::chrono::high_resolution_clock::now(); std::chrono::duration<double> inferenceTime = end - start; std::cout << "추론 시간: " << inferenceTime.count() << " seconds." << std::endl; cv::imshow("Input Frame", frame); cv::imshow("Segmentation Mask", mask); if (cv::waitKey(1) == 27) { // ESC 키 체크 break; } } cap.release(); cudaStreamDestroy(stream); // CUDA 스트림 해제 return EXIT_SUCCESS; } | cs |
CMakeList.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | #CMakeList.txt # cmake_minimum_required(VERSION 3.10) # project(test_tensorrt) # # Set C++ standard # set(CMAKE_CXX_STANDARD 14) # # Find CUDA # find_package(CUDA REQUIRED) # # Find OpenCV # find_package(OpenCV REQUIRED) # # Include directories # include_directories(${CUDA_INCLUDE_DIRS}) # include_directories(${TENSORRT_INCLUDE_DIR}) # include_directories(${OpenCV_INCLUDE_DIRS}) # include_directories(${CMAKE_CURRENT_SOURCE_DIR}/include) # # Link directories # link_directories(${CUDA_LIBRARIES}) # link_directories(${TENSORRT_LIB_DIR}) # # Add source files # set(SOURCES # src/my_main.cpp # src/my_TensorRTEngine.cpp # src/my_ImageProcessor.cpp # ) # # Add executable # add_executable(main ${SOURCES}) # # Include directories for the target # target_include_directories(main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) # # Link libraries # target_link_libraries(main # ${OpenCV_LIBS} # ${CUDA_LIBRARIES} # nvinfer # nvinfer_plugin # nvonnxparser # ) # message(${OpenCV_LIBS}) # # After finding packages # message(STATUS "CUDA Include dirs: ${CUDA_INCLUDE_DIRS}") # message(STATUS "CUDA Libraries: ${CUDA_LIBRARIES}") # message(STATUS "TensorRT Include dir: ${TENSORRT_INCLUDE_DIR}") # message(STATUS "TensorRT Lib dir: ${TENSORRT_LIB_DIR}") # message(STATUS "OpenCV Include dirs: ${OpenCV_INCLUDE_DIRS}") # message(STATUS "OpenCV Libraries: ${OpenCV_LIBS}") cmake_minimum_required(VERSION 3.10) project(test_tensorrt) # Set C++ standard set(CMAKE_CXX_STANDARD 17)#filesystem 헤더파일을 사용하기 위해 17로 수정함 # Find CUDA find_package(CUDA REQUIRED)#CUDA 패키지 찾는 코드 # Find OpenCV find_package(OpenCV REQUIRED)#opencv 패키지찾는 코드 if(NOT OpenCV_FOUND) message(FATAL_ERROR "OpenCV not found!") endif() # Include directories include_directories( ${CUDA_INCLUDE_DIRS} ${TENSORRT_INCLUDE_DIR} # Ensure this variable is correctly set ${OpenCV_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/include ) # Add this line to explicitly set OpenCV include path include_directories(/usr/include/opencv4) # Link directories link_directories( ${CUDA_LIBRARY_DIRS} ${TENSORRT_LIB_DIR} # Ensure this variable is correctly set ) # Add source files set(SOURCES #본인의 소스코드 경로 src/my_main.cpp src/my_TensorRTEngine.cpp src/my_ImageProcessor.cpp ) # Add executable add_executable(main ${SOURCES}) # Include directories for the target target_include_directories(main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include) # Link libraries target_link_libraries(main ${OpenCV_LIBS} ${CUDA_LIBRARIES} nvinfer nvinfer_plugin nvonnxparser ) # Display found OpenCV libraries (for debugging purposes) message(STATUS "OpenCV libraries: ${OpenCV_LIBS}")#디버깅 # Ensure the NVCC flags are properly set (if using CUDA) set_target_properties(main PROPERTIES CUDA_SEPARABLE_COMPILATION ON) | cs |
위 코드 실행 결과 (비동기 모드 FP16)
FP16모드 일때 속도가 확실히 빨라진 것을 느낌 그러나 정확도를 고려하여 FP32 모드로 생각해 봐야 할것 같음
비동기 모드: GPU 작업이 끝날때 까지 CPU가 기다리는 것이아니라 CPU가 작업을 할 준비를 하여 시간을 줄이는 모드
동기 모드: GPU작업이 끝날때 까지 CPU 대기
현재 건우 코드에서 작업을 진행 할 때 동기모드,비동기 모드 차이가 거의없어서 개선필. tensorrt를 사용하여 추론시간 개선 (기존 33ms~40ms)->(16ms~20ms)
위 코드 실행 결과

FP16모드 일때 속도가 확실히 빨라진 것을 느낌
정확도를 고려하여 FP32 모드로 생각해 봐야 할것 같음
트랜스 포머기반 분할모델 추론성능 평가
segmenter 모델로 추론
https://github.com/rstrudel/segmenter
위 깃허브의 모델: Segmenter
데이터셋: ADE-20K
Segmenter models with ViT backbone: Seg-S-Mask/16 선택
성능과 추론시간이 준수한 편이라 Seg-S-Mask/16을 채택
추론이 약 1초? 걸린것으로 확인 사진 총 4장
원본
새그맨테이션 맵
아쉽게도 바닥 클래스는 없는것 같음 ADE 모델의 총 클래스 개수는 149로 확인
ADE-20k 으로 학습한 yml 파일 링크
https://github.com/rstrudel/segmenter/blob/master/segm/data/config/ade20k.yml
같이 돌려본 예제사진
새그맨테이션 맵
아마 클래스에 바닥이 아니라 인도가 있는 것으로 보임
오버레이 코드를 비활성화 하고segmentation맵만 저장한 결과
inference.py (오버레이 비활성화 코드)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | import click from tqdm import tqdm from pathlib import Path from PIL import Image import numpy as np import torchvision.transforms.functional as F import segm.utils.torch as ptu from segm.data.utils import STATS from segm.data.ade20k import ADE20K_CATS_PATH from segm.data.utils import dataset_cat_description, seg_to_rgb from segm.model.factory import load_model from segm.model.utils import inference @click.command() @click.option("--model-path", type=str) @click.option("--input-dir", "-i", type=str, help="folder with input images") @click.option("--output-dir", "-o", type=str, help="folder with output images") @click.option("--gpu/--cpu", default=True, is_flag=True) def main(model_path, input_dir, output_dir, gpu): ptu.set_gpu_mode(gpu) model_dir = Path(model_path).parent model, variant = load_model(model_path) model.to(ptu.device) normalization_name = variant["dataset_kwargs"]["normalization"] normalization = STATS[normalization_name] cat_names, cat_colors = dataset_cat_description(ADE20K_CATS_PATH) input_dir = Path(input_dir) output_dir = Path(output_dir) output_dir.mkdir(exist_ok=True) list_dir = list(input_dir.iterdir()) for filename in tqdm(list_dir, ncols=80): pil_im = Image.open(filename).copy() im = F.pil_to_tensor(pil_im).float() / 255 im = F.normalize(im, normalization["mean"], normalization["std"]) im = im.to(ptu.device).unsqueeze(0) im_meta = dict(flip=False) logits = inference( model, [im], [im_meta], ori_shape=im.shape[2:4], window_size=variant["inference_kwargs"]["window_size"], window_stride=variant["inference_kwargs"]["window_stride"], batch_size=2, ) seg_map = logits.argmax(0, keepdim=True) seg_rgb = seg_to_rgb(seg_map, cat_colors) seg_rgb = (255 * seg_rgb.cpu().numpy()).astype(np.uint8) pil_seg = Image.fromarray(seg_rgb[0]) # pil_blend = Image.blend(pil_im, pil_seg, 0.5).convert("RGB")#오버레이 코드 # pil_blend.save(output_dir / filename.name) #오버레이 이미지 저장 pil_seg.save(output_dir / filename.name)# 세그멘테이션 맵만 저장 if __name__ == "__main__": main() | cs |
원본
segmentation 맵
벽,바닥, 천장빼고 다 background로 수정하여 진행할 예정
다른 모델, 백본을 바꿔서 진행할 예정

첫댓글 실내영상 추론에서 벽, 바닥에 오버레이모드로 그려진것 같은데 그리고 ade20k 데이터셋에는 바닥(floor) 클래스가 존재함
오버레이 색깔을 변경해볼것 -> 오버레이모드 사용하지 말고 완전 마스크영상으로 저장해볼것
수정 완료 하였고 벽,바닥,천장 빼고 background로 해보고 결과올리겠습니다.
@이승현 tensorrt로 구현해볼것
원본에서는 createSegmentationMask함수 안에서 softmax함수를 사용했는데 너의 코드에서는 사용하지 않는데 이유가 뭔가?
ADE20K 데이터셋으로 학습한 ViT기반 분할 모델이용해서 군산대 실내영상으로 전이학습을 해보도록 할것
ADE20K 데이터셋에는 실내 영상이 포함되어 이미 선행학습이 잘되어있을거고 군산대 영상을 사용해서 전이학습하면 더 잘하지 않을까
전이학습시 백본모델로 사용한 ViT의 가중치는 고정해야함 -> 작은데이터로도 좋은 성능을 얻을수 있을것임 대신 ADE20K 클래스기준으로 세분화해서 다시 레이블링해야함