|
1) 훈련예제에서 사용한 deeplabv3 모델의 구조를 조사하고 설명하시오.
deeplabv3 모델구조
1.백본(backbone)네트워크: 이미지의 특징을 추출하기 위해 강력한 백본 네트워크를 사용한다. 일반적으로 resnet101모델을 사용하여 분류 작업에서 검증된 강력한 구조로 고수준의 특징 맵을 생성한다.
2. 공간 피라미드 풀링: ASPP모듈을 사용하여 다양한 크기의 필터로 입력 이미지에서 다양한 스케일의 정보를 추출한다. 이를 통해 모델은 작은 객체부터 큰 객체까지 다양한 크기의 객체를 효과적으로 분할할 수 있다.
확장 합성곱(Atrous Convolution): 필터 크기를 넓히면서도 계산량을 크게 늘리지 않고 특징을 추출할 수 있는 방법 이를 통해 모델은 더 넓은 범위에서 정보를 얻을 수 있다.
3.디코더: 더욱 세밀한 세그멘테이션이 가능하다 고해상도 특징 맵을 사용하여 결과를 생성한다.
4.예측:모델의 마지막 단계에서 각 픽셀의 클래스 확률을 예측한다. 이출력은 원본 이미지 크기와 동일한 크기의 세그멘테이션 맵을 생성하며 각 픽셀은 특정 클래스에 속한다.
2) 다음 2가지 방식으로 훈련결과를 실시간으로 시각화하시오.
- 원본은 건드리지 말고 새로운 소스파일 my_train.py 생성하여 원본을 복사하여 작업하고 시각화 코드를 적당한 위치에 추가할것
- 훈련로스, 정확도, 검증로스, 검증정확도 그래프 출력기능을 다음 2가지 방식으로 구현
- 교재 626페이지 코드를 이용하는 방법, 텐서보드를 사용하여 시각화하는 방법
- 매 에퍽마다 실시간으로 그래프를 업데이트하도록 할것 -> 훈련하는 동안 시간에 따라 로스의 변화를 확인가능하도록 할것 -> 손실이 더이상 감소하지 않거나 감소하다가 다시 증가한다면 훈련중단해야함
3) 최종 모델파일을 이용하여 모델의 성능을 테스트하는 파이토치 코드 my_test.py를 작성하시오.
테스트영상은 교내 건물실내영상 30장을 새로 찍어서 사용하라
파일을 개별적으로 오픈하지 말고 파이썬 os 패키지는 이용하여 폴더명을 알려주면 폴더에 저장된 영상파일을 모두 검색하여 불러와서 테스트하도록 작성할것
영상 1장의 추론(예측)시간을 측정하여 출력하시오.
https://learnopencv.com/pytorch-for-beginners-semantic-segmentation-using-torchvision/
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 | import torch from PIL import Image, ImageOps import matplotlib.pyplot as plt import torchvision.transforms as T import numpy as np import cv2 import time import os # Define the helper function def decode_segmap(image, nc=21): label_colors = np.array([(0, 0, 0), # 0=background # 1=ceiling, 2=chair, 3=door, 4=floor, 5=glassdoor (128, 0, 0), (0, 128, 0), (128, 128, 0), (0, 0, 128), (128, 0, 128), # 6=table, 7=wall, 8=window, 9=chair, (0, 128, 128), (128, 128, 128), (64, 0, 0), (192, 0, 0), (64, 128, 0), (192, 128, 0), (64, 0, 128), (192, 0, 128), (64, 128, 128), (192, 128, 128), (0, 64, 0), (128, 64, 0), (0, 192, 0), (128, 192, 0), (0, 64, 128)]) r = np.zeros_like(image).astype(np.uint8) g = np.zeros_like(image).astype(np.uint8) b = np.zeros_like(image).astype(np.uint8) for l in range(0, nc): idx = image == l r[idx] = label_colors[l, 0] g[idx] = label_colors[l, 1] b[idx] = label_colors[l, 2] rgb = np.stack([r, g, b], axis=2) return rgb # load pth model model = torch.load(r'C:\Users\Admin\LEE_workspace\pytorch\dataset4\output\model.pth') # set model to inference mode model.eval() #print(model) folder_path = r'C:\Users\Admin\LEE_workspace\pytorch\dataset4\test' for filename in os.listdir(folder_path): # 폴더 내의 모든 파일에 대해 반복 if filename.endswith(".jpg") or filename.endswith(".jfif") or filename.endswith(".webp"): # 지원하는 파일 형식 검사 image_path = os.path.join(folder_path, filename) # 파일 경로 생성 img = Image.open(image_path) # 이미지 파일 열기 start_time = time.time() img = ImageOps.exif_transpose(img) transform = T.Compose([T.Resize(520), T.CenterCrop(480), T.ToTensor(), T.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) ]) trans_img = transform(img).permute(1, 2, 0) img = transform(img).unsqueeze(0) img = img.to('cuda') out = model(img)['out'] print(img.shape) print(out.shape) om = torch.argmax(out.squeeze(), dim=0).detach().cpu().numpy() end_time = time.time() print (om.shape) print (np.unique(om)) rgb = decode_segmap(om) print("prediction one image : ", round(end_time - start_time, 3)) plt.subplot(121), plt.axis('off'), plt.imshow(trans_img) plt.subplot(122), plt.axis('off'), plt.imshow(rgb) plt.show() | cs |
4) 최종 모델파일을 이용하여 테스트하는 C++ opencv코드 my_test.cpp를 작성하시오.
테스트영상은 교내 건물실내영상 30장을 새로 찍어서 사용하라
파일을 개별적으로 오픈하지 말고 파일시스템관련 C++ 라이브러리를 이용하여 폴더명을 알려주면 폴더에 저장된 영상파일을 모두 검색하여 불러와서 테스트하도록 작성할것
영상 1장의 추론(예측)시간을 측정하여 출력하시오.
https://github.com/opencv/opencv/tree/4.x/samples/dnn
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 | #define _CRT_SECURE_NO_WARNINGS #include <fstream> #include <sstream> #include <opencv2/dnn.hpp> #include <opencv2/imgproc.hpp> #include <opencv2/highgui.hpp> #include <opencv2/core.hpp> // TickMeter 포함 #include "common.hpp" #include <iostream> std::string keys = "{ help h | | Print help message. }" "{ @alias | | An alias name of model to extract preprocessing parameters from models.yml file. }" "{ zoo | models.yml | An optional path to file with preprocessing parameters }" "{ device | 0 | camera device number. }" "{ input i | | Path to input image or video file. Skip this argument to capture frames from a camera. }" "{ framework f | | Optional name of an origin framework of the model. Detect it automatically if it does not set. }" "{ classes | | Optional path to a text file with names of classes. }" "{ colors | | Optional path to a text file with colors for an every class. " "An every color is represented with three values from 0 to 255 in BGR channels order. }" "{ backend | 0 | Choose one of computation backends: " "0: automatically (by default), " "1: Halide language (http://halide-lang.org/), " "2: Intel's Deep Learning Inference Engine (https://software.intel.com/openvino-toolkit), " "3: OpenCV implementation, " "4: VKCOM, " "5: CUDA }" "{ target | 0 | Choose one of target computation devices: " "0: CPU target (by default), " "1: OpenCL, " "2: OpenCL fp16 (half-float precision), " "3: VPU, " "4: Vulkan, " "6: CUDA, " "7: CUDA fp16 (half-float preprocess) }"; using namespace cv; using namespace dnn; using namespace std; std::vector<std::string> classes; std::vector<Vec3b> colors; void showLegend(); void colorizeSegmentation(const Mat& score, Mat& segm); int main(int argc, char** argv) { String model = "model.onnx"; // ONNX 모델 파일 경로를 직접 설정 String config = ""; // Config 파일이 없다면 빈 문자열 String framework = ""; // 프레임워크를 자동 감지 int backendId = 0; // 사용할 백엔드를 지정 int targetId = 0; // 사용할 타겟 디바이스를 지정 // 클래스 파일과 색상 파일 경로 지정 std::string classesFile = "classes.txt"; std::string colorsFile = "color.txt"; std::ifstream ifs(classesFile.c_str()); if (!ifs.is_open()) CV_Error(Error::StsError, "클래스 파일이 없음"); std::string line; while (std::getline(ifs, line)) { classes.push_back(line); } std::ifstream ifsColor(colorsFile.c_str()); if (!ifsColor.is_open()) CV_Error(Error::StsError, "컬러 파일이 없음"); while (std::getline(ifsColor, line)) { std::istringstream colorStr(line); int r, g, b; if (colorStr >> r >> g >> b) { colors.push_back(Vec3b(b, g, r)); } } for (const auto& color : colors) { cout << "Color: " << (int)color[0] << ", " << (int)color[1] << ", " << (int)color[2] << endl; } Net net = readNet(model, config, framework); net.setPreferableBackend(backendId); net.setPreferableTarget(targetId); VideoCapture cap("test.jpg"); // 입력 이미지 경로 설정 Mat frame, blob; while (waitKey(1) < 0) { cap >> frame; if (frame.empty()) { waitKey(); break; } // 1. Resize: 입력 이미지를 파이썬 코드의 T.Resize(520)와 동일하게 크기를 조정 resize(frame, frame, Size(520, 520)); // 2. Center Crop: T.CenterCrop(480)와 동일하게 중앙을 자름 int cropSize = 480; int startX = (frame.cols - cropSize) / 2; int startY = (frame.rows - cropSize) / 2; Rect cropRegion(startX, startY, cropSize, cropSize); frame = frame(cropRegion); // 3. Normalize: 이미지 픽셀 값을 [0, 1] 범위로 스케일링하고, 각 채널별로 정규화 수행 Scalar mean = Scalar(0.485, 0.456, 0.406); // 파이썬 코드와 동일한 평균값 Scalar std = Scalar(0.229, 0.224, 0.225); // 파이썬 코드와 동일한 표준 편차 blobFromImage(frame, blob, 1.0 / 255.0, Size(), mean, true, false); // 정규화 수행 net.setInput(blob); // 추론 시간 측정 시작 int64 start = getTickCount(); Mat score = net.forward(); // 추론 시간 측정 종료 int64 end = getTickCount(); double duration = (end - start) / getTickFrequency() * 1000; // 밀리초로 변환 cout << "추론시간: " << duration << " ms" << endl; cout << "Score dimensions: " << score.dims << std::endl; for (int i = 0; i < score.dims; ++i) { cout << "Size[" << i << "]: " << score.size[i] << std::endl; } Mat segm; colorizeSegmentation(score, segm); resize(segm, segm, frame.size(), 0, 0, INTER_NEAREST); addWeighted(frame, 0.5, segm, 0.5, 0.0, frame); imshow("Deep learning semantic segmentation in OpenCV", frame); if (!classes.empty()) showLegend(); } return 0; } void colorizeSegmentation(const Mat& score, Mat& segm) { const int rows = score.size[2]; const int cols = score.size[3]; const int chns = score.size[1]; if (colors.empty()) { colors.push_back(Vec3b()); for (int i = 1; i < chns; ++i) { Vec3b color; for (int j = 0; j < 3; ++j) color[j] = (colors[i - 1][j] + rand() % 256) / 2; colors.push_back(color); } } else if (chns != (int)colors.size()) { CV_Error(Error::StsError, format("Number of output classes does not match " "number of colors (%d != %zu)", chns, colors.size())); } Mat maxCl = Mat::zeros(rows, cols, CV_8UC1); Mat maxVal(rows, cols, CV_32FC1, score.data); for (int ch = 1; ch < chns; ch++) { for (int row = 0; row < rows; row++) { const float* ptrScore = score.ptr<float>(0, ch, row); uint8_t* ptrMaxCl = maxCl.ptr<uint8_t>(row); float* ptrMaxVal = maxVal.ptr<float>(row); for (int col = 0; col < cols; col++) { if (ptrScore[col] > ptrMaxVal[col]) { ptrMaxVal[col] = ptrScore[col]; ptrMaxCl[col] = (uchar)ch; } } } } segm.create(rows, cols, CV_8UC3); for (int row = 0; row < rows; row++) { const uchar* ptrMaxCl = maxCl.ptr<uchar>(row); Vec3b* ptrSegm = segm.ptr<Vec3b>(row); for (int col = 0; col < cols; col++) { ptrSegm[col] = colors[ptrMaxCl[col]]; } } } void showLegend() { static const int kBlockHeight = 30; static Mat legend; if (legend.empty()) { const int numClasses = (int)classes.size(); if ((int)colors.size() != numClasses) { CV_Error(Error::StsError, format("Number of output classes does not match " "number of labels (%zu != %zu)", colors.size(), classes.size())); } legend.create(kBlockHeight * numClasses, 200, CV_8UC3); for (int i = 0; i < numClasses; i++) { Mat block = legend.rowRange(i * kBlockHeight, (i + 1) * kBlockHeight); block.setTo(colors[i]); putText(block, classes[i], Point(0, kBlockHeight / 2), FONT_HERSHEY_SIMPLEX, 0.5, Vec3b(255, 255, 255)); } namedWindow("Legend", WINDOW_NORMAL); imshow("Legend", legend); } } | cs |
추론시간 약 0.3초
5) 훈련 결과에서 정확도(average row correct)와 IoU가 낮은 클래스를 포함하는 영상을 추가하여 훈련을 다시하여 성능이 개선되는지 관찰하라. chair, glassdoor, table 클래스가 정확도가 낮다.
테이블,의자 등의 사진을 각각 10장 추가하였으나 큰 성능차이가 없음
6) 훈련 코드에서 백본레이어의 가중치를 freeze하는 이유를 설명하라.
백본 레이어의 가중치를 freeze하는 이유는 이미 학습된 특징을 그대로 활용하여 학습하는데 걸리는 시간을 줄이고 부족한 양의 학습데이터에 대해 일반화 성능을 높여주기 떄문이다.
7) 백본모델을 resnet101 모델로 변경하여 훈련 및 테스트 해보고 resnet50과 성능을 비교하시오.
전체적으로 비슷하나 의자,창문의 정확도가 올라감
8) 훈련결과를 이용하여 과적합을 판단하는 방법을 설명하라.
훈련 손실은 계속해서 감소할 때 검증 손실은 일정 시점부터 값이 유지하면 과적합으로 판단한다. 또한 훈련 데이터에 대한 정확도나 IoU값은 높지만 검증 데이터에 대한 정확도나 IoU가 낮거나 감소하는 경우를 보이면 과적합으로 판단한다.
9) 파이토치 예제 train.py를 분석하여 사용한 증식방법을 설명하고 dataloader객체에 저장된 증식결과영상를 출력하여 잘 동작하는지를 확인하라. 원본소스는 건드리지 말고 새로운 파일 my_train.py를 만들어서 적당한 테스트코드를 추가하여 실행할것
참고 : https://cafe.daum.net/SmartRobot/W2or/159
v1을 사용할시 1개의 모듈만 사용하고 v2를 사용할시 2개의 모듈을 반환하는 것을 볼 수 있다.
10) 건물 실내를 촬영한 동영상을 이용하여 테스트해보고 추론시간을 측정하고 초당 처리가능한 프레임수를 구하라.
모델은 resnet50, resnet101을 이용하여 비교해보라.
실시간 처리가 가능하려면 어떻게 해야하나?
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 | import torch from PIL import Image, ImageOps import torchvision.transforms as T import numpy as np import cv2 import time def decode_segmap(image, nc=5): # 각 클래스 레이블에 대한 색상 맵 정의 label_colors = np.array([(0, 0, 0), # 0=배경 # 1=천장, 2=바닥, 3=벽, 4=차단물, 5=유리문 (100, 100, 100), (255, 255, 255), (50, 50, 50), (0, 0, 0), (128, 0, 128), ]) # RGB 채널을 위한 빈 배열 생성 r = np.zeros_like(image).astype(np.uint8) g = np.zeros_like(image).astype(np.uint8) b = np.zeros_like(image).astype(np.uint8) # 세그멘테이션 맵에서 각 클래스에 해당하는 픽셀에 색상 할당 for l in range(0, nc): idx = image == l r[idx] = label_colors[l, 0] g[idx] = label_colors[l, 1] b[idx] = label_colors[l, 2] # R, G, B 채널을 쌓아 최종 RGB 이미지 생성 rgb = np.stack([r, g, b], axis=2) return rgb # 훈련된 세그멘테이션 모델 로드 model = torch.load(r'C:\Users\Admin\LEE_workspace\pytorch\dataset4\output101\model.pth', weights_only=False) model.eval() # 모델을 평가 모드로 설정 # 비디오 경로 및 비디오 캡처 객체 정의 video_path = r'C:\Users\Admin\LEE_workspace\pytorch\dataset4\test\test.mp4' cap = cv2.VideoCapture(video_path) # 비디오 파일 열기 if not cap.isOpened(): print("Error: Cannot open video file") exit() # 비디오 속성 가져오기 fps = cap.get(cv2.CAP_PROP_FPS) # 프레임 속도(FPS) 가져오기 frame_width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)) # 프레임 너비 가져오기 frame_height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) # 프레임 높이 가져오기 # 출력 비디오 작성을 위한 코덱 및 VideoWriter 객체 정의 output_path = 'rC:\Users\Admin\LEE_workspace\pytorch\dataset4\test_segmentation.mp4' fourcc = cv2.VideoWriter_fourcc(*'mp4v') # 비디오 코덱 설정 out = cv2.VideoWriter(output_path, fourcc, fps, (frame_width * 2, frame_height)) # 출력 비디오 설정 while cap.isOpened(): ret, frame = cap.read() # 비디오에서 프레임 읽기 if not ret: break # OpenCV 이미지(BGR 형식)를 PIL 이미지(RGB 형식)로 변환 pil_img = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)) # 이미지 전처리 수행 pil_img = ImageOps.exif_transpose(pil_img) transform = T.Compose([ T.ToTensor(), # 이미지를 텐서로 변환 T.Normalize( mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 이미지 정규화 ]) img = transform(pil_img).unsqueeze(0).to('cuda') # 이미지 배치를 추가하고 GPU로 이동 # 모델을 사용하여 출력 예측 with torch.no_grad(): out = model(img)['out'] om = torch.argmax(out.squeeze(), dim=0).detach().cpu().numpy() # 예측 결과를 세그멘테이션 맵으로 변환 # 세그멘테이션 맵을 RGB 이미지로 디코딩 rgb = decode_segmap(om) # RGB 이미지를 BGR로 변환하여 OpenCV에서 사용 output_frame = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) # 원본 이미지와 세그멘테이션 결과 이미지를 나란히 결합 combined_frame = np.hstack((frame, output_frame)) # 결합된 프레임을 화면에 표시 cv2.imshow('Original and Segmentation', combined_frame) if cv2.waitKey(1) & 0xFF == ord('q'): break # 출력 비디오 파일에 프레임 작성 out.write(combined_frame) cap.release() out.release() cv2.destroyAllWindows() | cs |
11) Yolo모델을 이용하여 영상분할을 수행하라.
- 파이토치 프레임워크로 구현된 Yolo 최신 영상분할용 모델의 소스를 찾아서 위 예제코드에 붙여보라
- 원본소스는 건드리지 말고 새로운 파일 yolo_train.py을 만들어서 사용할것
- DeepLabV3 모델과 추론(예측)시간, 정확도를 비교하시오.
첫댓글 4번 결과 첨부
9번 적용된 변환 설명 추가
10번 결과영상, 설명 추가