검색결과 리스트
글
Flask 라이브러리로 이미지를 업로드하고 볼 수 있는 서버 구축하기
공대생의 팁
2024. 10. 26. 01:19
※ 이 프로젝트는 ChatGPT로 생성된 코드를 사용하였습니다.
프로그래밍을 생업으로 일하시는 분들이라면 한 번 즈음은 자신이 사용해보지 못했던 프로그래밍 언어를 사용해야 할 수 밖에 없는 경험이 있으셨을 것입니다. 저 또한 Python을 위주로 개발을 하다보니 간혹 C/C++를 사용할 일이 있을 때엔 버벅거리면서 코딩을 간신히 해내곤 합니다.
그런 와중에 프로그램 시연을 해야 하는 상황이 되었는데 막상 준비하려 하니 1주일이라는 시간을 갖고는 GUI 프로그램을 뚝딱 만드는건 사실상 불가능한 상황이었습니다. 그러한 고민을 하던 상황에서 혹시나 Chat GPT에게 이러한 고민을 털어놨더니 Web프로그래밍으로 짧은 시간 내에 시각화를 할 수 있는 프로젝트를 만들 수 있다는 답변을 하는 것이었습니다!
지금까지 Web프로그래밍 관련 지식은 20년전 개인 홈페이지 제작을 위해 제로보드를 다루었던 경험밖에 없는 저에게 직접 Web프로그래밍을 하는 것은 불가능했습니다. 하지만 우리는 언제나 그랬듯이 방법을 찾아내왔지요?
이번 포스팅에서는 Web프로그래밍 경험이 없는 사람의 입장에서 ChatGPT에게 다음과 같은 질문을 하였고 아래와 같은 질문을 통해 매우 훌륭한 Web프로그램을 완성하였습니다.
Python을 사용해서 다음과 같은 프로그램을 만들어줘.
- 이미지를 웹사이트에 업로드 하고, 이를 볼 수 있는 웹사이트 제작
- 이미지를 볼 때 마우스의 휠로 이미지의 축소/확대 기능 적용
- 마우스로 드래그하여 확대된 이미지를 이동하며 볼 수 있도록함
- 이미지 뷰어의 아래쪽에는 Thumbnail 방식으로 업로드된 이미지를 클릭하여 볼 수 있도록 함
- 클립보드에 복사된 이미지를 웹페이지에 붙여넣기를 하는 방법으로 이미지를 업로드
- 업로드된 이미지는 OpenCV를 사용하여 grayscale 이미지를 생성하고, 원본 및 변환된 이미지를 볼 수 있도록 함
프롬프트의 전반적인 구조는 위와 같이 하여 질문을 하였고 몇몇 부분은 추가로 프롬프트를 작성하였으며, 부족한 부분은 직접 다듬었을 뿐인데 다행히도 원하는 대로 동작하는 웹사이트가 완성되었습니다!
아래는 ChatGPT가 Flask를 사용하여 만들어낸 프로젝트입니다. 아래는 프로젝트의 파일 구조입니다. app.py가 html을 제어하여 웹페이지를 동작시키는 것으로 이해하면 얼추 맞을 겁니다.
templates/index.html
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
302
303
304
305
|
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Drag and Drop Image Upload</title>
<style>
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
height: 100vh;
margin: 0;
background-color: #f5f5f5;
overflow: hidden;
/* Hide scrollbars */
}
#drop-area {
border: 2px dashed #ccc;
padding: 80px;
width: 100%;
height: 80px;
/* Increased height */
box-sizing: border-box;
/* Include padding and border in the element's total width and height */
text-align: center;
background-color: #fff;
position: fixed;
top: 0;
left: 0;
display: flex;
justify-content: center;
align-items: center;
/* Center the text */
}
#drop-area p {
margin: 0;
font-size: 24px;
font-weight: bold;
/* Bold text */
}
#loading {
display: none;
/* Hide loading initially */
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-color: rgba(128, 128, 128, 0.7);
/* Gray with 70% opacity */
color: white;
padding: 40px;
/* Increased padding */
border-radius: 10px;
/* Increased border-radius */
text-align: center;
z-index: 1000;
/* Ensure loading screen overlaps all content */
font-size: 32px;
/* Larger font size for loading text */
font-weight: bold;
/* Bold text for loading */
}
#gallery {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: calc(100vh - 280px);
/* Full height minus drop area height, thumbnails, and border */
margin-top: 95px;
/* Adjusted to match drop-area height and border */
overflow: hidden;
position: relative;
/* Keep positioned relative to viewport */
border-bottom: 1px solid black;
/* Add bottom border for separation */
}
#thumbnails {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100px;
margin: 5px;
overflow-x: auto;
background-color: #f5f5f5;
position: fixed;
bottom: 0;
left: 0;
}
#thumbnails img {
height: 80px;
margin: 10px;
cursor: pointer;
transition: transform 0.25s ease;
}
#thumbnails img:hover {
transform: scale(1.1);
}
img {
max-width: 100%;
max-height: 100%;
transition: transform 0.25s ease;
/* Smooth transition for transform */
cursor: grab;
/* Cursor shows grab icon */
}
img:active {
cursor: grabbing;
/* Cursor shows grabbing icon */
}
</style>
</head>
<body>
<div id="loading">Loading...</div>
<h2>Drag and Drop Image Upload</h2>
<div id="drop-area">
<p>Drag & drop image files here<br>Or paste image from clipboard</p>
</div>
<div id="gallery"></div>
<div id="thumbnails"></div>
<script>
let dropArea = document.getElementById('drop-area');
let gallery = document.getElementById('gallery');
let thumbnails = document.getElementById('thumbnails');
let loading = document.getElementById('loading');
let currentImage;
let scale = 1;
let translateX = 0;
let translateY = 0;
dropArea.addEventListener('dragover', (event) => {
event.preventDefault();
dropArea.style.borderColor = 'green';
});
dropArea.addEventListener('dragleave', () => {
dropArea.style.borderColor = '#ccc';
});
dropArea.addEventListener('drop', (event) => {
event.preventDefault();
dropArea.style.borderColor = '#ccc';
thumbnails.innerHTML = ''; // Clear existing thumbnails on each drop
let files = event.dataTransfer.files;
handleFiles(files);
});
function handleFiles(files) {
([...files]).forEach(uploadFile);
}
async function uploadFile(file) {
// Show loading screen before starting upload
loading.style.display = 'block';
let url = '/upload';
let formData = new FormData();
formData.append('file', file);
try {
const response = await fetch(url, {
method: 'POST',
body: formData
});
const data = await response.json();
// Hide loading screen after upload is successful
displayImage(data.images[0]);
loading.style.display = 'none';
// Add the uploaded image to the thumbnails
data.images.forEach(imgUrl => addThumbnail(imgUrl))
} catch (error) {
console.error('Upload failed');
loading.style.display = 'none';
}
}
function displayImage(imageUrl) {
// Clear previous image
gallery.innerHTML = '';
// Create new image
const img = document.createElement('img');
img.src = imageUrl;
gallery.appendChild(img);
// Update current image reference
currentImage = img;
// Reset scale and position
scale = 1;
translateX = 0;
translateY = 0;
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
// Add drag-to-pan functionality
let isDragging = false;
let startX, startY;
img.addEventListener('mousedown', (event) => {
event.preventDefault();
isDragging = true;
startX = event.clientX - translateX;
startY = event.clientY - translateY;
img.style.cursor = 'grabbing';
});
document.addEventListener('mousemove', (event) => {
if (isDragging) {
event.preventDefault();
translateX = event.clientX - startX;
translateY = event.clientY - startY;
// Restrict movement within the boundaries
const rect = img.getBoundingClientRect();
const galleryRect = gallery.getBoundingClientRect();
// Calculate overflows
const overflowX = Math.max(0, rect.width * scale - galleryRect.width);
const overflowY = Math.max(0, rect.height * scale - galleryRect.height);
// Clamp translateX and translateY within allowed bounds
translateX = Math.min(overflowX / 2, Math.max(-overflowX / 2, translateX));
translateY = Math.min(overflowY / 2, Math.max(-overflowY / 2, translateY));
img.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
}
});
document.addEventListener('mouseup', () => {
if (isDragging) {
isDragging = false;
img.style.cursor = 'grab';
}
});
}
function addThumbnail(imageUrl) {
const thumb = document.createElement('img');
thumb.src = imageUrl;
thumb.addEventListener('click', () => {
displayImage(imageUrl);
});
thumbnails.appendChild(thumb);
}
// Zoom in/out handlers
gallery.addEventListener('wheel', (event) => {
if (currentImage) {
event.preventDefault();
if (event.deltaY > 0) {
// Scroll down - zoom out
scale *= 0.9;
} else {
// Scroll up - zoom in
scale *= 1.1;
}
scale = Math.min(Math.max(.125, scale), 4); // Restrict scale
// Restrict movement within the boundaries
const rect = currentImage.getBoundingClientRect();
const galleryRect = gallery.getBoundingClientRect();
// Calculate overflows
const overflowX = Math.max(0, rect.width * scale - galleryRect.width);
const overflowY = Math.max(0, rect.height * scale - galleryRect.height);
// Clamp translateX and translateY within allowed bounds
translateX = Math.min(overflowX / 2, Math.max(-overflowX / 2, translateX));
translateY = Math.min(overflowY / 2, Math.max(-overflowY / 2, translateY));
currentImage.style.transform = `translate(${translateX}px, ${translateY}px) scale(${scale})`;
}
});
// Add click event to hide loading screen in case of manual reset
loading.addEventListener('click', () => {
loading.style.display = 'none';
});
// Handle image paste from clipboard
window.addEventListener('paste', (event) => {
const items = event.clipboardData.items;
for (const item of items) {
if (item.type.startsWith('image/')) {
const file = item.getAsFile();
thumbnails.innerHTML = ''; // Clear existing thumbnails on paste
handleFiles([file]);
}
}
});
</script>
</body>
</html>
|
cs |
app.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
|
from flask import Flask, request, url_for, render_template, send_from_directory, jsonify
from time import time
import os
import cv2
app = Flask(__name__)
UPLOAD_FOLDER = r'D:\server\static\uploads'
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER
app.config['MAX_CONTENT_PATH'] = 16 * 1024 * 1024
# Ensure the upload directory exists
if not os.path.exists(app.config['UPLOAD_FOLDER']):
os.makedirs(app.config['UPLOAD_FOLDER'])
@app.route('/')
def index():
return render_template('index.html')
@app.route('/upload', methods=['POST'])
def upload_file():
if 'file' not in request.files:
return 'No file part'
file = request.files['file']
if file.filename == '':
return 'No selected file'
if file:
image_paths = []
input_image_path = 'input_image.jpg'
filepath = os.path.join(app.config['UPLOAD_FOLDER'], input_image_path)
file.save(filepath)
image_paths.append(url_for('uploaded_file', filename=input_image_path) + '?' + str(int(time())))
img = cv2.imread(filepath)
gray_img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray_filepath = os.path.join(app.config['UPLOAD_FOLDER'], 'gray_' + os.path.basename(filepath))
cv2.imwrite(gray_filepath, gray_img)
image_paths.append(url_for('uploaded_file', filename='gray_' + os.path.basename(filepath)) + '?' + str(int(time())))
# Return the URL to access the uploaded file
return jsonify(images=image_paths)
return 'File not uploaded correctly'
@app.route('/uploads/<filename>')
def uploaded_file(filename):
return send_from_directory(app.config['UPLOAD_FOLDER'], filename)
if __name__ == '__main__':
# 모든 외부 접속 허용시 host='0,0,0,0' 설정
app.run(debug=True, host='0,0,0,0', port=5000)
|
cs |
- 결과
저는 지금까지 제 블로그에는 제가 직접 작성한 소스코드를 업로드 해왔었습니다. 그러했던 제가 최근에는 ChatGPT에게 원하는 시스템의 전반적인 내용을 작성 시키고, 저는 그 코드에서 제가 원하는 기능들을 덮붙이는 식으로 일을 수행하고 있습니다. AI를 연구하는 입장에서 어느덧 AI와 함께 협업을 하며 일을 하는 세상이 되었다는 것이 참으로 놀라운 세상이네요. 앞으로도 ChatGPT를 사용해 업무 효율을 높일수 있는 기회들이 이번처럼 많아진다면 앞으로는 지금보다 업무 생산성이 많이 높아질 것이라 기대를 해봅니다.
300x250
'공대생의 팁' 카테고리의 다른 글
Windows Powershell에서 python 실행시 환경변수 설정 방법 (0) | 2024.05.28 |
---|---|
[mmcv] AssertionError: only one of size and size_divisor should be valid (0) | 2024.03.31 |
LabelMe로 Coco 데이터셋 변환후 MMDetection에서 학습이 안될 때 해결 방법 (0) | 2024.03.26 |
MMOCR로 OCR 이해하기(2) - OCR 데이터셋 만들기 (0) | 2024.01.31 |
MMCV에서 사용되는 pretrained model 주소 알아내는 방법 (0) | 2023.11.05 |
설정
트랙백
댓글
글
중앙선에 남은 마지막 아담한 간이역 - 화본역(2024.07.27)
좌충우돌 여행기/국내여행
2024. 9. 30. 23:51
1939년 4월 처음으로 개통한 중앙선은 1942년 4월 청량리-경주 전구간이 개통되었을 당시 많은 승객들이 각 지역을 잇는 구불구불한 철로를 따라 서있던 간이역들을 통해 열차를 이용해왔었습니다. 중앙선 개통 이라 86년의 세월동안 많은 것이 변하였는데 특히 청량리에서 부전까지 비둘기호가 반나절 넘게 달려야 완주하던 중앙선 철길은 직선화되면서 소요시간이 점점 줄어들었고 이제는 무궁화호를 타고 무려 6시간 남짓이면 될 정도로 소요시간이 무려 절반이나 줄었습니다. 심지어 전철화까지 완료되어 KTX-이음이 달리게 되면 시간을 이보다 더 줄어들 예정이니 말 그대로 격세지감이라는 어르신들의 말의 의미가 이런 경우에 쓰임을 깨닫습니다.
2005년 청량리-덕소 구간 복선화 및 직선화를 시작으로 양평, 원주, 제천, 단양, 영주, 안동, 의성까지 차례차례 진행되었고 어느덧 이러한 변화는 의성-북영천 구간을 제외한 전 구간이 완공되었습니다. 이러한 변화를 아는지 모르는지 내년부로 더이상 열차가 다니지 않게될 화본역은 오늘도 무궁화호를 타고 찾아온 승객들을 맞이하고 있었습니다.
아화역에서 무궁화호를 타고 화본역에 도착하였습니다.
전역과 다음역인 두 역은 여객 영업을 하지 않고 있습니다.
내년 선로가 이설되면 두 역은 승객 없이 쓸쓸히 마지막 열차를 보내겠지요.
퇴역한 새마을호 객차가 역 인근에서 카페로 사용되고 있는 듯 합니다.
화본역에도 기관차가 다니던 시절 사용되었던 급수탑이 있습니다.
역 구내는 올해 마지막 영업하는 역 치고는 상당히 잘 관리되고 있습니다.
옛날 양식의 역명판도 그대로 재현해 두었군요.
이전에는 이 곳에서 강릉역에도 갈 수 있었지요?
어느덧 열차 문이 닫히고
열차는 청량리역을 향히 달려갑니다.
역사안으로 들어가봅니다.
역의 유명세 덕에 현대 양식이 아닌 옛모습으로 리모델링된 역사의 모습입니다.
승차권이 없는 방문객의 경우 기념권 성격의 입장권을 구매후 역무원의 안내에 따라 승강장에 입장할 수 있습니다.
열차는 하루에 총 6회 정차하네요.
그러고보니 저는 화본역 방문으로 군위는 처음 방문해보네요.
역광장은 상당히 넓습니다.
치즈냥 한 마리가 더운날 휴식을 취하고 있습니다.
군위군이 대구광역시에 편입됨에 따라 경상북도라고 적혀있어야 할 부분을 가렸네요.
무더웠던 2024년 7월말이었어서 열차카페에서 오미자에이드 한잔 샀습니다.
멀리서 바라본 화본역 역명판
삼국유사 군위를 형상화한 듯 한 캐릭터들일까요?
인근에 화본역에 대한 정보를 설명하는 비석이 보입니다.
한적한 시골 간이역 치고는 광장이 나름 넓습니다.
진입로에서 바라본 화본역
역세권에 무려 식당도 있습니다!
다음 열차가 들어오기 전 역앞 마을 구경을 잠시 하다가
아화역으로 돌아가기 위해 다시 승강장으로 들어옵니다.
요 역명판도 철도청 시절에 쓰던 것으로 보이는데?
역 주변 구경에 정신이 팔려있던 찰나에 벌써 열차가 들어옵니다.
새로운 철로로 이설되면 이 구간에서 디젤기관차를 볼 기회기 많지는 않겠죠?
잠시 짬을 내어 찾아온 간이역 여행을 마치고 다시 일상으로 돌아가봅니다!
300x250
'좌충우돌 여행기 > 국내여행' 카테고리의 다른 글
뒤바뀐 운명 - 중앙선 건천역과 아화역(2024.07.27) (0) | 2024.07.28 |
---|---|
추운 겨울 한적한 마을에 역이 다시 들어서다 - 경의선 운천역[2022.12.18] (0) | 2022.12.23 |
열차가 멈추지 않는 정원속 간이역 - 경전전 남평역[2022.06.01] (0) | 2022.06.14 |
도심속 꼬마열차가 다니는 작은 간이역 - 광주선 극락강역[2022.06.01] (0) | 2022.06.11 |
석탑, 금성(탑리)버스터미널, 그리고 탑리역[2022.03.26] (1) | 2022.04.06 |
설정
트랙백
댓글
글
MMCV 라이브러리로 Custom AI모델을 만들어보자!(1) - Backbone 추가하기
프로그래밍 팁
2024. 8. 31. 23:39
Pytorch는 Meta AI(구 페이스북 AI연구소)에서 만든 딥러닝 라이브러리로, 오늘날 AI 관련 논문등에서 많이 사용되고 있으며 Github를 통해 오픈소스로 공개되는 AI모델 다수가 Pytorch를 사용하고 있습니다. 과거 Google의 TensorFlow가 산업용 AI분야에 주로 사용되고 있다고 알려져 있으나 대학원에서 Pytorch를 접했던 연구원들이 산업 현장에서 Pytorch 사용을 이어가게 되면서 산업용 AI분야에서도 Pytorch의 점유율이 높아져가고 있습니다.
TensorFlow에 비해 Pytorch가 갖는 강점으로 모델 설계가 직관적이고 수정이 쉽다는 점입니다. 다만, Pytorch로 자신이 원하는 모델을 만들 수 있다 하더라도 지금까지 공개된 수많은 모델들을 일일히 공부하고 이를 이해하고 설계를 하는 과정은 개발시간이 소요되며, 개발자별 소스코드의 구조에 차이가 있을 경우 모델 설계를 처음부터 하는 것이 나을 정도로 복잡한 과정이 필요할 수 있습니다. 심지어 같은 구조의 AI모델임에도 소스코드 구조가 달라지게 되면 각각의 모델들이 파편화되어 모델 구조 관리에 더 큰 어려움이 생기게 되기 마련입니다.
OpenMMLab에서 공개한 오픈소스 라이브러리인 MMCV는 자주 사용되는 기능들을 단일화하고, 기본에 공개된 AI모델들을 MMCV 라이브러리로 설계하여 모델들의 구조를 단일화하여 개발자 입장에서 간단하게 소스코드 일부 수정만으로 AI모델을 쉽게 변경할 수 있어 개발이 쉽습니다.
이번 포스팅에서는 MMCV라이브러리에 자신이 직접 모델을 만들어 적용하는 방법을 소개시켜드리고자 합니다.
본 포스팅에서는 MMSegmentation을 기준으로 설명드리도록 하겠습니다. MMSegmentation 라이브러리에서 FCN모델은 Backbone으로 ResNet과 HRNet이 기본으로 적용되어 있는데, 저는 여기에 VGG 백본을 추가해보고자 합니다.
VGG는 2014년 공개된 AI분야 입장에서 보았을 땐 고전 CNN 구조의 모델입니다. 지금 시점에서 보았을 땐 단순한 구조로서 CNN, max pooling, Relu, softmax 등으로 Layer가 구성되어 있어, AI에 입문하시는 분들께서 자신이 직접 AI모델을 만드는 실습자료로 훌륭한 모델 중 하나입니다.
MMCV 라이브러리에서 VGG 모델이 기본으로 제공되고 있어, MMSegmentation에서는 VGG모델을 상속하는 방법으로 Backbone 모델을 추가해보겠습니다. 아래와 같은 경로에 소스코드를 추가해줍니다.
mmseg/models/backbones/FCNVGG.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
|
import warnings
import torch.nn as nn
from typing import List, Optional, Sequence, Tuple, Union
from mmcv.cnn Import VGG
from mmseg.registry import MODELS
@MODELS.register_module()
class FCNVGG(VGG):
def __init__(self,
depth: int,
with_bn: bool = False,
num_classes: int = -1,
num_stages: int = 5,
dilations: Sequence[int] = (1, 1, 1, 1, 1),
out_indices: Sequence[int] = (0, 1, 2, 3, 4),
frozen_stages: int = -1,
bn_eval: bool = True,
bn_frozen: bool = False,
ceil_mode: bool = False,
with_label_pool: bool = True,
pretrained = None,
init_cfg = None):
super().__init__(
depth,
with_bn,
num_classes,
dilations,
out_indies,
frozem_stages,
bn_eval,
bn_frozen,
ceil_mode,
with_last_pool)
assert not (init_cfg and pretrained), \
'init_cfg and pretrained cannot be specified at the same time'
if init_cfg is not None:
self.init_cfg = init_cfg
elif isinstance(pretrained, str):
warnings.warn('DeprecationWarning: pretrained is deprecated, '
'please use "init_cfg" instead')
self.init_cfg = dict(type='Pretrained', checkpoint=pretrained)
elif pretrained is None:
self.init_cfg = [
dict(type='Kaiming', layer='Conv2d'),
dict(type='Constant', val=1, layer='BatchNorm2d'),
dict(type='Normal', std=0.01, layer='Linear'),
]
else:
raise TypeError('pretrained must e a str or None')
def init_weights(self, pretrained=None):
super().init_weights(pretrained)
def forward(self, x): # should return a tuple
result = super().forward(x)
return result
|
cs |
추가한 Backbone 모델이 mmsegmentation 라이브러리 import시 불러오도록 설정합니다.
mmseg/models/backbones/__init__.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
|
# Copyright (c) OpenMMLab. All rights reserved.
from .beit import BEiT
from .bisenetv1 import BiSeNetV1
from .bisenetv2 import BiSeNetV2
from .cgnet import CGNet
from .ddrnet import DDRNet
from .erfnet import ERFNet
from .fast_scnn import FastSCNN
from .hrnet import HRNet
from .icnet import ICNet
from .mae import MAE
from .mit import MixVisionTransformer
from .mobilenet_v2 import MobileNetV2
from .mobilenet_v3 import MobileNetV3
from .mscan import MSCAN
from .pidnet import PIDNet
from .resnest import ResNeSt
from .resnet import ResNet, ResNetV1c, ResNetV1d
from .resnext import ResNeXt
from .stdc import STDCContextPathNet, STDCNet
from .swin import SwinTransformer
from .timm_backbone import TIMMBackbone
from .twins import PCPVT, SVT
from .unet import UNet
from .vit import VisionTransformer
from .vpd import VPD
__all__ = [
'ResNet', 'ResNetV1c', 'ResNetV1d', 'ResNeXt', 'HRNet', 'FastSCNN',
'ResNeSt', 'MobileNetV2', 'UNet', 'CGNet', 'MobileNetV3',
'VisionTransformer', 'SwinTransformer', 'MixVisionTransformer',
'BiSeNetV1', 'BiSeNetV2', 'ICNet', 'TIMMBackbone', 'ERFNet', 'PCPVT',
'SVT', 'STDCNet', 'STDCContextPathNet', 'BEiT', 'MAE', 'PIDNet', 'MSCAN',
'DDRNet', 'VPD', 'FCNVGG'
]
|
cs |
끝으로, VGG를 Backbone으로 하는 FCN모델을 Config로 구성해줍니다.
fcn_vgg16.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
|
_base_ = [
'configs/_base_/datasets/cityscapes.py',
'configs/_base_/default_runtime.py',
'configs/_base_/schedules/schedule_40k.py'
]
# model settings
norm_cfg = dict(type='SyncBN', requires_grad=True)
data_preprocessor = dict(
type='SegDataPreProcessor',
mean=[123.675, 116.28, 103.53],
std=[58.395, 57.12, 57.375],
bgr_to_rgb=True,
pad_val=0,
size=(512,512),
seg_pad_val=255)
model = dict(
type='EncoderDecoder',
data_preprocessor=data_preprocessor,
backbone=dict(
type='FCNVGG',
depth=16,
with_last_pool=False,
ceil_mode=True,
init_cfg=dict(
type='Pretrained',
checkpoint='open-mmlab://vgg16_caffe'),
decode_head=dict(
type='FCNHead',
in_channels=512, # VGG모델의 출력 채널수와 일치시킬것
in_index=4, # VGG모델의 출력 Layer중 선택(5개의 Layer중 5번째)
channels=512,
num_convs=2,
concat_input=True,
dropout_ratio=0.1,
num_classes=80,
norm_cfg=norm_cfg,
align_corners=False,
loss_decode=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
auxiliary_head=dict(
type='FCNHead',
in_channels=512, # VGG모델의 출력 채널수와 일치시킬것
in_index=3, # VGG모델의 출력 Layer중 선택(5개의 Layer중 4번째)
channels=256,
num_convs=1,
concat_input=False,
dropout_ratio=0.1,
num_classes=80,
norm_cfg=norm_cfg,
align_corners=False,
loss_decode=dict(
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)),
# model training and testing settings
train_cfg = dict()
test_cfg = dict(mode='whole'))
|
cs |
위와 같이 설정해주신 다음 아래의 명령어를 실행하시면 VGG Backbone이 적용된 AI모델이 학습되는 것을 확인하실 수 있습니다.
$ python tools/train.py fcn_vgg16.py
300x250
'프로그래밍 팁' 카테고리의 다른 글
회전 이미지를 직사각형으로 Labeling하는 방법 - roLabelImg (0) | 2024.02.25 |
---|---|
Windows 환경에서 MMDeploy로 TensorRT 구동(Python, C++) (0) | 2023.10.31 |
인공지능 모델 최적화 배포 라이브러리 - MMDeploy (0) | 2023.04.08 |
MMCV에서 직접 만든 데이터셋 학습 시키기(COCO, Pascal VOC Custom Dataset 만들기) (0) | 2023.03.27 |
복합 이벤트 처리 라이브러리 Esper를 VScode에서 실행해보기 (1) | 2022.10.09 |