feat(prediction): 이미지 분석 서버 Docker 패키징 + DB 코드 제거
- prediction/image/ FastAPI 서버 Docker 환경 구성 - Dockerfile: PyTorch 2.1 + CUDA 12.1 기반 GPU 이미지 - docker-compose.yml: GPU 할당 + 데이터 볼륨 마운트 - requirements.txt: 서버 의존성 목록 - .env.example: 환경변수 템플릿 - DOCKER_USAGE.md: 빌드/실행/API 사용법 문서 - Dockerfile에 .dockerignore 제외 폴더 mkdir -p 추가 - .gitignore: prediction/image 결과물 및 모델 가중치(.pth) 제외 추가 - dbInsert_csv.py, dbInsert_shp.py 삭제 (미사용 DB 로직) - api.py: dbInsert import 및 주석 처리된 DB 호출 코드 제거 - aerialRouter.ts: req.params 타입 오류 수정
This commit is contained in:
부모
b601edd741
커밋
3946ff6a25
12
.gitignore
vendored
12
.gitignore
vendored
@ -58,10 +58,22 @@ __pycache__/
|
|||||||
# prediction/ Python 엔진 (로컬 실행 결과물)
|
# prediction/ Python 엔진 (로컬 실행 결과물)
|
||||||
prediction/**/__pycache__/
|
prediction/**/__pycache__/
|
||||||
prediction/**/*.pyc
|
prediction/**/*.pyc
|
||||||
|
# prediction/ opendrift 결과물 (로컬 실행 결과물)
|
||||||
prediction/opendrift/result/
|
prediction/opendrift/result/
|
||||||
prediction/opendrift/logs/
|
prediction/opendrift/logs/
|
||||||
prediction/opendrift/uvicorn.pid
|
prediction/opendrift/uvicorn.pid
|
||||||
prediction/opendrift/.env
|
prediction/opendrift/.env
|
||||||
|
# prediction/ 이미지분석 결과물 (로컬 실행 결과물)
|
||||||
|
prediction/image/stitch/
|
||||||
|
prediction/image/mx15hdi/Detect/Mask_result/
|
||||||
|
prediction/image/mx15hdi/Detect/result/
|
||||||
|
prediction/image/mx15hdi/Georeference/Mask_Tif/
|
||||||
|
prediction/image/mx15hdi/Georeference/Tif/
|
||||||
|
prediction/image/mx15hdi/Metadata/CSV/
|
||||||
|
prediction/image/mx15hdi/Metadata/Image/Original_Images/
|
||||||
|
prediction/image/mx15hdi/Polygon/Shp/
|
||||||
|
# prediction/ 이미지분석 대용량 바이너리 (모델 가중치)
|
||||||
|
prediction/image/**/*.pth
|
||||||
|
|
||||||
# HNS manual images (large binary)
|
# HNS manual images (large binary)
|
||||||
frontend/public/hns-manual/pages/
|
frontend/public/hns-manual/pages/
|
||||||
|
|||||||
124
backend/package-lock.json
generated
124
backend/package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "backend",
|
"name": "backend",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/multer": "^2.1.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@ -17,6 +18,7 @@
|
|||||||
"google-auth-library": "^10.6.1",
|
"google-auth-library": "^10.6.1",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
|
"multer": "^2.1.1",
|
||||||
"pg": "^8.19.0"
|
"pg": "^8.19.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@ -515,7 +517,6 @@
|
|||||||
"version": "1.19.6",
|
"version": "1.19.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
|
||||||
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
|
"integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/connect": "*",
|
"@types/connect": "*",
|
||||||
@ -526,7 +527,6 @@
|
|||||||
"version": "3.4.38",
|
"version": "3.4.38",
|
||||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||||
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@ -556,9 +556,7 @@
|
|||||||
"version": "5.0.6",
|
"version": "5.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz",
|
||||||
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
"integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/body-parser": "*",
|
"@types/body-parser": "*",
|
||||||
"@types/express-serve-static-core": "^5.0.0",
|
"@types/express-serve-static-core": "^5.0.0",
|
||||||
@ -569,7 +567,6 @@
|
|||||||
"version": "5.1.1",
|
"version": "5.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz",
|
||||||
"integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
|
"integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*",
|
"@types/node": "*",
|
||||||
@ -592,7 +589,6 @@
|
|||||||
"version": "2.0.5",
|
"version": "2.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
|
||||||
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
|
"integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/jsonwebtoken": {
|
"node_modules/@types/jsonwebtoken": {
|
||||||
@ -613,11 +609,19 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/@types/multer": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/multer/-/multer-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-zYZb0+nJhOHtPpGDb3vqPjwpdeGlGC157VpkqNQL+UU2qwoacoQ7MpsAmUptI/0Oa127X32JzWDqQVEXp2RcIA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@types/express": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@types/node": {
|
"node_modules/@types/node": {
|
||||||
"version": "22.19.11",
|
"version": "22.19.11",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.11.tgz",
|
||||||
"integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==",
|
"integrity": "sha512-BH7YwL6rA93ReqeQS1c4bsPpcfOmJasG+Fkr6Y59q83f9M1WcBRHR2vM+P9eOisYRcN3ujQoiZY8uk5W+1WL8w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"undici-types": "~6.21.0"
|
"undici-types": "~6.21.0"
|
||||||
@ -639,21 +643,18 @@
|
|||||||
"version": "6.14.0",
|
"version": "6.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
|
||||||
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
"integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/range-parser": {
|
"node_modules/@types/range-parser": {
|
||||||
"version": "1.2.7",
|
"version": "1.2.7",
|
||||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/@types/send": {
|
"node_modules/@types/send": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
|
||||||
"integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
|
"integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "*"
|
"@types/node": "*"
|
||||||
@ -663,7 +664,6 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz",
|
||||||
"integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
|
"integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/http-errors": "*",
|
"@types/http-errors": "*",
|
||||||
@ -716,6 +716,12 @@
|
|||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/append-field": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/array-flatten": {
|
"node_modules/array-flatten": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
|
||||||
@ -837,6 +843,23 @@
|
|||||||
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
|
||||||
"license": "BSD-3-Clause"
|
"license": "BSD-3-Clause"
|
||||||
},
|
},
|
||||||
|
"node_modules/buffer-from": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
|
"node_modules/busboy": {
|
||||||
|
"version": "1.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
||||||
|
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
||||||
|
"dependencies": {
|
||||||
|
"streamsearch": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.16.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/bytes": {
|
"node_modules/bytes": {
|
||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||||
@ -893,6 +916,21 @@
|
|||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/concat-stream": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
|
||||||
|
"engines": [
|
||||||
|
"node >= 6.0"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"buffer-from": "^1.0.0",
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"readable-stream": "^3.0.2",
|
||||||
|
"typedarray": "^0.0.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/content-disposition": {
|
"node_modules/content-disposition": {
|
||||||
"version": "0.5.4",
|
"version": "0.5.4",
|
||||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
|
||||||
@ -1841,6 +1879,25 @@
|
|||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/multer": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/multer/-/multer-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"append-field": "^1.0.0",
|
||||||
|
"busboy": "^1.6.0",
|
||||||
|
"concat-stream": "^2.0.0",
|
||||||
|
"type-is": "^1.6.18"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.16.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/express"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/negotiator": {
|
"node_modules/negotiator": {
|
||||||
"version": "0.6.3",
|
"version": "0.6.3",
|
||||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
|
||||||
@ -1992,7 +2049,6 @@
|
|||||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.19.0.tgz",
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.19.0.tgz",
|
||||||
"integrity": "sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==",
|
"integrity": "sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"peer": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"pg-connection-string": "^2.11.0",
|
"pg-connection-string": "^2.11.0",
|
||||||
"pg-pool": "^3.12.0",
|
"pg-pool": "^3.12.0",
|
||||||
@ -2180,6 +2236,20 @@
|
|||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/readable-stream": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"string_decoder": "^1.1.1",
|
||||||
|
"util-deprecate": "^1.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/resolve-pkg-maps": {
|
"node_modules/resolve-pkg-maps": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
|
||||||
@ -2426,6 +2496,23 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/streamsearch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/string_decoder": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||||
|
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safe-buffer": "~5.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/string-width": {
|
"node_modules/string-width": {
|
||||||
"version": "5.1.2",
|
"version": "5.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
||||||
@ -2564,6 +2651,12 @@
|
|||||||
"node": ">= 0.6"
|
"node": ">= 0.6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/typedarray": {
|
||||||
|
"version": "0.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
|
||||||
|
"integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/typescript": {
|
"node_modules/typescript": {
|
||||||
"version": "5.9.3",
|
"version": "5.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
|
||||||
@ -2582,7 +2675,6 @@
|
|||||||
"version": "6.21.0",
|
"version": "6.21.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/unpipe": {
|
"node_modules/unpipe": {
|
||||||
@ -2594,6 +2686,12 @@
|
|||||||
"node": ">= 0.8"
|
"node": ">= 0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/util-deprecate": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/utils-merge": {
|
"node_modules/utils-merge": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
"db:seed": "tsx src/db/seed.ts"
|
"db:seed": "tsx src/db/seed.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@types/multer": "^2.1.0",
|
||||||
"bcrypt": "^6.0.0",
|
"bcrypt": "^6.0.0",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
@ -18,6 +19,7 @@
|
|||||||
"google-auth-library": "^10.6.1",
|
"google-auth-library": "^10.6.1",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
"jsonwebtoken": "^9.0.3",
|
"jsonwebtoken": "^9.0.3",
|
||||||
|
"multer": "^2.1.1",
|
||||||
"pg": "^8.19.0"
|
"pg": "^8.19.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@ -1,7 +1,10 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
import multer from 'multer';
|
||||||
import {
|
import {
|
||||||
listMedia,
|
listMedia,
|
||||||
createMedia,
|
createMedia,
|
||||||
|
getMediaBySn,
|
||||||
|
fetchOriginalImage,
|
||||||
listCctv,
|
listCctv,
|
||||||
listSatRequests,
|
listSatRequests,
|
||||||
createSatRequest,
|
createSatRequest,
|
||||||
@ -9,11 +12,13 @@ import {
|
|||||||
isValidSatStatus,
|
isValidSatStatus,
|
||||||
requestOilInference,
|
requestOilInference,
|
||||||
checkInferenceHealth,
|
checkInferenceHealth,
|
||||||
|
stitchImages,
|
||||||
} from './aerialService.js';
|
} from './aerialService.js';
|
||||||
import { isValidNumber } from '../middleware/security.js';
|
import { isValidNumber } from '../middleware/security.js';
|
||||||
import { requireAuth, requirePermission } from '../auth/authMiddleware.js';
|
import { requireAuth, requirePermission } from '../auth/authMiddleware.js';
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
const stitchUpload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 50 * 1024 * 1024 } });
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// AERIAL_MEDIA 라우트
|
// AERIAL_MEDIA 라우트
|
||||||
@ -63,6 +68,40 @@ router.post('/media', requireAuth, requirePermission('aerial', 'CREATE'), async
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// GET /api/aerial/media/:sn/download — 원본 이미지 다운로드
|
||||||
|
router.get('/media/:sn/download', requireAuth, requirePermission('aerial', 'READ'), async (req, res) => {
|
||||||
|
try {
|
||||||
|
const sn = parseInt(req.params['sn'] as string, 10);
|
||||||
|
if (!isValidNumber(sn, 1, 999999)) {
|
||||||
|
res.status(400).json({ error: '유효하지 않은 미디어 번호' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const media = await getMediaBySn(sn);
|
||||||
|
if (!media) {
|
||||||
|
res.status(404).json({ error: '미디어를 찾을 수 없습니다.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// fileId 추출: FILE_NM의 앞 36자가 UUID 형식인지 검증 (이미지 분석 생성 레코드만 다운로드 가능)
|
||||||
|
const fileId = media.fileNm.substring(0, 36);
|
||||||
|
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
||||||
|
if (!UUID_PATTERN.test(fileId) || !media.equipNm) {
|
||||||
|
res.status(404).json({ error: '다운로드 가능한 이미지가 없습니다.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buffer = await fetchOriginalImage(media.equipNm, fileId);
|
||||||
|
const downloadName = media.orgnlNm ?? media.fileNm;
|
||||||
|
res.setHeader('Content-Type', 'image/jpeg');
|
||||||
|
res.setHeader('Content-Disposition', `attachment; filename*=UTF-8''${encodeURIComponent(downloadName)}`);
|
||||||
|
res.send(buffer);
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[aerial] 이미지 다운로드 오류:', err);
|
||||||
|
res.status(502).json({ error: '이미지 다운로드 실패' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// CCTV_CAMERA 라우트
|
// CCTV_CAMERA 라우트
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@ -257,6 +296,39 @@ router.post('/oil-detect', express.json({ limit: '3mb' }), requireAuth, requireP
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// STITCH (이미지 합성) 라우트
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
// POST /api/aerial/stitch — 여러 이미지를 합성하여 JPEG 반환
|
||||||
|
router.post(
|
||||||
|
'/stitch',
|
||||||
|
requireAuth,
|
||||||
|
requirePermission('aerial', 'READ'),
|
||||||
|
stitchUpload.array('files', 6),
|
||||||
|
async (req, res) => {
|
||||||
|
try {
|
||||||
|
const files = req.files as Express.Multer.File[];
|
||||||
|
if (!files || files.length < 2) {
|
||||||
|
res.status(400).json({ error: '이미지를 최소 2장 이상 선택해주세요.' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const fileId = `stitch_${Date.now()}`;
|
||||||
|
const buffer = await stitchImages(files, fileId);
|
||||||
|
res.type('image/jpeg').send(buffer);
|
||||||
|
} catch (err) {
|
||||||
|
const message = err instanceof Error ? err.message : String(err);
|
||||||
|
if (message.includes('abort') || message.includes('timeout')) {
|
||||||
|
console.error('[aerial] 스티칭 서버 타임아웃:', message);
|
||||||
|
res.status(504).json({ error: '이미지 합성 서버 응답 시간 초과' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.error('[aerial] 이미지 합성 오류:', err);
|
||||||
|
res.status(503).json({ error: '이미지 합성 서버 연결 불가' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// GET /api/aerial/oil-detect/health — 추론 서버 상태 확인
|
// GET /api/aerial/oil-detect/health — 추론 서버 상태 확인
|
||||||
router.get('/oil-detect/health', requireAuth, async (_req, res) => {
|
router.get('/oil-detect/health', requireAuth, async (_req, res) => {
|
||||||
const health = await checkInferenceHealth();
|
const health = await checkInferenceHealth();
|
||||||
|
|||||||
@ -49,6 +49,26 @@ function rowToMedia(r: Record<string, unknown>): AerialMediaItem {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getMediaBySn(sn: number): Promise<AerialMediaItem | null> {
|
||||||
|
const { rows } = await wingPool.query(
|
||||||
|
`SELECT AERIAL_MEDIA_SN, ACDNT_SN, FILE_NM, ORGNL_NM, FILE_PATH,
|
||||||
|
LON, LAT, LOC_DC, EQUIP_TP_CD, EQUIP_NM, MEDIA_TP_CD,
|
||||||
|
TAKNG_DTM, FILE_SZ, RESOLUTION, REG_DTM
|
||||||
|
FROM wing.AERIAL_MEDIA WHERE AERIAL_MEDIA_SN = $1 AND USE_YN = 'Y'`,
|
||||||
|
[sn]
|
||||||
|
);
|
||||||
|
return rows.length > 0 ? rowToMedia(rows[0]) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchOriginalImage(camTy: string, fileId: string): Promise<Buffer> {
|
||||||
|
const res = await fetch(`${IMAGE_ANALYSIS_URL}/get-original-image/${camTy}/${fileId}`, {
|
||||||
|
signal: AbortSignal.timeout(30_000),
|
||||||
|
});
|
||||||
|
if (!res.ok) throw new Error(`이미지 서버 응답: ${res.status}`);
|
||||||
|
const base64 = await res.json() as string;
|
||||||
|
return Buffer.from(base64, 'base64');
|
||||||
|
}
|
||||||
|
|
||||||
export async function listMedia(input: ListMediaInput): Promise<AerialMediaItem[]> {
|
export async function listMedia(input: ListMediaInput): Promise<AerialMediaItem[]> {
|
||||||
const conditions: string[] = ["USE_YN = 'Y'"];
|
const conditions: string[] = ["USE_YN = 'Y'"];
|
||||||
const params: (string | number)[] = [];
|
const params: (string | number)[] = [];
|
||||||
@ -109,8 +129,8 @@ export async function createMedia(input: {
|
|||||||
TAKNG_DTM, FILE_SZ, RESOLUTION
|
TAKNG_DTM, FILE_SZ, RESOLUTION
|
||||||
) VALUES (
|
) VALUES (
|
||||||
$1, $2, $3, $4,
|
$1, $2, $3, $4,
|
||||||
$5, $6,
|
$5::float8, $6::float8,
|
||||||
CASE WHEN $5 IS NOT NULL AND $6 IS NOT NULL THEN ST_SetSRID(ST_MakePoint($5::float, $6::float), 4326) END,
|
CASE WHEN $5 IS NOT NULL AND $6 IS NOT NULL THEN ST_SetSRID(ST_MakePoint($5, $6), 4326) END,
|
||||||
$7, $8, $9, $10,
|
$7, $8, $9, $10,
|
||||||
$11, $12, $13
|
$11, $12, $13
|
||||||
) RETURNING AERIAL_MEDIA_SN`,
|
) RETURNING AERIAL_MEDIA_SN`,
|
||||||
@ -344,7 +364,8 @@ export async function updateSatRequestStatus(sn: number, sttsCd: string): Promis
|
|||||||
// OIL INFERENCE (GPU 서버 프록시)
|
// OIL INFERENCE (GPU 서버 프록시)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
||||||
const OIL_INFERENCE_URL = process.env.OIL_INFERENCE_URL || 'http://localhost:8090';
|
const OIL_INFERENCE_URL = process.env.OIL_INFERENCE_URL || 'http://localhost:5001';
|
||||||
|
const IMAGE_ANALYSIS_URL = process.env.IMAGE_ANALYSIS_URL || OIL_INFERENCE_URL;
|
||||||
const INFERENCE_TIMEOUT_MS = 10_000;
|
const INFERENCE_TIMEOUT_MS = 10_000;
|
||||||
|
|
||||||
export interface OilInferenceRegion {
|
export interface OilInferenceRegion {
|
||||||
@ -362,6 +383,28 @@ export interface OilInferenceResult {
|
|||||||
regions: OilInferenceRegion[];
|
regions: OilInferenceRegion[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 여러 이미지를 이미지 분석 서버의 /stitch 엔드포인트로 전송해 합성 JPEG를 반환한다. */
|
||||||
|
export async function stitchImages(
|
||||||
|
files: Express.Multer.File[],
|
||||||
|
fileId: string
|
||||||
|
): Promise<Buffer> {
|
||||||
|
const form = new FormData();
|
||||||
|
form.append('fileId', fileId);
|
||||||
|
for (const f of files) {
|
||||||
|
form.append('files', new Blob([f.buffer], { type: f.mimetype }), f.originalname);
|
||||||
|
}
|
||||||
|
const response = await fetch(`${IMAGE_ANALYSIS_URL}/stitch`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: form,
|
||||||
|
signal: AbortSignal.timeout(300_000),
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
const detail = await response.text().catch(() => '');
|
||||||
|
throw new Error(`stitch server responded ${response.status}: ${detail}`);
|
||||||
|
}
|
||||||
|
return Buffer.from(await response.arrayBuffer());
|
||||||
|
}
|
||||||
|
|
||||||
/** GPU 추론 서버에 이미지를 전송하고 세그멘테이션 결과를 반환한다. */
|
/** GPU 추론 서버에 이미지를 전송하고 세그멘테이션 결과를 반환한다. */
|
||||||
export async function requestOilInference(imageBase64: string): Promise<OilInferenceResult> {
|
export async function requestOilInference(imageBase64: string): Promise<OilInferenceResult> {
|
||||||
const controller = new AbortController();
|
const controller = new AbortController();
|
||||||
|
|||||||
174
backend/src/prediction/imageAnalyzeService.ts
Normal file
174
backend/src/prediction/imageAnalyzeService.ts
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
import crypto from 'crypto';
|
||||||
|
import { wingPool } from '../db/wingDb.js';
|
||||||
|
import { createMedia } from '../aerial/aerialService.js';
|
||||||
|
|
||||||
|
const IMAGE_API_URL = process.env.IMAGE_API_URL ?? 'http://localhost:5001';
|
||||||
|
|
||||||
|
// 유류 클래스 → UI 유종명 매핑
|
||||||
|
const CLASS_ID_TO_OIL_TYPE: Record<string, string> = {
|
||||||
|
'검정': '벙커C유',
|
||||||
|
'갈색': '벙커C유',
|
||||||
|
'무지개': '경유',
|
||||||
|
'은색': '등유',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 유종명 → DB 코드 매핑
|
||||||
|
const OIL_DB_CODE_MAP: Record<string, string> = {
|
||||||
|
'벙커C유': 'BUNKER_C',
|
||||||
|
'원유': 'CRUDE_OIL',
|
||||||
|
'경유': 'DIESEL',
|
||||||
|
'등유': 'GASOLINE',
|
||||||
|
};
|
||||||
|
|
||||||
|
interface OilPolygon {
|
||||||
|
classId: string;
|
||||||
|
area: number;
|
||||||
|
volume: number;
|
||||||
|
note: string;
|
||||||
|
thickness: number;
|
||||||
|
wkt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImageServerResponse {
|
||||||
|
meta: string;
|
||||||
|
data: OilPolygon[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageAnalyzeResult {
|
||||||
|
acdntSn: number;
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
oilType: string;
|
||||||
|
area: number;
|
||||||
|
volume: number;
|
||||||
|
fileId: string;
|
||||||
|
occurredAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* mx15hdi CSV 컬럼 순서:
|
||||||
|
* Filename, Tlat_d, Tlat_m, Tlat_s, Tlon_d, Tlon_m, Tlon_s,
|
||||||
|
* Alat_d, Alat_m, Alat_s, Alon_d, Alon_m, Alon_s,
|
||||||
|
* Az, El, Alt, Date1, Date2, Date3, Time1, Time2, Time3
|
||||||
|
*/
|
||||||
|
function parseMeta(metaStr: string): { lat: number; lon: number; occurredAt: string } {
|
||||||
|
const parts = metaStr.split(',');
|
||||||
|
const tlat_d = parseFloat(parts[1]);
|
||||||
|
const tlat_m = parseFloat(parts[2]);
|
||||||
|
const tlat_s = parseFloat(parts[3]);
|
||||||
|
const tlon_d = parseFloat(parts[4]);
|
||||||
|
const tlon_m = parseFloat(parts[5]);
|
||||||
|
const tlon_s = parseFloat(parts[6]);
|
||||||
|
|
||||||
|
const lat = tlat_d + tlat_m / 60 + tlat_s / 3600;
|
||||||
|
const lon = tlon_d + tlon_m / 60 + tlon_s / 3600;
|
||||||
|
|
||||||
|
// Date: Date1(DD), Date2(MM), Date3(YYYY) / Time: Time1(HH), Time2(mm), Time3(ss)
|
||||||
|
const dd = (parts[16] ?? '01').padStart(2, '0');
|
||||||
|
const mm = (parts[17] ?? '01').padStart(2, '0');
|
||||||
|
const yyyy = parts[18] ?? new Date().getFullYear().toString();
|
||||||
|
const time1 = (parts[19] ?? '00').padStart(2, '0');
|
||||||
|
const time2 = (parts[20] ?? '00').padStart(2, '0');
|
||||||
|
const occurredAt = `${yyyy}-${mm}-${dd}T${time1}:${time2}:00+09:00`;
|
||||||
|
|
||||||
|
return { lat, lon, occurredAt };
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function analyzeImageFile(imageBuffer: Buffer, originalName: string): Promise<ImageAnalyzeResult> {
|
||||||
|
const fileId = crypto.randomUUID();
|
||||||
|
|
||||||
|
// camTy는 현재 "mx15hdi"로 하드코딩한다.
|
||||||
|
// TODO: 추후 이미지 EXIF에서 카메라 모델명을 읽어 camTy를 자동 판별하는 로직을
|
||||||
|
// 이미지 분석 서버(api.py)에 추가할 예정이다. (check_camera_info 함수 활용)
|
||||||
|
const camTy = 'mx15hdi';
|
||||||
|
|
||||||
|
// 이미지 분석 서버 호출
|
||||||
|
const formData = new FormData();
|
||||||
|
const blob = new Blob([imageBuffer]);
|
||||||
|
formData.append('camTy', camTy);
|
||||||
|
formData.append('fileId', fileId);
|
||||||
|
formData.append('image', blob, originalName);
|
||||||
|
|
||||||
|
let serverResponse: ImageServerResponse;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${IMAGE_API_URL}/run-script/`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: formData,
|
||||||
|
signal: AbortSignal.timeout(300_000),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text();
|
||||||
|
if (res.status === 400 && text.includes('GPS')) {
|
||||||
|
throw Object.assign(new Error('GPS_NOT_FOUND'), { code: 'GPS_NOT_FOUND' });
|
||||||
|
}
|
||||||
|
throw new Error(`이미지 분석 서버 오류: ${res.status}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
serverResponse = await res.json() as ImageServerResponse;
|
||||||
|
} catch (err: unknown) {
|
||||||
|
if (err instanceof Error && (err as NodeJS.ErrnoException).code === 'GPS_NOT_FOUND') throw err;
|
||||||
|
if (err instanceof Error && err.name === 'TimeoutError') {
|
||||||
|
throw Object.assign(new Error('TIMEOUT'), { code: 'TIMEOUT' });
|
||||||
|
}
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 응답 파싱
|
||||||
|
const { lat, lon, occurredAt } = parseMeta(serverResponse.meta);
|
||||||
|
const firstOil = serverResponse.data[0];
|
||||||
|
const oilType = firstOil ? (CLASS_ID_TO_OIL_TYPE[firstOil.classId] ?? '벙커C유') : '벙커C유';
|
||||||
|
const area = firstOil?.area ?? 0;
|
||||||
|
const volume = firstOil?.volume ?? 0;
|
||||||
|
|
||||||
|
// ACDNT INSERT
|
||||||
|
const acdntNm = `이미지분석_${new Date().toISOString().slice(0, 16).replace('T', ' ')}`;
|
||||||
|
const acdntRes = await wingPool.query(
|
||||||
|
`INSERT INTO wing.ACDNT
|
||||||
|
(ACDNT_CD, ACDNT_NM, ACDNT_TP_CD, OCCRN_DTM, LAT, LNG, ACDNT_STTS_CD, USE_YN, REG_DTM)
|
||||||
|
VALUES (
|
||||||
|
'INC-' || EXTRACT(YEAR FROM NOW())::TEXT || '-' ||
|
||||||
|
LPAD(
|
||||||
|
(SELECT COALESCE(MAX(CAST(SPLIT_PART(ACDNT_CD, '-', 3) AS INTEGER)), 0) + 1
|
||||||
|
FROM wing.ACDNT
|
||||||
|
WHERE ACDNT_CD LIKE 'INC-' || EXTRACT(YEAR FROM NOW())::TEXT || '-%')::TEXT,
|
||||||
|
4, '0'
|
||||||
|
),
|
||||||
|
$1, '유류유출', $2, $3, $4, 'ACTIVE', 'Y', NOW()
|
||||||
|
)
|
||||||
|
RETURNING ACDNT_SN`,
|
||||||
|
[acdntNm, occurredAt, lat, lon]
|
||||||
|
);
|
||||||
|
const acdntSn: number = acdntRes.rows[0].acdnt_sn;
|
||||||
|
|
||||||
|
// SPIL_DATA INSERT (img_rslt_data에 분석 원본 저장)
|
||||||
|
await wingPool.query(
|
||||||
|
`INSERT INTO wing.SPIL_DATA
|
||||||
|
(ACDNT_SN, OIL_TP_CD, SPIL_QTY, SPIL_UNIT_CD, SPIL_TP_CD, FCST_HR, IMG_RSLT_DATA, REG_DTM)
|
||||||
|
VALUES ($1, $2, $3, 'KL', 'CONTINUOUS', 48, $4, NOW())`,
|
||||||
|
[
|
||||||
|
acdntSn,
|
||||||
|
OIL_DB_CODE_MAP[oilType] ?? 'BUNKER_C',
|
||||||
|
volume,
|
||||||
|
JSON.stringify(serverResponse),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
// AERIAL_MEDIA INSERT (영상사진관리 목록에서 조회 가능하도록 저장)
|
||||||
|
const fileSizeMb = (imageBuffer.length / (1024 * 1024)).toFixed(1) + ' MB';
|
||||||
|
await createMedia({
|
||||||
|
fileNm: `${fileId}_${originalName}`,
|
||||||
|
orgnlNm: originalName,
|
||||||
|
acdntSn,
|
||||||
|
lon,
|
||||||
|
lat,
|
||||||
|
locDc: `${lon.toFixed(4)} + ${lat.toFixed(4)}`,
|
||||||
|
equipTpCd: 'drone',
|
||||||
|
equipNm: camTy,
|
||||||
|
mediaTpCd: '사진',
|
||||||
|
takngDtm: occurredAt,
|
||||||
|
fileSz: fileSizeMb,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { acdntSn, lat, lon, oilType, area, volume, fileId, occurredAt };
|
||||||
|
}
|
||||||
@ -1,11 +1,15 @@
|
|||||||
import express from 'express';
|
import express from 'express';
|
||||||
|
import multer from 'multer';
|
||||||
import {
|
import {
|
||||||
listAnalyses, getAnalysisDetail, getBacktrack, listBacktracksByAcdnt,
|
listAnalyses, getAnalysisDetail, getBacktrack, listBacktracksByAcdnt,
|
||||||
createBacktrack, saveBoomLine, listBoomLines, getAnalysisTrajectory,
|
createBacktrack, saveBoomLine, listBoomLines, getAnalysisTrajectory,
|
||||||
} from './predictionService.js';
|
} from './predictionService.js';
|
||||||
|
import { analyzeImageFile } from './imageAnalyzeService.js';
|
||||||
import { isValidNumber } from '../middleware/security.js';
|
import { isValidNumber } from '../middleware/security.js';
|
||||||
import { requireAuth, requirePermission } from '../auth/authMiddleware.js';
|
import { requireAuth, requirePermission } from '../auth/authMiddleware.js';
|
||||||
|
|
||||||
|
const upload = multer({ storage: multer.memoryStorage(), limits: { fileSize: 50 * 1024 * 1024 } });
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
// GET /api/prediction/analyses — 분석 목록
|
// GET /api/prediction/analyses — 분석 목록
|
||||||
@ -144,4 +148,36 @@ router.post('/boom', requireAuth, requirePermission('prediction', 'CREATE'), asy
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// POST /api/prediction/image-analyze — 이미지 업로드 분석
|
||||||
|
router.post(
|
||||||
|
'/image-analyze',
|
||||||
|
requireAuth,
|
||||||
|
requirePermission('prediction', 'CREATE'),
|
||||||
|
upload.single('image'),
|
||||||
|
async (req, res) => {
|
||||||
|
try {
|
||||||
|
if (!req.file) {
|
||||||
|
res.status(400).json({ error: '이미지 파일이 필요합니다' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const result = await analyzeImageFile(req.file.buffer, req.file.originalname);
|
||||||
|
res.json(result);
|
||||||
|
} catch (err: unknown) {
|
||||||
|
if (err instanceof Error) {
|
||||||
|
const code = (err as NodeJS.ErrnoException).code;
|
||||||
|
if (code === 'GPS_NOT_FOUND') {
|
||||||
|
res.status(422).json({ error: 'GPS_NOT_FOUND', message: 'GPS 정보가 없는 이미지입니다' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (code === 'TIMEOUT') {
|
||||||
|
res.status(504).json({ error: 'TIMEOUT', message: '이미지 분석 서버 응답 시간 초과' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.error('[prediction] 이미지 분석 오류:', err);
|
||||||
|
res.status(500).json({ error: '이미지 분석 실패' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
5
database/migration/021_spil_img_rslt.sql
Normal file
5
database/migration/021_spil_img_rslt.sql
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
-- 021_spil_img_rslt.sql
|
||||||
|
-- SPIL_DATA 테이블에 이미지 분석 결과 컬럼 추가
|
||||||
|
|
||||||
|
ALTER TABLE wing.spil_data
|
||||||
|
ADD COLUMN IF NOT EXISTS img_rslt_data JSONB;
|
||||||
27
frontend/src/common/utils/imageAnalysisSignal.ts
Normal file
27
frontend/src/common/utils/imageAnalysisSignal.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import type { ImageAnalyzeResult } from '@tabs/prediction/services/predictionApi';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 항공탐색(유출유면적분석) → 유출유 확산예측 탭 간 데이터 전달용 모듈 레벨 시그널.
|
||||||
|
* registerMainTabSwitcher / navigateToTab 패턴과 동일한 방식으로 구현된다.
|
||||||
|
*/
|
||||||
|
|
||||||
|
interface PendingImageAnalysis extends ImageAnalyzeResult {
|
||||||
|
autoRun: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let _pending: PendingImageAnalysis | null = null;
|
||||||
|
|
||||||
|
/** 분석 결과를 시그널에 저장한다. navigateToTab 호출 직전에 사용한다. */
|
||||||
|
export function setPendingImageAnalysis(data: PendingImageAnalysis): void {
|
||||||
|
_pending = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 시그널에서 분석 결과를 꺼내고 초기화한다.
|
||||||
|
* OilSpillView 마운트 시 1회 호출한다.
|
||||||
|
*/
|
||||||
|
export function consumePendingImageAnalysis(): PendingImageAnalysis | null {
|
||||||
|
const value = _pending;
|
||||||
|
_pending = null;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
@ -1,6 +1,7 @@
|
|||||||
import { useState, useCallback, useRef, useEffect } from 'react'
|
import { useState, useCallback, useRef, useEffect } from 'react'
|
||||||
import { fetchAerialMedia } from '../services/aerialApi'
|
import { fetchAerialMedia, downloadAerialMedia } from '../services/aerialApi'
|
||||||
import type { AerialMediaItem } from '../services/aerialApi'
|
import type { AerialMediaItem } from '../services/aerialApi'
|
||||||
|
import { navigateToTab } from '@common/hooks/useSubMenu'
|
||||||
|
|
||||||
// ── Helpers ──
|
// ── Helpers ──
|
||||||
|
|
||||||
@ -48,6 +49,9 @@ export function MediaManagement() {
|
|||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [sortBy, setSortBy] = useState('latest')
|
const [sortBy, setSortBy] = useState('latest')
|
||||||
const [showUpload, setShowUpload] = useState(false)
|
const [showUpload, setShowUpload] = useState(false)
|
||||||
|
const [downloadingId, setDownloadingId] = useState<number | null>(null)
|
||||||
|
const [bulkDownloading, setBulkDownloading] = useState(false)
|
||||||
|
const [downloadResult, setDownloadResult] = useState<{ total: number; success: number } | null>(null)
|
||||||
const modalRef = useRef<HTMLDivElement>(null)
|
const modalRef = useRef<HTMLDivElement>(null)
|
||||||
|
|
||||||
const loadData = useCallback(async () => {
|
const loadData = useCallback(async () => {
|
||||||
@ -118,6 +122,38 @@ export function MediaManagement() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleBulkDownload = async () => {
|
||||||
|
if (bulkDownloading || selectedIds.size === 0) return
|
||||||
|
setBulkDownloading(true)
|
||||||
|
let success = 0
|
||||||
|
const total = selectedIds.size
|
||||||
|
for (const sn of selectedIds) {
|
||||||
|
const item = mediaItems.find(f => f.aerialMediaSn === sn)
|
||||||
|
if (!item) continue
|
||||||
|
try {
|
||||||
|
await downloadAerialMedia(sn, item.orgnlNm ?? item.fileNm)
|
||||||
|
success++
|
||||||
|
} catch {
|
||||||
|
// 실패 건 스킵
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setBulkDownloading(false)
|
||||||
|
setDownloadResult({ total, success })
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleDownload = async (e: React.MouseEvent, item: AerialMediaItem) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (downloadingId !== null) return
|
||||||
|
setDownloadingId(item.aerialMediaSn)
|
||||||
|
try {
|
||||||
|
await downloadAerialMedia(item.aerialMediaSn, item.orgnlNm ?? item.fileNm)
|
||||||
|
} catch {
|
||||||
|
alert('다운로드 실패: 이미지를 찾을 수 없습니다.')
|
||||||
|
} finally {
|
||||||
|
setDownloadingId(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const droneCount = mediaItems.filter(f => f.equipTpCd === 'drone').length
|
const droneCount = mediaItems.filter(f => f.equipTpCd === 'drone').length
|
||||||
const planeCount = mediaItems.filter(f => f.equipTpCd === 'plane').length
|
const planeCount = mediaItems.filter(f => f.equipTpCd === 'plane').length
|
||||||
const satCount = mediaItems.filter(f => f.equipTpCd === 'satellite').length
|
const satCount = mediaItems.filter(f => f.equipTpCd === 'satellite').length
|
||||||
@ -254,8 +290,12 @@ export function MediaManagement() {
|
|||||||
<td className="px-2 py-2 text-[11px] font-mono">{f.fileSz ?? '—'}</td>
|
<td className="px-2 py-2 text-[11px] font-mono">{f.fileSz ?? '—'}</td>
|
||||||
<td className="px-2 py-2 text-[11px] font-mono">{f.resolution ?? '—'}</td>
|
<td className="px-2 py-2 text-[11px] font-mono">{f.resolution ?? '—'}</td>
|
||||||
<td className="px-2 py-2 text-center" onClick={e => e.stopPropagation()}>
|
<td className="px-2 py-2 text-center" onClick={e => e.stopPropagation()}>
|
||||||
<button className="px-2 py-1 text-[10px] rounded bg-[rgba(6,182,212,0.1)] text-primary-cyan border border-primary-cyan/20 hover:bg-[rgba(6,182,212,0.2)] transition-colors">
|
<button
|
||||||
📥
|
onClick={(e) => handleDownload(e, f)}
|
||||||
|
disabled={downloadingId === f.aerialMediaSn}
|
||||||
|
className="px-2 py-1 text-[10px] rounded bg-[rgba(6,182,212,0.1)] text-primary-cyan border border-primary-cyan/20 hover:bg-[rgba(6,182,212,0.2)] transition-colors disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{downloadingId === f.aerialMediaSn ? '⏳' : '📥'}
|
||||||
</button>
|
</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -274,15 +314,47 @@ export function MediaManagement() {
|
|||||||
<button onClick={toggleAll} className="px-3 py-1.5 text-[11px] font-semibold rounded bg-bg-3 border border-border text-text-2 hover:bg-bg-hover transition-colors font-korean">
|
<button onClick={toggleAll} className="px-3 py-1.5 text-[11px] font-semibold rounded bg-bg-3 border border-border text-text-2 hover:bg-bg-hover transition-colors font-korean">
|
||||||
☑ 전체선택
|
☑ 전체선택
|
||||||
</button>
|
</button>
|
||||||
<button className="px-3 py-1.5 text-[11px] font-semibold rounded bg-[rgba(6,182,212,0.1)] text-primary-cyan border border-primary-cyan/30 hover:bg-[rgba(6,182,212,0.2)] transition-colors font-korean">
|
<button
|
||||||
📥 선택 다운로드
|
onClick={handleBulkDownload}
|
||||||
|
disabled={bulkDownloading || selectedIds.size === 0}
|
||||||
|
className="px-3 py-1.5 text-[11px] font-semibold rounded bg-[rgba(6,182,212,0.1)] text-primary-cyan border border-primary-cyan/30 hover:bg-[rgba(6,182,212,0.2)] transition-colors font-korean disabled:opacity-50"
|
||||||
|
>
|
||||||
|
{bulkDownloading ? '⏳ 다운로드 중...' : '📥 선택 다운로드'}
|
||||||
</button>
|
</button>
|
||||||
<button className="px-3 py-1.5 text-[11px] font-semibold rounded bg-[rgba(168,85,247,0.1)] text-primary-purple border border-primary-purple/30 hover:bg-[rgba(168,85,247,0.2)] transition-colors font-korean">
|
<button
|
||||||
🧩 유출유면적분석으로 →
|
onClick={() => navigateToTab('prediction', 'analysis')}
|
||||||
|
className="px-3 py-1.5 text-[11px] font-semibold rounded bg-[rgba(168,85,247,0.1)] text-primary-purple border border-primary-purple/30 hover:bg-[rgba(168,85,247,0.2)] transition-colors font-korean"
|
||||||
|
>
|
||||||
|
🔬 유출유확산예측으로 →
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* 선택 다운로드 결과 팝업 */}
|
||||||
|
{downloadResult && (
|
||||||
|
<div className="fixed inset-0 z-[300] bg-black/60 backdrop-blur-sm flex items-center justify-center">
|
||||||
|
<div className="bg-bg-1 border border-border rounded-md p-6 w-72 text-center">
|
||||||
|
<div className="text-2xl mb-3">📥</div>
|
||||||
|
<div className="text-sm font-bold font-korean mb-3">다운로드 완료</div>
|
||||||
|
<div className="text-[13px] font-korean text-text-2 mb-1">
|
||||||
|
총 <span className="text-primary-cyan font-bold">{downloadResult.total}</span>건 선택
|
||||||
|
</div>
|
||||||
|
<div className="text-[13px] font-korean text-text-2 mb-4">
|
||||||
|
<span className="text-status-green font-bold">{downloadResult.success}</span>건 다운로드 성공
|
||||||
|
{downloadResult.total - downloadResult.success > 0 && (
|
||||||
|
<> / <span className="text-status-red font-bold">{downloadResult.total - downloadResult.success}</span>건 실패</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => setDownloadResult(null)}
|
||||||
|
className="px-6 py-2 text-sm font-semibold rounded bg-[rgba(6,182,212,0.15)] text-primary-cyan border border-primary-cyan/30 hover:bg-[rgba(6,182,212,0.25)] transition-colors font-korean"
|
||||||
|
>
|
||||||
|
확인
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Upload Modal */}
|
{/* Upload Modal */}
|
||||||
{showUpload && (
|
{showUpload && (
|
||||||
<div className="fixed inset-0 z-[200] bg-black/60 backdrop-blur-sm flex items-center justify-center">
|
<div className="fixed inset-0 z-[200] bg-black/60 backdrop-blur-sm flex items-center justify-center">
|
||||||
|
|||||||
@ -1,212 +1,245 @@
|
|||||||
import { useState } from 'react'
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
||||||
|
import { stitchImages } from '../services/aerialApi';
|
||||||
|
import { analyzeImage } from '@tabs/prediction/services/predictionApi';
|
||||||
|
import { setPendingImageAnalysis } from '@common/utils/imageAnalysisSignal';
|
||||||
|
import { navigateToTab } from '@common/hooks/useSubMenu';
|
||||||
|
|
||||||
// ── Types & Mock Data ──
|
const MAX_IMAGES = 6;
|
||||||
|
|
||||||
interface MosaicImage {
|
|
||||||
id: string
|
|
||||||
filename: string
|
|
||||||
status: 'done' | 'processing' | 'waiting'
|
|
||||||
hasOil: boolean
|
|
||||||
}
|
|
||||||
|
|
||||||
const mosaicImages: MosaicImage[] = [
|
|
||||||
{ id: 'T1', filename: '드론_001.jpg', status: 'done', hasOil: true },
|
|
||||||
{ id: 'T2', filename: '드론_002.jpg', status: 'done', hasOil: true },
|
|
||||||
{ id: 'T3', filename: '드론_003.jpg', status: 'done', hasOil: true },
|
|
||||||
{ id: 'T4', filename: '드론_004.jpg', status: 'done', hasOil: true },
|
|
||||||
{ id: 'T5', filename: '드론_005.jpg', status: 'processing', hasOil: false },
|
|
||||||
{ id: 'T6', filename: '드론_006.jpg', status: 'waiting', hasOil: false },
|
|
||||||
]
|
|
||||||
|
|
||||||
// ── Component ──
|
|
||||||
|
|
||||||
export function OilAreaAnalysis() {
|
export function OilAreaAnalysis() {
|
||||||
const [activeStep, setActiveStep] = useState(1)
|
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
|
||||||
const [analyzing, setAnalyzing] = useState(false)
|
const [previewUrls, setPreviewUrls] = useState<string[]>([]);
|
||||||
const [analyzed, setAnalyzed] = useState(false)
|
const [stitchedBlob, setStitchedBlob] = useState<Blob | null>(null);
|
||||||
|
const [stitchedPreviewUrl, setStitchedPreviewUrl] = useState<string | null>(null);
|
||||||
|
const [isStitching, setIsStitching] = useState(false);
|
||||||
|
const [isAnalyzing, setIsAnalyzing] = useState(false);
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const handleAnalyze = () => {
|
// Object URL 메모리 누수 방지 — 언마운트 시 전체 revoke
|
||||||
setAnalyzing(true)
|
useEffect(() => {
|
||||||
setTimeout(() => {
|
return () => {
|
||||||
setAnalyzing(false)
|
previewUrls.forEach(url => URL.revokeObjectURL(url));
|
||||||
setAnalyzed(true)
|
if (stitchedPreviewUrl) URL.revokeObjectURL(stitchedPreviewUrl);
|
||||||
}, 1500)
|
};
|
||||||
}
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const stepCls = (idx: number) => {
|
const handleFileSelect = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
if (idx < activeStep) return 'border-status-green text-status-green bg-[rgba(34,197,94,0.05)]'
|
setError(null);
|
||||||
if (idx === activeStep) return 'border-primary-cyan text-primary-cyan bg-[rgba(6,182,212,0.05)]'
|
const incoming = Array.from(e.target.files ?? []);
|
||||||
return 'border-border text-text-3 bg-bg-3'
|
if (incoming.length === 0) return;
|
||||||
}
|
|
||||||
|
setSelectedFiles(prev => {
|
||||||
|
const merged = [...prev, ...incoming].slice(0, MAX_IMAGES);
|
||||||
|
if (prev.length + incoming.length > MAX_IMAGES) {
|
||||||
|
setError(`최대 ${MAX_IMAGES}장까지 선택할 수 있습니다.`);
|
||||||
|
}
|
||||||
|
return merged;
|
||||||
|
});
|
||||||
|
|
||||||
|
// setSelectedFiles updater 밖에서 독립 호출 — updater 내부 side effect는
|
||||||
|
// React Strict Mode의 이중 호출로 인해 URL이 중복 생성되는 버그를 유발함
|
||||||
|
setPreviewUrls(prev => {
|
||||||
|
const available = MAX_IMAGES - prev.length;
|
||||||
|
const toAdd = incoming.slice(0, available);
|
||||||
|
return [...prev, ...toAdd.map(f => URL.createObjectURL(f))];
|
||||||
|
});
|
||||||
|
|
||||||
|
// input 초기화 (동일 파일 재선택 허용)
|
||||||
|
e.target.value = '';
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const handleRemoveFile = useCallback((idx: number) => {
|
||||||
|
setSelectedFiles(prev => prev.filter((_, i) => i !== idx));
|
||||||
|
setPreviewUrls(prev => {
|
||||||
|
URL.revokeObjectURL(prev[idx]);
|
||||||
|
return prev.filter((_, i) => i !== idx);
|
||||||
|
});
|
||||||
|
// 합성 결과 초기화 (선택 파일이 바뀌었으므로)
|
||||||
|
setStitchedBlob(null);
|
||||||
|
if (stitchedPreviewUrl) {
|
||||||
|
URL.revokeObjectURL(stitchedPreviewUrl);
|
||||||
|
setStitchedPreviewUrl(null);
|
||||||
|
}
|
||||||
|
setError(null);
|
||||||
|
}, [stitchedPreviewUrl]);
|
||||||
|
|
||||||
|
const handleStitch = async () => {
|
||||||
|
if (selectedFiles.length < 2) {
|
||||||
|
setError('이미지를 2장 이상 선택해주세요.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setError(null);
|
||||||
|
setIsStitching(true);
|
||||||
|
try {
|
||||||
|
const blob = await stitchImages(selectedFiles);
|
||||||
|
if (stitchedPreviewUrl) URL.revokeObjectURL(stitchedPreviewUrl);
|
||||||
|
setStitchedBlob(blob);
|
||||||
|
setStitchedPreviewUrl(URL.createObjectURL(blob));
|
||||||
|
} catch (err) {
|
||||||
|
const msg =
|
||||||
|
err instanceof Error
|
||||||
|
? err.message
|
||||||
|
: (err as { message?: string }).message ?? '이미지 합성에 실패했습니다.';
|
||||||
|
const status = err instanceof Error ? 0 : (err as { status?: number }).status ?? 0;
|
||||||
|
setError(status === 504 ? '이미지 합성 서버 응답 시간이 초과되었습니다.' : msg);
|
||||||
|
} finally {
|
||||||
|
setIsStitching(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAnalyze = async () => {
|
||||||
|
if (!stitchedBlob) return;
|
||||||
|
setError(null);
|
||||||
|
setIsAnalyzing(true);
|
||||||
|
try {
|
||||||
|
const stitchedFile = new File([stitchedBlob], `stitch_${Date.now()}.jpg`, { type: 'image/jpeg' });
|
||||||
|
const result = await analyzeImage(stitchedFile);
|
||||||
|
setPendingImageAnalysis({ ...result, autoRun: true });
|
||||||
|
navigateToTab('prediction', 'analysis');
|
||||||
|
} catch (err) {
|
||||||
|
const msg = err instanceof Error ? err.message : '분석에 실패했습니다.';
|
||||||
|
setError(msg.includes('GPS') ? '이미지에 GPS 정보가 없습니다. GPS 정보가 포함된 이미지를 사용해주세요.' : msg);
|
||||||
|
setIsAnalyzing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const canStitch = selectedFiles.length >= 2 && !isStitching && !isAnalyzing;
|
||||||
|
const canAnalyze = stitchedBlob !== null && !isStitching && !isAnalyzing;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex gap-5 h-full overflow-hidden">
|
<div className="flex gap-5 h-full overflow-hidden">
|
||||||
{/* Left Panel */}
|
{/* ── Left Panel ── */}
|
||||||
<div className="w-[340px] min-w-[340px] flex flex-col overflow-y-auto scrollbar-thin">
|
<div className="w-[280px] min-w-[280px] flex flex-col overflow-y-auto scrollbar-thin">
|
||||||
<div className="text-sm font-bold mb-1 font-korean">🧩 유출유면적분석</div>
|
<div className="text-sm font-bold mb-1 font-korean">🧩 유출유면적분석</div>
|
||||||
<div className="text-[11px] text-text-3 mb-4 font-korean">단면 사진을 합성하여 유출유 확산 면적과 기름 양을 산정합니다.</div>
|
<div className="text-[11px] text-text-3 mb-4 font-korean">
|
||||||
|
드론 사진을 합성하여 유출유 확산 면적과 기름 양을 산정합니다.
|
||||||
{/* Step Indicator */}
|
|
||||||
<div className="flex gap-2 mb-3">
|
|
||||||
{['① 사진 선택', '② 정합·합성', '③ 면적 산정'].map((label, i) => (
|
|
||||||
<button
|
|
||||||
key={i}
|
|
||||||
onClick={() => setActiveStep(i)}
|
|
||||||
className={`flex-1 py-2 rounded-sm border text-center text-[10px] font-semibold font-korean cursor-pointer transition-colors ${stepCls(i)}`}
|
|
||||||
>
|
|
||||||
{label}
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Selected Images */}
|
{/* 이미지 선택 버튼 */}
|
||||||
<div className="text-[11px] font-bold mb-2 font-korean">선택된 사진 (6장)</div>
|
<input
|
||||||
<div className="flex flex-col gap-1 mb-3.5">
|
ref={fileInputRef}
|
||||||
{['여수항_드론_001.jpg', '여수항_드론_002.jpg', '여수항_드론_003.jpg', '여수항_드론_004.jpg', '여수항_드론_005.jpg', '여수항_드론_006.jpg'].map((name, i) => (
|
type="file"
|
||||||
<div key={i} className="flex items-center gap-2 px-2 py-1.5 bg-bg-3 border border-border rounded-sm text-[11px] font-korean">
|
accept="image/*"
|
||||||
<span>🛸</span>
|
multiple
|
||||||
<span className="flex-1 truncate">{name}</span>
|
className="hidden"
|
||||||
<span className={`text-[9px] font-semibold ${
|
onChange={handleFileSelect}
|
||||||
i < 4 ? 'text-status-green' : i === 4 ? 'text-status-orange' : 'text-text-3'
|
/>
|
||||||
}`}>
|
<button
|
||||||
{i < 4 ? '✓ 정합' : i === 4 ? '⏳ 정합중' : '대기'}
|
onClick={() => fileInputRef.current?.click()}
|
||||||
</span>
|
disabled={selectedFiles.length >= MAX_IMAGES || isStitching || isAnalyzing}
|
||||||
|
className="w-full py-2 mb-3 border border-dashed border-border rounded-sm text-xs font-korean text-text-2
|
||||||
|
hover:border-primary-cyan hover:text-primary-cyan transition-colors cursor-pointer
|
||||||
|
disabled:opacity-40 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
+ 이미지 선택 ({selectedFiles.length}/{MAX_IMAGES})
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* 선택된 이미지 목록 */}
|
||||||
|
{selectedFiles.length > 0 && (
|
||||||
|
<>
|
||||||
|
<div className="text-[11px] font-bold mb-1.5 font-korean">선택된 이미지</div>
|
||||||
|
<div className="flex flex-col gap-1 mb-3">
|
||||||
|
{selectedFiles.map((file, i) => (
|
||||||
|
<div
|
||||||
|
key={`${file.name}-${i}`}
|
||||||
|
className="flex items-center gap-2 px-2 py-1.5 bg-bg-3 border border-border rounded-sm text-[11px] font-korean"
|
||||||
|
>
|
||||||
|
<span className="text-primary-cyan">📷</span>
|
||||||
|
<span className="flex-1 truncate text-text-1">{file.name}</span>
|
||||||
|
<button
|
||||||
|
onClick={() => handleRemoveFile(i)}
|
||||||
|
disabled={isStitching || isAnalyzing}
|
||||||
|
className="text-text-3 hover:text-status-red transition-colors cursor-pointer
|
||||||
|
disabled:opacity-40 disabled:cursor-not-allowed ml-1 shrink-0"
|
||||||
|
title="제거"
|
||||||
|
>
|
||||||
|
✕
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
</>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
{/* Analysis Parameters */}
|
{/* 에러 메시지 */}
|
||||||
<div className="text-[11px] font-bold mb-2 font-korean">분석 파라미터</div>
|
{error && (
|
||||||
<div className="flex flex-col gap-1.5 mb-3.5">
|
<div className="mb-3 px-2.5 py-2 bg-[rgba(239,68,68,0.08)] border border-[rgba(239,68,68,0.3)] rounded-sm text-[11px] text-status-red font-korean">
|
||||||
{[
|
{error}
|
||||||
['촬영 고도', '120 m'],
|
</div>
|
||||||
['GSD (지상해상도)', '3.2 cm/px'],
|
)}
|
||||||
['오버랩 비율', '80% / 70%'],
|
|
||||||
['좌표계', 'EPSG:5186'],
|
|
||||||
['유종 판별 기준', 'NDVI + NIR'],
|
|
||||||
['유막 두께 추정', 'Bonn Agreement'],
|
|
||||||
].map(([label, value], i) => (
|
|
||||||
<div key={i} className="flex justify-between items-center text-[11px]">
|
|
||||||
<span className="text-text-3 font-korean">{label}</span>
|
|
||||||
<span className="font-mono font-semibold">{value}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Action Buttons */}
|
{/* 이미지 합성 버튼 */}
|
||||||
|
<button
|
||||||
|
onClick={handleStitch}
|
||||||
|
disabled={!canStitch}
|
||||||
|
className="w-full py-2.5 mb-2 rounded-sm text-[12px] font-bold font-korean cursor-pointer transition-colors
|
||||||
|
border border-primary-cyan text-primary-cyan bg-[rgba(6,182,212,0.06)]
|
||||||
|
hover:bg-[rgba(6,182,212,0.15)] disabled:opacity-40 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{isStitching ? '⏳ 합성 중...' : stitchedBlob ? '✅ 다시 합성' : '🔗 이미지 합성'}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* 분석 시작 버튼 */}
|
||||||
<button
|
<button
|
||||||
onClick={handleAnalyze}
|
onClick={handleAnalyze}
|
||||||
disabled={analyzing}
|
disabled={!canAnalyze}
|
||||||
className={`w-full py-3 rounded-sm text-[13px] font-bold font-korean cursor-pointer border-none mb-2 transition-colors ${
|
className="w-full py-3 rounded-sm text-[13px] font-bold font-korean cursor-pointer border-none transition-colors
|
||||||
analyzed
|
disabled:opacity-40 disabled:cursor-not-allowed text-white"
|
||||||
? 'bg-[rgba(34,197,94,0.15)] text-status-green border border-status-green'
|
style={canAnalyze ? { background: 'linear-gradient(135deg, var(--cyan), var(--blue))' } : { background: 'var(--bg-3)' }}
|
||||||
: 'text-white'
|
|
||||||
}`}
|
|
||||||
style={!analyzed ? { background: 'linear-gradient(135deg, var(--cyan), var(--blue))' } : undefined}
|
|
||||||
>
|
>
|
||||||
{analyzing ? '⏳ 분석중...' : analyzed ? '✅ 분석 완료!' : '🧩 면적분석 실행'}
|
{isAnalyzing ? '⏳ 분석 중...' : '🧩 분석 시작'}
|
||||||
</button>
|
|
||||||
<button className="w-full py-2.5 border border-border bg-bg-3 text-text-2 rounded-sm text-xs font-semibold font-korean cursor-pointer hover:bg-bg-hover transition-colors">
|
|
||||||
📥 결과 다운로드 (GeoTIFF)
|
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right Panel */}
|
{/* ── Right Panel ── */}
|
||||||
<div className="flex-1 flex flex-col overflow-hidden">
|
<div className="flex-1 flex flex-col overflow-hidden">
|
||||||
{/* Header */}
|
{/* 3×2 이미지 그리드 */}
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="text-[11px] font-bold mb-2 font-korean">선택된 이미지 미리보기</div>
|
||||||
<span className="text-xs font-bold font-korean">🗺 합성 영상 및 유막 탐지 결과</span>
|
|
||||||
<div className="flex gap-1.5">
|
|
||||||
<span className="text-[10px] px-2 py-0.5 rounded-full bg-[rgba(239,68,68,0.1)] text-status-red font-semibold font-korean">■ 유막 탐지</span>
|
|
||||||
<span className="text-[10px] px-2 py-0.5 rounded-full bg-[rgba(6,182,212,0.1)] text-primary-cyan font-semibold font-korean">□ 원본 타일</span>
|
|
||||||
<span className="text-[10px] px-2 py-0.5 rounded-full bg-[rgba(34,197,94,0.1)] text-status-green font-semibold font-korean">정합률 96.2%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Image Grid 3×2 */}
|
|
||||||
<div className="grid grid-cols-3 gap-1.5 mb-3">
|
<div className="grid grid-cols-3 gap-1.5 mb-3">
|
||||||
{mosaicImages.map(img => (
|
{Array.from({ length: MAX_IMAGES }).map((_, i) => (
|
||||||
<div key={img.id} className="bg-bg-3 border border-border rounded-sm overflow-hidden cursor-pointer hover:border-border-light transition-colors">
|
<div
|
||||||
<div
|
key={i}
|
||||||
className="h-[100px] relative flex items-center justify-center overflow-hidden"
|
className="bg-bg-3 border border-border rounded-sm overflow-hidden"
|
||||||
style={{ background: 'linear-gradient(135deg, #0c1624, #1a1a2e)' }}
|
style={{ height: '300px' }}
|
||||||
>
|
>
|
||||||
{img.hasOil && (
|
{previewUrls[i] ? (
|
||||||
<div
|
<img
|
||||||
className="absolute inset-0"
|
src={previewUrls[i]}
|
||||||
style={{
|
alt={selectedFiles[i]?.name ?? ''}
|
||||||
background: 'rgba(239,68,68,0.15)',
|
className="w-full h-full object-cover"
|
||||||
border: '1px solid rgba(239,68,68,0.35)',
|
/>
|
||||||
clipPath: 'polygon(20% 30%,60% 15%,85% 40%,70% 80%,30% 75%,10% 50%)',
|
) : (
|
||||||
}}
|
<div className="flex items-center justify-center h-full text-text-3 text-lg font-mono opacity-20">
|
||||||
/>
|
{i + 1}
|
||||||
)}
|
|
||||||
<div className="text-lg font-bold text-white/[0.08] font-mono">{img.id}</div>
|
|
||||||
<div className={`absolute top-1.5 right-1.5 px-1.5 py-0.5 rounded-md text-[9px] font-bold font-korean ${
|
|
||||||
img.status === 'done' && img.hasOil ? 'bg-[rgba(239,68,68,0.2)] text-status-red' :
|
|
||||||
img.status === 'processing' ? 'bg-[rgba(249,115,22,0.2)] text-status-orange' :
|
|
||||||
'bg-[rgba(100,116,139,0.2)] text-text-3'
|
|
||||||
}`}>
|
|
||||||
{img.status === 'done' && img.hasOil ? '유막' : img.status === 'processing' ? '정합중' : '대기'}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)}
|
||||||
<div className="px-2 py-1.5 flex justify-between items-center text-[10px] font-korean text-text-2">
|
|
||||||
<span>{img.filename}</span>
|
|
||||||
<span className={
|
|
||||||
img.status === 'done' ? 'text-status-green' :
|
|
||||||
img.status === 'processing' ? 'text-status-orange' :
|
|
||||||
'text-text-3'
|
|
||||||
}>
|
|
||||||
{img.status === 'done' ? '✓' : img.status === 'processing' ? '⏳' : '—'}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Merged Result Preview */}
|
{/* 합성 결과 */}
|
||||||
<div className="relative h-[140px] bg-bg-0 border border-border rounded-sm overflow-hidden mb-3">
|
<div className="text-[11px] font-bold mb-2 font-korean">합성 결과</div>
|
||||||
<div className="absolute inset-0" style={{ background: 'radial-gradient(ellipse at 40% 50%, rgba(10,25,40,0.7), rgba(8,14,26,0.95))' }}>
|
<div
|
||||||
<div className="absolute border border-dashed rounded flex items-center justify-center text-[10px] font-korean" style={{ top: '15%', left: '10%', width: '65%', height: '70%', borderColor: 'rgba(6,182,212,0.3)', color: 'rgba(6,182,212,0.5)' }}>
|
className="relative bg-bg-0 border border-border rounded-sm overflow-hidden flex items-center justify-center"
|
||||||
합성 영역 (3×2 그리드)
|
style={{ minHeight: '160px', flex: '1 1 0' }}
|
||||||
|
>
|
||||||
|
{stitchedPreviewUrl ? (
|
||||||
|
<img
|
||||||
|
src={stitchedPreviewUrl}
|
||||||
|
alt="합성 결과"
|
||||||
|
className="max-w-full max-h-full object-contain"
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<div className="text-[12px] text-text-3 font-korean text-center px-4">
|
||||||
|
{isStitching
|
||||||
|
? '⏳ 이미지를 합성하고 있습니다...'
|
||||||
|
: '이미지를 선택하고 합성 버튼을 클릭하면\n합성 결과가 여기에 표시됩니다.'}
|
||||||
</div>
|
</div>
|
||||||
<div className="absolute" style={{ top: '22%', left: '18%', width: '35%', height: '40%', background: 'rgba(239,68,68,0.12)', border: '1.5px solid rgba(239,68,68,0.4)', borderRadius: '30% 50% 40% 60%' }} />
|
)}
|
||||||
<div className="absolute" style={{ top: '40%', left: '38%', width: '20%', height: '30%', background: 'rgba(239,68,68,0.08)', border: '1px solid rgba(239,68,68,0.3)', borderRadius: '50% 30% 60% 40%' }} />
|
|
||||||
</div>
|
|
||||||
<div className="absolute bottom-1.5 left-2.5 text-[9px] text-text-3 font-mono">34.7312°N, 127.6845°E</div>
|
|
||||||
<div className="absolute bottom-1.5 right-2.5 text-[9px] text-text-3 font-mono">축척 ≈ 1:2,500</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Analysis Results */}
|
|
||||||
<div className="p-4 bg-bg-3 border border-border rounded-md">
|
|
||||||
<div className="text-xs font-bold mb-2.5 font-korean">📊 유출유 분석 결과</div>
|
|
||||||
<div className="grid grid-cols-3 gap-2">
|
|
||||||
{[
|
|
||||||
{ value: '0.42 km²', label: '유막 면적', color: 'text-status-red' },
|
|
||||||
{ value: '12.6 kL', label: '추정 유출량', color: 'text-status-orange' },
|
|
||||||
{ value: '1.84 km²', label: '합성 영역 면적', color: 'text-primary-cyan' },
|
|
||||||
].map((r, i) => (
|
|
||||||
<div key={i} className="text-center py-2.5 px-2 bg-bg-0 border border-border rounded-sm">
|
|
||||||
<div className={`text-lg font-bold font-mono ${r.color}`}>{r.value}</div>
|
|
||||||
<div className="text-[9px] text-text-3 mt-0.5 font-korean">{r.label}</div>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="grid grid-cols-2 gap-1.5 mt-2.5 text-[11px]">
|
|
||||||
{[
|
|
||||||
['두꺼운 유막 (>1mm)', '0.08 km²', 'text-status-red'],
|
|
||||||
['얇은 유막 (<1mm)', '0.34 km²', 'text-status-orange'],
|
|
||||||
['무지개 빛깔', '0.12 km²', 'text-status-yellow'],
|
|
||||||
['Bonn 코드', 'Code 3~4', 'text-text-1'],
|
|
||||||
].map(([label, value, color], i) => (
|
|
||||||
<div key={i} className="flex justify-between px-2 py-1 bg-bg-0 rounded">
|
|
||||||
<span className="text-text-3 font-korean">{label}</span>
|
|
||||||
<span className={`font-semibold font-mono ${color}`}>{value}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -103,3 +103,30 @@ export async function createSatRequest(
|
|||||||
const response = await api.post<{ satReqSn: number }>('/aerial/sat-requests', input);
|
const response = await api.post<{ satReqSn: number }>('/aerial/sat-requests', input);
|
||||||
return response.data;
|
return response.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function downloadAerialMedia(sn: number, fileName: string): Promise<void> {
|
||||||
|
const res = await api.get(`/aerial/media/${sn}/download`, { responseType: 'blob' });
|
||||||
|
const url = URL.createObjectURL(res.data as Blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = fileName;
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
document.body.removeChild(a);
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 여러 이미지 파일을 /aerial/stitch 엔드포인트로 전송해 합성 JPEG Blob을 반환한다.
|
||||||
|
* FastAPI /stitch → pic_gps.py 스티칭 파이프라인 프록시.
|
||||||
|
*/
|
||||||
|
export async function stitchImages(files: File[]): Promise<Blob> {
|
||||||
|
const form = new FormData();
|
||||||
|
files.forEach(f => form.append('files', f));
|
||||||
|
const response = await api.post<Blob>('/aerial/stitch', form, {
|
||||||
|
responseType: 'blob',
|
||||||
|
timeout: 310_000,
|
||||||
|
headers: { 'Content-Type': undefined }, // 기본 application/json 제거 → 브라우저가 multipart/form-data 자동 설정
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
}
|
||||||
|
|||||||
@ -47,6 +47,7 @@ export function LeftPanel({
|
|||||||
onLayerOpacityChange,
|
onLayerOpacityChange,
|
||||||
layerBrightness,
|
layerBrightness,
|
||||||
onLayerBrightnessChange,
|
onLayerBrightnessChange,
|
||||||
|
onImageAnalysisResult,
|
||||||
}: LeftPanelProps) {
|
}: LeftPanelProps) {
|
||||||
const [expandedSections, setExpandedSections] = useState<ExpandedSections>({
|
const [expandedSections, setExpandedSections] = useState<ExpandedSections>({
|
||||||
predictionInput: true,
|
predictionInput: true,
|
||||||
@ -93,6 +94,7 @@ export function LeftPanel({
|
|||||||
onIncidentNameChange={onIncidentNameChange}
|
onIncidentNameChange={onIncidentNameChange}
|
||||||
spillUnit={spillUnit}
|
spillUnit={spillUnit}
|
||||||
onSpillUnitChange={onSpillUnitChange}
|
onSpillUnitChange={onSpillUnitChange}
|
||||||
|
onImageAnalysisResult={onImageAnalysisResult}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Incident Section */}
|
{/* Incident Section */}
|
||||||
|
|||||||
@ -13,11 +13,12 @@ import type { BoomLine, AlgorithmSettings, ContainmentResult, BoomLineCoord } fr
|
|||||||
import type { BacktrackPhase, BacktrackVessel, BacktrackConditions, ReplayShip, CollisionEvent } from '@common/types/backtrack'
|
import type { BacktrackPhase, BacktrackVessel, BacktrackConditions, ReplayShip, CollisionEvent } from '@common/types/backtrack'
|
||||||
import { TOTAL_REPLAY_FRAMES } from '@common/types/backtrack'
|
import { TOTAL_REPLAY_FRAMES } from '@common/types/backtrack'
|
||||||
import { fetchBacktrackByAcdnt, createBacktrack, fetchPredictionDetail, fetchAnalysisTrajectory } from '../services/predictionApi'
|
import { fetchBacktrackByAcdnt, createBacktrack, fetchPredictionDetail, fetchAnalysisTrajectory } from '../services/predictionApi'
|
||||||
import type { CenterPoint, HydrDataStep, PredictionDetail, SimulationRunResponse, SimulationSummary, WindPoint } from '../services/predictionApi'
|
import type { CenterPoint, HydrDataStep, ImageAnalyzeResult, PredictionDetail, SimulationRunResponse, SimulationSummary, WindPoint } from '../services/predictionApi'
|
||||||
import { useSimulationStatus } from '../hooks/useSimulationStatus'
|
import { useSimulationStatus } from '../hooks/useSimulationStatus'
|
||||||
import SimulationLoadingOverlay from './SimulationLoadingOverlay'
|
import SimulationLoadingOverlay from './SimulationLoadingOverlay'
|
||||||
import { api } from '@common/services/api'
|
import { api } from '@common/services/api'
|
||||||
import { generateAIBoomLines } from '@common/utils/geo'
|
import { generateAIBoomLines } from '@common/utils/geo'
|
||||||
|
import { consumePendingImageAnalysis } from '@common/utils/imageAnalysisSignal'
|
||||||
|
|
||||||
export type PredictionModel = 'KOSPS' | 'POSEIDON' | 'OpenDrift'
|
export type PredictionModel = 'KOSPS' | 'POSEIDON' | 'OpenDrift'
|
||||||
|
|
||||||
@ -308,6 +309,37 @@ export function OilSpillView() {
|
|||||||
|
|
||||||
// flyTo 완료 후 재생 대기 플래그
|
// flyTo 완료 후 재생 대기 플래그
|
||||||
const pendingPlayRef = useRef(false)
|
const pendingPlayRef = useRef(false)
|
||||||
|
|
||||||
|
// 항공 이미지 분석 완료 후 자동실행 플래그
|
||||||
|
const pendingAutoRunRef = useRef(false)
|
||||||
|
|
||||||
|
// 마운트 시 이미지 분석 시그널 확인 (유출유면적분석 탭에서 이동한 경우)
|
||||||
|
useEffect(() => {
|
||||||
|
const pending = consumePendingImageAnalysis()
|
||||||
|
if (!pending) return
|
||||||
|
handleImageAnalysisResult({
|
||||||
|
acdntSn: pending.acdntSn,
|
||||||
|
lat: pending.lat,
|
||||||
|
lon: pending.lon,
|
||||||
|
oilType: pending.oilType,
|
||||||
|
area: pending.area,
|
||||||
|
volume: pending.volume,
|
||||||
|
fileId: pending.fileId,
|
||||||
|
occurredAt: pending.occurredAt,
|
||||||
|
})
|
||||||
|
if (pending.autoRun) pendingAutoRunRef.current = true
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
// incidentCoord 업데이트 후 시뮬레이션 자동실행
|
||||||
|
useEffect(() => {
|
||||||
|
if (pendingAutoRunRef.current && incidentCoord) {
|
||||||
|
pendingAutoRunRef.current = false
|
||||||
|
handleRunSimulation()
|
||||||
|
}
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [incidentCoord])
|
||||||
|
|
||||||
const handleFlyEnd = useCallback(() => {
|
const handleFlyEnd = useCallback(() => {
|
||||||
setFlyToCoord(undefined)
|
setFlyToCoord(undefined)
|
||||||
if (pendingPlayRef.current) {
|
if (pendingPlayRef.current) {
|
||||||
@ -472,6 +504,34 @@ export function OilSpillView() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleImageAnalysisResult = useCallback((result: ImageAnalyzeResult) => {
|
||||||
|
setIncidentCoord({ lat: result.lat, lon: result.lon })
|
||||||
|
setFlyToCoord({ lat: result.lat, lon: result.lon })
|
||||||
|
setAccidentTime(result.occurredAt.slice(0, 16))
|
||||||
|
setOilType(result.oilType)
|
||||||
|
setSpillAmount(parseFloat(result.volume.toFixed(4)))
|
||||||
|
setSpillUnit('kL')
|
||||||
|
setSelectedAnalysis({
|
||||||
|
acdntSn: result.acdntSn,
|
||||||
|
acdntNm: '',
|
||||||
|
occurredAt: result.occurredAt,
|
||||||
|
analysisDate: '',
|
||||||
|
requestor: '',
|
||||||
|
duration: '48',
|
||||||
|
oilType: result.oilType,
|
||||||
|
volume: result.volume,
|
||||||
|
location: '',
|
||||||
|
lat: result.lat,
|
||||||
|
lon: result.lon,
|
||||||
|
kospsStatus: 'pending',
|
||||||
|
poseidonStatus: 'pending',
|
||||||
|
opendriftStatus: 'pending',
|
||||||
|
backtrackStatus: 'pending',
|
||||||
|
analyst: '',
|
||||||
|
officeName: '',
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleRunSimulation = async () => {
|
const handleRunSimulation = async () => {
|
||||||
// incidentName이 있으면 직접 입력 모드 — 기존 selectedAnalysis.acdntSn 무시하고 새 사고 생성
|
// incidentName이 있으면 직접 입력 모드 — 기존 selectedAnalysis.acdntSn 무시하고 새 사고 생성
|
||||||
const isDirectInput = incidentName.trim().length > 0;
|
const isDirectInput = incidentName.trim().length > 0;
|
||||||
@ -589,6 +649,7 @@ export function OilSpillView() {
|
|||||||
onLayerOpacityChange={setLayerOpacity}
|
onLayerOpacityChange={setLayerOpacity}
|
||||||
layerBrightness={layerBrightness}
|
layerBrightness={layerBrightness}
|
||||||
onLayerBrightnessChange={setLayerBrightness}
|
onLayerBrightnessChange={setLayerBrightness}
|
||||||
|
onImageAnalysisResult={handleImageAnalysisResult}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
import { useState } from 'react'
|
import { useState, useRef } from 'react'
|
||||||
import { decimalToDMS } from '@common/utils/coordinates'
|
import { decimalToDMS } from '@common/utils/coordinates'
|
||||||
import { ComboBox } from '@common/components/ui/ComboBox'
|
import { ComboBox } from '@common/components/ui/ComboBox'
|
||||||
import { ALL_MODELS } from './OilSpillView'
|
import { ALL_MODELS } from './OilSpillView'
|
||||||
import type { PredictionModel } from './OilSpillView'
|
import type { PredictionModel } from './OilSpillView'
|
||||||
|
import { analyzeImage } from '../services/predictionApi'
|
||||||
|
import type { ImageAnalyzeResult } from '../services/predictionApi'
|
||||||
|
|
||||||
interface PredictionInputSectionProps {
|
interface PredictionInputSectionProps {
|
||||||
expanded: boolean
|
expanded: boolean
|
||||||
@ -29,6 +31,7 @@ interface PredictionInputSectionProps {
|
|||||||
onIncidentNameChange: (name: string) => void
|
onIncidentNameChange: (name: string) => void
|
||||||
spillUnit: string
|
spillUnit: string
|
||||||
onSpillUnitChange: (unit: string) => void
|
onSpillUnitChange: (unit: string) => void
|
||||||
|
onImageAnalysisResult?: (result: ImageAnalyzeResult) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const PredictionInputSection = ({
|
const PredictionInputSection = ({
|
||||||
@ -56,26 +59,53 @@ const PredictionInputSection = ({
|
|||||||
onIncidentNameChange,
|
onIncidentNameChange,
|
||||||
spillUnit,
|
spillUnit,
|
||||||
onSpillUnitChange,
|
onSpillUnitChange,
|
||||||
|
onImageAnalysisResult,
|
||||||
}: PredictionInputSectionProps) => {
|
}: PredictionInputSectionProps) => {
|
||||||
const [inputMode, setInputMode] = useState<'direct' | 'upload'>('direct')
|
const [inputMode, setInputMode] = useState<'direct' | 'upload'>('direct')
|
||||||
const [uploadedImage, setUploadedImage] = useState<string | null>(null)
|
const [uploadedFile, setUploadedFile] = useState<File | null>(null)
|
||||||
const [uploadedFileName, setUploadedFileName] = useState<string>('')
|
const [isAnalyzing, setIsAnalyzing] = useState(false)
|
||||||
|
const [analyzeError, setAnalyzeError] = useState<string | null>(null)
|
||||||
|
const [analyzeResult, setAnalyzeResult] = useState<ImageAnalyzeResult | null>(null)
|
||||||
|
const fileInputRef = useRef<HTMLInputElement>(null)
|
||||||
|
|
||||||
const handleImageUpload = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0]
|
const file = e.target.files?.[0] ?? null
|
||||||
if (file) {
|
setUploadedFile(file)
|
||||||
setUploadedFileName(file.name)
|
setAnalyzeError(null)
|
||||||
const reader = new FileReader()
|
setAnalyzeResult(null)
|
||||||
reader.onload = (event) => {
|
|
||||||
setUploadedImage(event.target?.result as string)
|
|
||||||
}
|
|
||||||
reader.readAsDataURL(file)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeUploadedImage = () => {
|
const handleRemoveFile = () => {
|
||||||
setUploadedImage(null)
|
setUploadedFile(null)
|
||||||
setUploadedFileName('')
|
setAnalyzeError(null)
|
||||||
|
setAnalyzeResult(null)
|
||||||
|
if (fileInputRef.current) fileInputRef.current.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAnalyze = async () => {
|
||||||
|
if (!uploadedFile) return
|
||||||
|
setIsAnalyzing(true)
|
||||||
|
setAnalyzeError(null)
|
||||||
|
try {
|
||||||
|
const result = await analyzeImage(uploadedFile)
|
||||||
|
setAnalyzeResult(result)
|
||||||
|
onImageAnalysisResult?.(result)
|
||||||
|
} catch (err: unknown) {
|
||||||
|
if (err && typeof err === 'object' && 'response' in err) {
|
||||||
|
const res = (err as { response?: { data?: { error?: string } } }).response
|
||||||
|
if (res?.data?.error === 'GPS_NOT_FOUND') {
|
||||||
|
setAnalyzeError('GPS 정보가 없는 이미지입니다')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (res?.data?.error === 'TIMEOUT') {
|
||||||
|
setAnalyzeError('분석 서버 응답 없음 (시간 초과)')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setAnalyzeError('이미지 분석 중 오류가 발생했습니다')
|
||||||
|
} finally {
|
||||||
|
setIsAnalyzing(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -102,8 +132,7 @@ const PredictionInputSection = ({
|
|||||||
name="prdType"
|
name="prdType"
|
||||||
checked={inputMode === 'direct'}
|
checked={inputMode === 'direct'}
|
||||||
onChange={() => setInputMode('direct')}
|
onChange={() => setInputMode('direct')}
|
||||||
className="m-0 w-[11px] h-[11px]"
|
className="accent-[var(--cyan)] m-0 w-[11px] h-[11px]"
|
||||||
className="accent-[var(--cyan)]"
|
|
||||||
/>
|
/>
|
||||||
직접 입력
|
직접 입력
|
||||||
</label>
|
</label>
|
||||||
@ -113,8 +142,7 @@ const PredictionInputSection = ({
|
|||||||
name="prdType"
|
name="prdType"
|
||||||
checked={inputMode === 'upload'}
|
checked={inputMode === 'upload'}
|
||||||
onChange={() => setInputMode('upload')}
|
onChange={() => setInputMode('upload')}
|
||||||
className="m-0 w-[11px] h-[11px]"
|
className="accent-[var(--cyan)] m-0 w-[11px] h-[11px]"
|
||||||
className="accent-[var(--cyan)]"
|
|
||||||
/>
|
/>
|
||||||
이미지 업로드
|
이미지 업로드
|
||||||
</label>
|
</label>
|
||||||
@ -136,35 +164,10 @@ const PredictionInputSection = ({
|
|||||||
{/* Image Upload Mode */}
|
{/* Image Upload Mode */}
|
||||||
{inputMode === 'upload' && (
|
{inputMode === 'upload' && (
|
||||||
<>
|
<>
|
||||||
<input className="prd-i" placeholder="여수 유조선 충돌" />
|
{/* 파일 선택 영역 */}
|
||||||
<ComboBox
|
{!uploadedFile ? (
|
||||||
className="prd-i"
|
<label
|
||||||
value=""
|
className="flex items-center justify-center text-[11px] text-text-3 cursor-pointer"
|
||||||
onChange={() => {}}
|
|
||||||
options={[
|
|
||||||
{ value: '', label: '여수 유조선 충돌 (INC-0042)' },
|
|
||||||
{ value: 'INC-0042', label: '여수 유조선 충돌 (INC-0042)' }
|
|
||||||
]}
|
|
||||||
placeholder="사고 선택"
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Upload Success Message */}
|
|
||||||
{uploadedImage && (
|
|
||||||
<div className="flex items-center gap-[6px] text-[10px] font-semibold text-[#22c55e] rounded"
|
|
||||||
style={{
|
|
||||||
padding: '6px 8px',
|
|
||||||
background: 'rgba(34,197,94,0.1)',
|
|
||||||
border: '1px solid rgba(34,197,94,0.3)',
|
|
||||||
borderRadius: 'var(--rS)',
|
|
||||||
}}>
|
|
||||||
<span className="text-[12px]">✓</span>
|
|
||||||
내 이미지가 업로드됨
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* File Upload Area */}
|
|
||||||
{!uploadedImage ? (
|
|
||||||
<label className="flex items-center justify-center text-[11px] text-text-3 cursor-pointer"
|
|
||||||
style={{
|
style={{
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
background: 'var(--bg0)',
|
background: 'var(--bg0)',
|
||||||
@ -179,63 +182,81 @@ const PredictionInputSection = ({
|
|||||||
onMouseLeave={(e) => {
|
onMouseLeave={(e) => {
|
||||||
e.currentTarget.style.borderColor = 'var(--bd)'
|
e.currentTarget.style.borderColor = 'var(--bd)'
|
||||||
e.currentTarget.style.background = 'var(--bg0)'
|
e.currentTarget.style.background = 'var(--bg0)'
|
||||||
}}>
|
}}
|
||||||
|
>
|
||||||
📁 이미지 파일을 선택하세요
|
📁 이미지 파일을 선택하세요
|
||||||
<input
|
<input
|
||||||
|
ref={fileInputRef}
|
||||||
type="file"
|
type="file"
|
||||||
accept="image/*"
|
accept="image/*"
|
||||||
onChange={handleImageUpload}
|
onChange={handleFileSelect}
|
||||||
className="hidden"
|
className="hidden"
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex items-center justify-between font-mono text-[10px] bg-bg-0 border border-border"
|
<div
|
||||||
style={{
|
className="flex items-center justify-between font-mono text-[10px] bg-bg-0 border border-border"
|
||||||
padding: '8px 10px',
|
style={{ padding: '8px 10px', borderRadius: 'var(--rS)' }}
|
||||||
borderRadius: 'var(--rS)',
|
>
|
||||||
}}>
|
<span className="text-text-2">📄 {uploadedFile.name}</span>
|
||||||
<span className="text-text-2">📄 {uploadedFileName || 'example_plot_0.gif'}</span>
|
|
||||||
<button
|
<button
|
||||||
onClick={removeUploadedImage}
|
onClick={handleRemoveFile}
|
||||||
className="text-[10px] text-text-3 bg-transparent border-none cursor-pointer"
|
className="text-[10px] text-text-3 bg-transparent border-none cursor-pointer"
|
||||||
style={{ padding: '2px 6px', transition: '0.15s' }}
|
style={{ padding: '2px 6px', transition: '0.15s' }}
|
||||||
onMouseEnter={(e) => {
|
onMouseEnter={(e) => { e.currentTarget.style.color = 'var(--red)' }}
|
||||||
e.currentTarget.style.color = 'var(--red)'
|
onMouseLeave={(e) => { e.currentTarget.style.color = 'var(--t3)' }}
|
||||||
}}
|
|
||||||
onMouseLeave={(e) => {
|
|
||||||
e.currentTarget.style.color = 'var(--t3)'
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dropdowns */}
|
{/* 분석 실행 버튼 */}
|
||||||
<div className="grid grid-cols-2 gap-1">
|
<button
|
||||||
<ComboBox
|
className="prd-btn pri"
|
||||||
className="prd-i"
|
style={{ padding: '7px', fontSize: '11px' }}
|
||||||
value=""
|
onClick={handleAnalyze}
|
||||||
onChange={() => {}}
|
disabled={!uploadedFile || isAnalyzing}
|
||||||
options={[
|
>
|
||||||
{ value: '', label: '유출회사' },
|
{isAnalyzing ? '⏳ 분석 중...' : '🔍 이미지 분석 실행'}
|
||||||
{ value: 'company1', label: '회사A' },
|
</button>
|
||||||
{ value: 'company2', label: '회사B' }
|
|
||||||
]}
|
{/* 에러 메시지 */}
|
||||||
placeholder="유출회사"
|
{analyzeError && (
|
||||||
/>
|
<div
|
||||||
<ComboBox
|
className="text-[10px] font-semibold"
|
||||||
className="prd-i"
|
style={{
|
||||||
value=""
|
padding: '6px 8px',
|
||||||
onChange={() => {}}
|
background: 'rgba(239,68,68,0.1)',
|
||||||
options={[
|
border: '1px solid rgba(239,68,68,0.3)',
|
||||||
{ value: '', label: '예상시각' },
|
borderRadius: 'var(--rS)',
|
||||||
{ value: '09:00', label: '09:00' },
|
color: 'var(--red)',
|
||||||
{ value: '12:00', label: '12:00' }
|
}}
|
||||||
]}
|
>
|
||||||
placeholder="예상시각"
|
⚠ {analyzeError}
|
||||||
/>
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
|
{/* 분석 완료 메시지 */}
|
||||||
|
{analyzeResult && (
|
||||||
|
<div
|
||||||
|
className="text-[10px] font-semibold"
|
||||||
|
style={{
|
||||||
|
padding: '6px 8px',
|
||||||
|
background: 'rgba(34,197,94,0.1)',
|
||||||
|
border: '1px solid rgba(34,197,94,0.3)',
|
||||||
|
borderRadius: 'var(--rS)',
|
||||||
|
color: '#22c55e',
|
||||||
|
lineHeight: 1.6,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
✓ 분석 완료<br />
|
||||||
|
<span className="font-normal text-text-3">
|
||||||
|
위도 {analyzeResult.lat.toFixed(4)} / 경도 {analyzeResult.lon.toFixed(4)}<br />
|
||||||
|
유종: {analyzeResult.oilType} / 면적: {analyzeResult.area.toFixed(1)} m²
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -283,11 +304,10 @@ const PredictionInputSection = ({
|
|||||||
</div>
|
</div>
|
||||||
{/* 도분초 표시 */}
|
{/* 도분초 표시 */}
|
||||||
{incidentCoord && !isNaN(incidentCoord.lat) && !isNaN(incidentCoord.lon) && (
|
{incidentCoord && !isNaN(incidentCoord.lat) && !isNaN(incidentCoord.lon) && (
|
||||||
<div className="text-[9px] text-text-3 font-mono border border-border bg-bg-0"
|
<div
|
||||||
style={{
|
className="text-[9px] text-text-3 font-mono border border-border bg-bg-0"
|
||||||
padding: '4px 8px',
|
style={{ padding: '4px 8px', borderRadius: 'var(--rS)' }}
|
||||||
borderRadius: 'var(--rS)',
|
>
|
||||||
}}>
|
|
||||||
{decimalToDMS(incidentCoord.lat, true)} / {decimalToDMS(incidentCoord.lon, false)}
|
{decimalToDMS(incidentCoord.lat, true)} / {decimalToDMS(incidentCoord.lon, false)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@ -355,19 +375,6 @@ const PredictionInputSection = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Image Analysis Note (Upload Mode Only) */}
|
|
||||||
{inputMode === 'upload' && uploadedImage && (
|
|
||||||
<div className="text-[9px] text-text-3 leading-[1.4]"
|
|
||||||
style={{
|
|
||||||
padding: '8px',
|
|
||||||
background: 'rgba(59,130,246,0.08)',
|
|
||||||
border: '1px solid rgba(59,130,246,0.2)',
|
|
||||||
borderRadius: 'var(--rS)',
|
|
||||||
}}>
|
|
||||||
📊 이미지 내 확산경로를 분석하였습니다. 각 방제요소 가이드 참고하세요.
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Divider */}
|
{/* Divider */}
|
||||||
<div className="h-px bg-border my-0.5" />
|
<div className="h-px bg-border my-0.5" />
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type { PredictionModel } from './OilSpillView'
|
import type { PredictionModel } from './OilSpillView'
|
||||||
import type { BoomLine, BoomLineCoord, AlgorithmSettings, ContainmentResult } from '@common/types/boomLine'
|
import type { BoomLine, BoomLineCoord, AlgorithmSettings, ContainmentResult } from '@common/types/boomLine'
|
||||||
import type { Analysis } from './AnalysisListTable'
|
import type { Analysis } from './AnalysisListTable'
|
||||||
|
import type { ImageAnalyzeResult } from '../services/predictionApi'
|
||||||
|
|
||||||
export interface LeftPanelProps {
|
export interface LeftPanelProps {
|
||||||
selectedAnalysis?: Analysis | null
|
selectedAnalysis?: Analysis | null
|
||||||
@ -45,6 +46,8 @@ export interface LeftPanelProps {
|
|||||||
onLayerOpacityChange: (val: number) => void
|
onLayerOpacityChange: (val: number) => void
|
||||||
layerBrightness: number
|
layerBrightness: number
|
||||||
onLayerBrightnessChange: (val: number) => void
|
onLayerBrightnessChange: (val: number) => void
|
||||||
|
// 이미지 분석 결과 콜백
|
||||||
|
onImageAnalysisResult?: (result: ImageAnalyzeResult) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ExpandedSections {
|
export interface ExpandedSections {
|
||||||
|
|||||||
@ -192,3 +192,28 @@ export const fetchAnalysisTrajectory = async (acdntSn: number): Promise<Trajecto
|
|||||||
const response = await api.get<TrajectoryResponse>(`/prediction/analyses/${acdntSn}/trajectory`);
|
const response = await api.get<TrajectoryResponse>(`/prediction/analyses/${acdntSn}/trajectory`);
|
||||||
return response.data;
|
return response.data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ============================================================
|
||||||
|
// 이미지 업로드 분석
|
||||||
|
// ============================================================
|
||||||
|
|
||||||
|
export interface ImageAnalyzeResult {
|
||||||
|
acdntSn: number;
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
oilType: string;
|
||||||
|
area: number;
|
||||||
|
volume: number;
|
||||||
|
fileId: string;
|
||||||
|
occurredAt: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const analyzeImage = async (file: File): Promise<ImageAnalyzeResult> => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('image', file);
|
||||||
|
const response = await api.post<ImageAnalyzeResult>('/prediction/image-analyze', formData, {
|
||||||
|
headers: { 'Content-Type': 'multipart/form-data' },
|
||||||
|
timeout: 330_000,
|
||||||
|
});
|
||||||
|
return response.data;
|
||||||
|
};
|
||||||
|
|||||||
13
prediction/image/.dockerignore
Normal file
13
prediction/image/.dockerignore
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
__pycache__/
|
||||||
|
stitch/
|
||||||
|
|
||||||
|
mx15hdi/Detect/Mask_result/
|
||||||
|
mx15hdi/Detect/result/
|
||||||
|
|
||||||
|
mx15hdi/Georeference/Mask_Tif/
|
||||||
|
mx15hdi/Georeference/Tif/
|
||||||
|
|
||||||
|
mx15hdi/Metadata/CSV/
|
||||||
|
mx15hdi/Metadata/Image/Original_Images/
|
||||||
|
|
||||||
|
mx15hdi/Polygon/Shp/
|
||||||
299
prediction/image/DOCKER_USAGE.md
Normal file
299
prediction/image/DOCKER_USAGE.md
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
# wing-image-analysis Docker 사용 가이드
|
||||||
|
|
||||||
|
드론 영상 기반 유류 오염 분석 FastAPI 서버를 Docker 컨테이너로 빌드하고 실행하는 방법을 설명한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 목차
|
||||||
|
|
||||||
|
1. [사전 요구사항](#1-사전-요구사항)
|
||||||
|
2. [빠른 시작](#2-빠른-시작)
|
||||||
|
3. [빌드 명령어](#3-빌드-명령어)
|
||||||
|
4. [실행 명령어](#4-실행-명령어)
|
||||||
|
5. [환경변수 설정](#5-환경변수-설정)
|
||||||
|
6. [볼륨 구조](#6-볼륨-구조)
|
||||||
|
7. [API 엔드포인트 사용 예시](#7-api-엔드포인트-사용-예시)
|
||||||
|
8. [로그 확인 및 디버깅](#8-로그-확인-및-디버깅)
|
||||||
|
9. [컨테이너 관리](#9-컨테이너-관리)
|
||||||
|
10. [주의사항](#10-주의사항)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 사전 요구사항
|
||||||
|
|
||||||
|
| 항목 | 최소 버전 | 확인 명령어 |
|
||||||
|
|------|----------|-------------|
|
||||||
|
| Docker Engine | 24.0 이상 | `docker --version` |
|
||||||
|
| Docker Compose | 2.20 이상 | `docker compose version` |
|
||||||
|
| NVIDIA 드라이버 | 525 이상 (CUDA 12.1 지원) | `nvidia-smi` |
|
||||||
|
| nvidia-container-toolkit | 최신 | `nvidia-ctk --version` |
|
||||||
|
|
||||||
|
### nvidia-container-toolkit 설치 (Ubuntu 기준)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# GPG 키 및 저장소 추가
|
||||||
|
curl -fsSL https://nvidia.github.io/libnvidia-container/gpgkey \
|
||||||
|
| sudo gpg --dearmor -o /usr/share/keyrings/nvidia-container-toolkit-keyring.gpg
|
||||||
|
|
||||||
|
curl -s -L https://nvidia.github.io/libnvidia-container/stable/deb/nvidia-container-toolkit.list \
|
||||||
|
| sed 's#deb https://#deb [signed-by=/usr/share/keyrings/nvidia-container-toolkit-keyring.gpg] https://#g' \
|
||||||
|
| sudo tee /etc/apt/sources.list.d/nvidia-container-toolkit.list
|
||||||
|
|
||||||
|
sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
|
||||||
|
|
||||||
|
# Docker 런타임 설정 및 재시작
|
||||||
|
sudo nvidia-ctk runtime configure --runtime=docker
|
||||||
|
sudo systemctl restart docker
|
||||||
|
```
|
||||||
|
|
||||||
|
### GPU 동작 확인
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm --gpus all nvidia/cuda:12.1-base-ubuntu22.04 nvidia-smi
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 빠른 시작
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. prediction/image/ 디렉토리로 이동
|
||||||
|
cd prediction/image
|
||||||
|
|
||||||
|
# 2. 환경변수 파일 준비 (필요 시)
|
||||||
|
cp .env.example .env
|
||||||
|
|
||||||
|
# 3. 빌드 + 실행 (백그라운드)
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# 4. 서버 상태 확인
|
||||||
|
curl http://localhost:5001/docs
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 빌드 명령어
|
||||||
|
|
||||||
|
### docker compose (권장)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 이미지 빌드만 수행 (실행 안 함)
|
||||||
|
docker compose build
|
||||||
|
|
||||||
|
# 빌드 로그를 상세하게 출력
|
||||||
|
docker compose build --progress=plain
|
||||||
|
|
||||||
|
# 캐시 없이 처음부터 빌드 (의존성 변경 시)
|
||||||
|
docker compose build --no-cache
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker build (단독)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# prediction/image/ 디렉토리에서 실행
|
||||||
|
docker build -t wing-image-analysis:latest .
|
||||||
|
|
||||||
|
# 빌드 태그 지정
|
||||||
|
docker build -t wing-image-analysis:1.0.0 .
|
||||||
|
|
||||||
|
# 캐시 없이 빌드
|
||||||
|
docker build --no-cache -t wing-image-analysis:latest .
|
||||||
|
```
|
||||||
|
|
||||||
|
> **참고**: 첫 빌드는 PyTorch base 이미지(약 8GB) + GDAL/Python 패키지 설치로 **30~60분** 소요될 수 있다.
|
||||||
|
> 이후 빌드는 레이어 캐시로 수 분 내 완료된다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 실행 명령어
|
||||||
|
|
||||||
|
### docker compose (권장)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 백그라운드 실행
|
||||||
|
docker compose up -d
|
||||||
|
|
||||||
|
# 빌드 후 즉시 실행
|
||||||
|
docker compose up -d --build
|
||||||
|
|
||||||
|
# 포그라운드 실행 (로그 바로 출력)
|
||||||
|
docker compose up
|
||||||
|
|
||||||
|
# 중지
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# 중지 + 볼륨 삭제 (데이터 초기화 시)
|
||||||
|
docker compose down -v
|
||||||
|
```
|
||||||
|
|
||||||
|
### docker run (단독 — 테스트용)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run --rm \
|
||||||
|
--gpus all \
|
||||||
|
-p 5001:5001 \
|
||||||
|
--env-file .env \
|
||||||
|
-v "$(pwd)/mx15hdi/Metadata/Image/Original_Images:/app/mx15hdi/Metadata/Image/Original_Images" \
|
||||||
|
wing-image-analysis:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 환경변수 설정
|
||||||
|
|
||||||
|
`.env.example`을 복사하여 `.env`를 생성한다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
| 변수 | 설명 | 기본값 |
|
||||||
|
|------|------|--------|
|
||||||
|
| `API_HOST` | 서버 바인드 주소 | `0.0.0.0` |
|
||||||
|
| `API_PORT` | 서버 포트 | `5001` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 볼륨 구조
|
||||||
|
|
||||||
|
컨테이너 내부 경로와 호스트 경로의 매핑이다. 이미지/결과 데이터는 컨테이너 외부에 저장되어 컨테이너를 재시작해도 유지된다.
|
||||||
|
|
||||||
|
```
|
||||||
|
호스트 (prediction/image/) 컨테이너 (/app/)
|
||||||
|
─────────────────────────────────────────────────────────────────────
|
||||||
|
mx15hdi/Metadata/Image/Original_Images/ → mx15hdi/Metadata/Image/Original_Images/ ← 원본 이미지 입력
|
||||||
|
mx15hdi/Metadata/CSV/ → mx15hdi/Metadata/CSV/ ← 메타데이터 출력
|
||||||
|
mx15hdi/Georeference/Tif/ → mx15hdi/Georeference/Tif/ ← GeoTIFF 출력
|
||||||
|
mx15hdi/Georeference/Mask_Tif/ → mx15hdi/Georeference/Mask_Tif/ ← 마스크 GeoTIFF
|
||||||
|
mx15hdi/Polygon/Shp/ → mx15hdi/Polygon/Shp/ ← Shapefile 출력
|
||||||
|
mx15hdi/Detect/result/ → mx15hdi/Detect/result/ ← 블렌딩 결과
|
||||||
|
mx15hdi/Detect/Mask_result/ → mx15hdi/Detect/Mask_result/ ← 마스크 결과
|
||||||
|
starsafire/Metadata/Image/Original_Images → starsafire/Metadata/Image/Original_Images ← 열화상 입력
|
||||||
|
starsafire/{기타}/ → starsafire/{기타}/ ← 열화상 출력
|
||||||
|
stitch/ → stitch/ ← 스티칭 결과
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. API 엔드포인트 사용 예시
|
||||||
|
|
||||||
|
서버 기동 후 `http://localhost:5001/docs`에서 Swagger UI로 전체 API를 확인할 수 있다.
|
||||||
|
|
||||||
|
### 7.1 전체 분석 파이프라인 실행
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:5001/run-script/ \
|
||||||
|
-F "files=@/path/to/drone_image.jpg" \
|
||||||
|
-F "camTy=mx15hdi" \
|
||||||
|
-F "fileId=20240310_001"
|
||||||
|
```
|
||||||
|
|
||||||
|
**응답 예시**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"meta": "drone_image.jpg,37,30,0,126,55,0,...",
|
||||||
|
"data": [
|
||||||
|
{
|
||||||
|
"classId": 2,
|
||||||
|
"area": 1234.56,
|
||||||
|
"volume": 0.1234,
|
||||||
|
"note": "갈색",
|
||||||
|
"thickness": 0.0001,
|
||||||
|
"wkt": "POLYGON((...))"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 메타데이터 조회
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:5001/get-metadata/mx15hdi/20240310_001
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.3 원본 이미지 조회 (Base64)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:5001/get-original-image/mx15hdi/20240310_001
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.4 GeoTIFF + 좌표 조회
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl http://localhost:5001/get-image/mx15hdi/20240310_001
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.5 이미지 스티칭
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:5001/stitch \
|
||||||
|
-F "files=@photo1.jpg" \
|
||||||
|
-F "files=@photo2.jpg" \
|
||||||
|
-F "mode=drone"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. 로그 확인 및 디버깅
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 실시간 로그 출력
|
||||||
|
docker logs wing-image-analysis -f
|
||||||
|
|
||||||
|
# 최근 100줄만 출력
|
||||||
|
docker logs wing-image-analysis --tail 100
|
||||||
|
|
||||||
|
# 컨테이너 내부 쉘 접속
|
||||||
|
docker exec -it wing-image-analysis bash
|
||||||
|
|
||||||
|
# GPU 사용 현황 확인 (컨테이너 내부)
|
||||||
|
docker exec wing-image-analysis nvidia-smi
|
||||||
|
|
||||||
|
# Python 패키지 목록 확인
|
||||||
|
docker exec wing-image-analysis pip list
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. 컨테이너 관리
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 상태 확인
|
||||||
|
docker compose ps
|
||||||
|
|
||||||
|
# 재시작
|
||||||
|
docker compose restart
|
||||||
|
|
||||||
|
# 중지 (볼륨 유지)
|
||||||
|
docker compose down
|
||||||
|
|
||||||
|
# 이미지 삭제
|
||||||
|
docker rmi wing-image-analysis:latest
|
||||||
|
|
||||||
|
# 사용하지 않는 리소스 정리
|
||||||
|
docker system prune -f
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10. 주의사항
|
||||||
|
|
||||||
|
### GPU 필수
|
||||||
|
- AI 모델(`epoch_165.pth`)은 `cuda:0` 디바이스로 로드된다.
|
||||||
|
- GPU 없이 실행하면 서버 기동 시 오류가 발생한다.
|
||||||
|
- CPU 전용 환경에서 테스트하려면 `Inference.py`의 `device='cuda:0'`을 `device='cpu'`로 수정해야 한다.
|
||||||
|
|
||||||
|
### 첫 기동 시간
|
||||||
|
- AI 모델 로드: 약 **10~30초** 소요 (GPU 메모리에 로딩)
|
||||||
|
- 준비 완료 후 로그에 `Application startup complete` 메시지가 출력된다.
|
||||||
|
|
||||||
|
### workers=1 고정
|
||||||
|
- GPU 모델은 프로세스 간 공유가 불가하므로 uvicorn workers는 반드시 `1`로 유지해야 한다.
|
||||||
|
- 병렬 처리는 내부 `ThreadPoolExecutor`(max_workers=4)로 처리된다.
|
||||||
|
|
||||||
|
### 포트 충돌
|
||||||
|
- 기본 포트 `5001`이 다른 서비스와 충돌하면 `docker-compose.yml`의 `ports` 항목을 수정한다:
|
||||||
|
```yaml
|
||||||
|
ports:
|
||||||
|
- "5002:5001" # 호스트 5002 → 컨테이너 5001
|
||||||
|
```
|
||||||
75
prediction/image/Dockerfile
Normal file
75
prediction/image/Dockerfile
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
# ==============================================================================
|
||||||
|
# wing-image-analysis — 드론 영상 유류 분석 FastAPI 서버
|
||||||
|
#
|
||||||
|
# Base: PyTorch 2.1 + CUDA 12.1 + cuDNN 8 (devel 빌드 — GDAL 컴파일 필요)
|
||||||
|
# GPU: NVIDIA GPU 필수 (MMSegmentation 추론)
|
||||||
|
# Port: 5001
|
||||||
|
# ==============================================================================
|
||||||
|
FROM pytorch/pytorch:2.1.0-cuda12.1-cudnn8-devel
|
||||||
|
|
||||||
|
ENV DEBIAN_FRONTEND=noninteractive \
|
||||||
|
PYTHONDONTWRITEBYTECODE=1 \
|
||||||
|
PYTHONUNBUFFERED=1
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 시스템 패키지: GDAL / PROJ / GEOS (rasterio, geopandas 빌드 의존성)
|
||||||
|
# libpq-dev: psycopg2-binary 런타임 의존성
|
||||||
|
# libspatialindex-dev: geopandas 공간 인덱스
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
|
gdal-bin \
|
||||||
|
libgdal-dev \
|
||||||
|
libproj-dev \
|
||||||
|
libgeos-dev \
|
||||||
|
libspatialindex-dev \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
git \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# rasterio는 GDAL 헤더 버전을 맞춰 빌드해야 한다
|
||||||
|
ENV GDAL_VERSION=3.4.1
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# Python 의존성 설치
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 로컬 mmsegmentation 설치 (mx15hdi/Detect/mmsegmentation/)
|
||||||
|
# 번들 소스를 먼저 복사한 뒤 editable 설치한다
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
COPY mx15hdi/Detect/mmsegmentation/ /tmp/mmsegmentation/
|
||||||
|
RUN pip install --no-cache-dir -e /tmp/mmsegmentation/
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 소스 코드 전체 복사
|
||||||
|
# 대용량 데이터 디렉토리(Original_Images, result 등)는
|
||||||
|
# docker-compose.yml의 볼륨 마운트로 외부에서 주입된다
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# .dockerignore로 제외된 런타임 출력 디렉토리를 빈 폴더로 생성
|
||||||
|
# (볼륨 마운트 전에도 경로가 존재해야 한다)
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
RUN mkdir -p \
|
||||||
|
/app/stitch \
|
||||||
|
/app/mx15hdi/Detect/Mask_result \
|
||||||
|
/app/mx15hdi/Detect/result \
|
||||||
|
/app/mx15hdi/Georeference/Mask_Tif \
|
||||||
|
/app/mx15hdi/Georeference/Tif \
|
||||||
|
/app/mx15hdi/Metadata/CSV \
|
||||||
|
/app/mx15hdi/Metadata/Image/Original_Images \
|
||||||
|
/app/mx15hdi/Polygon/Shp
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
# 런타임 설정
|
||||||
|
# ------------------------------------------------------------------------------
|
||||||
|
EXPOSE 5001
|
||||||
|
|
||||||
|
# workers=1: GPU 모델을 프로세스 하나에서만 로드 (메모리 공유 불가)
|
||||||
|
CMD ["uvicorn", "api:app", "--host", "0.0.0.0", "--port", "5001", "--workers", "1"]
|
||||||
338
prediction/image/api.py
Normal file
338
prediction/image/api.py
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
from contextlib import asynccontextmanager
|
||||||
|
import asyncio
|
||||||
|
from concurrent.futures import ThreadPoolExecutor
|
||||||
|
|
||||||
|
from fastapi import FastAPI, HTTPException, File, UploadFile, Form
|
||||||
|
from fastapi.responses import Response, FileResponse
|
||||||
|
import subprocess
|
||||||
|
import rasterio
|
||||||
|
import numpy as np
|
||||||
|
from PIL import Image
|
||||||
|
from PIL.ExifTags import TAGS
|
||||||
|
import io
|
||||||
|
import base64
|
||||||
|
from pyproj import Transformer
|
||||||
|
from extract_data import get_metadata as get_meta
|
||||||
|
from extract_data import get_oil_type as get_oil
|
||||||
|
import time
|
||||||
|
|
||||||
|
from typing import List, Optional
|
||||||
|
import shutil
|
||||||
|
from datetime import datetime
|
||||||
|
from collections import Counter
|
||||||
|
|
||||||
|
# mx15hdi 파이프라인 모듈 임포트를 위한 sys.path 설정
|
||||||
|
_BASE_DIR = Path(__file__).parent
|
||||||
|
sys.path.insert(0, str(_BASE_DIR / 'mx15hdi' / 'Detect'))
|
||||||
|
sys.path.insert(0, str(_BASE_DIR / 'mx15hdi' / 'Metadata' / 'Scripts'))
|
||||||
|
sys.path.insert(0, str(_BASE_DIR / 'mx15hdi' / 'Georeference' / 'Scripts'))
|
||||||
|
sys.path.insert(0, str(_BASE_DIR / 'mx15hdi' / 'Polygon' / 'Scripts'))
|
||||||
|
|
||||||
|
from Inference import load_model, run_inference
|
||||||
|
from Export_Metadata_mx15hdi import run_metadata_export
|
||||||
|
from Create_Georeferenced_Images_nadir import run_georeference
|
||||||
|
from Oilshape import run_oilshape
|
||||||
|
|
||||||
|
# AI 모델 (서버 시작 시 1회 로드)
|
||||||
|
_model = None
|
||||||
|
# CPU/GPU 바운드 작업용 스레드 풀
|
||||||
|
_executor = ThreadPoolExecutor(max_workers=4)
|
||||||
|
|
||||||
|
|
||||||
|
@asynccontextmanager
|
||||||
|
async def lifespan(app: FastAPI):
|
||||||
|
"""서버 시작 시 AI 모델을 1회 로드하고, 종료 시 해제한다."""
|
||||||
|
global _model
|
||||||
|
print("AI 모델 로딩 중 (epoch_165.pth)...")
|
||||||
|
_model = load_model()
|
||||||
|
print("AI 모델 로드 완료")
|
||||||
|
yield
|
||||||
|
_model = None
|
||||||
|
|
||||||
|
|
||||||
|
app = FastAPI(lifespan=lifespan)
|
||||||
|
|
||||||
|
|
||||||
|
def check_gps_info(image_path: str):
|
||||||
|
# Pillow로 이미지 열기
|
||||||
|
image = Image.open(image_path)
|
||||||
|
|
||||||
|
# EXIF 데이터 추출
|
||||||
|
exifdata = image.getexif()
|
||||||
|
|
||||||
|
if not exifdata:
|
||||||
|
print("EXIF 정보를 찾을 수 없습니다.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# GPS 정보 추출
|
||||||
|
gps_ifd = exifdata.get_ifd(0x8825) # GPS IFD 태그
|
||||||
|
if not gps_ifd:
|
||||||
|
print("GPS 정보를 찾을 수 없습니다.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def check_camera_info(image_file):
|
||||||
|
# Pillow로 이미지 열기
|
||||||
|
image = Image.open(image_file)
|
||||||
|
|
||||||
|
# EXIF 데이터 추출
|
||||||
|
exifdata = image.getexif()
|
||||||
|
|
||||||
|
if not exifdata:
|
||||||
|
print("EXIF 정보를 찾을 수 없습니다.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
for tag_id, value in exifdata.items():
|
||||||
|
tag_name = TAGS.get(tag_id, tag_id)
|
||||||
|
if tag_name == "Model":
|
||||||
|
return value.strip() if isinstance(value, str) else value
|
||||||
|
|
||||||
|
|
||||||
|
async def _run_mx15hdi_pipeline(file_id: str):
|
||||||
|
"""
|
||||||
|
mx15hdi 파이프라인을 in-process로 실행한다.
|
||||||
|
- Step 1 (AI 추론) + Step 2 (메타데이터 추출) 병렬 실행
|
||||||
|
- Step 3 (지리참조) → Step 4 (폴리곤 추출) 순차 실행
|
||||||
|
- 중간 파일 I/O 없이 numpy 배열을 메모리로 전달
|
||||||
|
"""
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
# Step 1 + Step 2 병렬 실행 — inference_cache 캡처
|
||||||
|
inference_cache, _ = await asyncio.gather(
|
||||||
|
loop.run_in_executor(_executor, run_inference, _model, file_id),
|
||||||
|
loop.run_in_executor(_executor, run_metadata_export, file_id),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 3: Georeference — inference_cache 메모리로 전달, georef_cache 반환
|
||||||
|
georef_cache = await loop.run_in_executor(
|
||||||
|
_executor, run_georeference, file_id, inference_cache
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 4: Polygon 추출 — georef_cache 메모리로 전달 (Mask_Tif 디스크 읽기 없음)
|
||||||
|
await loop.run_in_executor(_executor, run_oilshape, file_id, georef_cache)
|
||||||
|
|
||||||
|
|
||||||
|
# 전체 과정을 구동하는 api
|
||||||
|
@app.post("/run-script/")
|
||||||
|
async def run_script(
|
||||||
|
# pollId: int = Form(...),
|
||||||
|
camTy: str = Form(...),
|
||||||
|
fileId: str = Form(...),
|
||||||
|
image: UploadFile = File(...)
|
||||||
|
):
|
||||||
|
try:
|
||||||
|
print("start")
|
||||||
|
start_time = time.perf_counter()
|
||||||
|
|
||||||
|
if camTy not in ["mx15hdi", "starsafire"]:
|
||||||
|
raise HTTPException(status_code=400, detail="string1 must be 'mx15hdi' or 'starsafire'")
|
||||||
|
|
||||||
|
# 저장할 이미지 경로 설정
|
||||||
|
upload_dir = os.path.join(camTy, "Metadata/Image/Original_Images", fileId)
|
||||||
|
os.makedirs(upload_dir, exist_ok=True)
|
||||||
|
|
||||||
|
# 이미지 파일 저장
|
||||||
|
image_path = os.path.join(upload_dir, image.filename)
|
||||||
|
with open(image_path, "wb") as f:
|
||||||
|
f.write(await image.read())
|
||||||
|
|
||||||
|
gps_flage = check_gps_info(image_path)
|
||||||
|
if not gps_flage:
|
||||||
|
return {"detail": "GPS Infomation Not Found"}
|
||||||
|
|
||||||
|
if camTy == "mx15hdi":
|
||||||
|
# in-process 파이프라인 실행 (모델 재로딩 없음, Step1+2 병렬)
|
||||||
|
await _run_mx15hdi_pipeline(fileId)
|
||||||
|
else:
|
||||||
|
# starsafire: 기존 subprocess 방식 유지
|
||||||
|
script_dir = os.path.join(os.getcwd(), camTy, "Main")
|
||||||
|
script_file = "Combine_module.py"
|
||||||
|
script_path = os.path.join(script_dir, script_file)
|
||||||
|
|
||||||
|
if not os.path.exists(script_path):
|
||||||
|
raise HTTPException(status_code=404, detail="Script not found")
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
["python", script_file, fileId],
|
||||||
|
cwd=script_dir,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
print(f"Subprocess stdout: {result.stdout}")
|
||||||
|
print(f"Subprocess stderr: {result.stderr}")
|
||||||
|
|
||||||
|
meta_string = get_meta(camTy, fileId)
|
||||||
|
oil_data = get_oil(camTy, fileId)
|
||||||
|
end_time = time.perf_counter()
|
||||||
|
print(f"Run time: {end_time - start_time:.4f} sec")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"meta": meta_string,
|
||||||
|
"data": oil_data
|
||||||
|
}
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
raise HTTPException(status_code=500, detail="Script execution timed out")
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/get-metadata/{camTy}/{fileId}")
|
||||||
|
async def get_metadata(camTy: str, fileId: str):
|
||||||
|
try:
|
||||||
|
meta_string = get_meta(camTy, fileId)
|
||||||
|
oil_data = get_oil(camTy, fileId)
|
||||||
|
|
||||||
|
return {
|
||||||
|
"meta": meta_string,
|
||||||
|
"data": oil_data
|
||||||
|
}
|
||||||
|
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
raise HTTPException(status_code=500, detail="Script execution timed out")
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/get-original-image/{camTy}/{fileId}")
|
||||||
|
async def get_original_image(camTy: str, fileId: str):
|
||||||
|
try:
|
||||||
|
image_path = os.path.join(camTy, "Metadata/Image/Original_Images", fileId)
|
||||||
|
files = os.listdir(image_path)
|
||||||
|
target_file = [f for f in files if f.endswith(".png") or f.endswith(".jpg")]
|
||||||
|
image_file = os.path.join(image_path, target_file[0])
|
||||||
|
|
||||||
|
with open(image_file, "rb") as origin_image:
|
||||||
|
base64_string = base64.b64encode(origin_image.read()).decode("utf-8")
|
||||||
|
print(base64_string[:100])
|
||||||
|
|
||||||
|
return base64_string
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
@app.get("/get-image/{camTy}/{fileId}")
|
||||||
|
async def get_image(camTy: str, fileId: str):
|
||||||
|
try:
|
||||||
|
tif_file_path = os.path.join(camTy, "Georeference/Tif", fileId)
|
||||||
|
files = os.listdir(tif_file_path)
|
||||||
|
target_file = [f for f in files if f.endswith(".tif")]
|
||||||
|
tif_file = os.path.join(tif_file_path, target_file[0])
|
||||||
|
|
||||||
|
with rasterio.open(tif_file) as dataset:
|
||||||
|
crs = dataset.crs
|
||||||
|
|
||||||
|
bounds = dataset.bounds
|
||||||
|
|
||||||
|
if crs != "EPSG:4326":
|
||||||
|
transformer = Transformer.from_crs(crs, "EPSG:4326", always_xy=True)
|
||||||
|
minx, miny = transformer.transform(bounds.left, bounds.bottom)
|
||||||
|
maxx, maxy = transformer.transform(bounds.right, bounds.top)
|
||||||
|
|
||||||
|
print(minx, miny, maxx, maxy)
|
||||||
|
|
||||||
|
data = dataset.read()
|
||||||
|
if data.shape[0] == 1:
|
||||||
|
image_data = data[0]
|
||||||
|
else:
|
||||||
|
image_data = np.moveaxis(data, 0, -1)
|
||||||
|
|
||||||
|
image = Image.fromarray(image_data)
|
||||||
|
buffer = io.BytesIO()
|
||||||
|
image.save(buffer, format="PNG")
|
||||||
|
|
||||||
|
base64_string = base64.b64encode(buffer.getvalue()).decode("utf-8")
|
||||||
|
|
||||||
|
print(base64_string[:100])
|
||||||
|
return {
|
||||||
|
"minLon": minx,
|
||||||
|
"minLat": miny,
|
||||||
|
"maxLon": maxx,
|
||||||
|
"maxLat": maxy,
|
||||||
|
"image": base64_string
|
||||||
|
}
|
||||||
|
except Exception as e:
|
||||||
|
raise HTTPException(status_code=500, detail=str(e))
|
||||||
|
|
||||||
|
|
||||||
|
BASE_DIR = Path(__file__).parent
|
||||||
|
PIC_GPS_SCRIPT = BASE_DIR / "pic_gps.py"
|
||||||
|
|
||||||
|
@app.post("/stitch")
|
||||||
|
async def stitch(
|
||||||
|
files: List[UploadFile] = File(..., description="합성할 이미지 파일들 (2장 이상)"),
|
||||||
|
fileId: str = Form(...)
|
||||||
|
):
|
||||||
|
if len(files) < 2:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="최소 2장 이상의 이미지가 필요합니다."
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
today = datetime.now().strftime("%Y%m%d")
|
||||||
|
upload_dir = BASE_DIR / "stitch" / fileId
|
||||||
|
upload_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
model_list = []
|
||||||
|
for idx, file in enumerate(files):
|
||||||
|
|
||||||
|
model = check_camera_info(file.file)
|
||||||
|
model_list.append(model)
|
||||||
|
|
||||||
|
original_filename = file.filename or f"image_{idx}.jpg"
|
||||||
|
filename = f"{model}_{idx:03d}_{original_filename}"
|
||||||
|
file_path = upload_dir / filename
|
||||||
|
|
||||||
|
output_filename = f"stitched_{fileId}.jpg"
|
||||||
|
output_path = upload_dir / output_filename
|
||||||
|
|
||||||
|
# 파일 저장
|
||||||
|
with open(file_path, "wb") as buffer:
|
||||||
|
shutil.copyfileobj(file.file, buffer)
|
||||||
|
|
||||||
|
model_counter = Counter(model_list)
|
||||||
|
most_common_model = model_counter.most_common(1)
|
||||||
|
|
||||||
|
cmd = [
|
||||||
|
"python",
|
||||||
|
str(PIC_GPS_SCRIPT),
|
||||||
|
"--mode", "drone",
|
||||||
|
"--input", str(upload_dir),
|
||||||
|
"--out", str(output_path),
|
||||||
|
"--model", most_common_model[0][0],
|
||||||
|
"--enhance"
|
||||||
|
]
|
||||||
|
|
||||||
|
print(cmd)
|
||||||
|
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=300
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Subprocess stdout: {result.stdout}")
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"Subprocess stderr: {result.stderr}")
|
||||||
|
raise HTTPException(status_code=500, detail=f"Script failed: {result.stderr}")
|
||||||
|
|
||||||
|
return FileResponse(
|
||||||
|
path=str(output_path),
|
||||||
|
media_type="image/jpeg",
|
||||||
|
filename=output_filename
|
||||||
|
)
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import uvicorn
|
||||||
|
uvicorn.run(app, host="0.0.0.0", port=5001)
|
||||||
48
prediction/image/docker-compose.yml
Normal file
48
prediction/image/docker-compose.yml
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
version: "3.9"
|
||||||
|
|
||||||
|
services:
|
||||||
|
image-analysis:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: Dockerfile
|
||||||
|
image: wing-image-analysis:latest
|
||||||
|
container_name: wing-image-analysis
|
||||||
|
ports:
|
||||||
|
- "5001:5001"
|
||||||
|
env_file: .env
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# ── mx15hdi (EO 드론 카메라) ────────────────────────────────────────
|
||||||
|
# 입력: 업로드된 원본 이미지
|
||||||
|
- ./mx15hdi/Metadata/Image/Original_Images:/app/mx15hdi/Metadata/Image/Original_Images
|
||||||
|
# 출력: 메타데이터 CSV
|
||||||
|
- ./mx15hdi/Metadata/CSV:/app/mx15hdi/Metadata/CSV
|
||||||
|
# 출력: 지리참조 GeoTIFF (컬러 / 마스크)
|
||||||
|
- ./mx15hdi/Georeference/Tif:/app/mx15hdi/Georeference/Tif
|
||||||
|
- ./mx15hdi/Georeference/Mask_Tif:/app/mx15hdi/Georeference/Mask_Tif
|
||||||
|
# 출력: 유류 폴리곤 Shapefile
|
||||||
|
- ./mx15hdi/Polygon/Shp:/app/mx15hdi/Polygon/Shp
|
||||||
|
# 출력: 블렌딩 추론 결과 / 마스크 이미지
|
||||||
|
- ./mx15hdi/Detect/result:/app/mx15hdi/Detect/result
|
||||||
|
- ./mx15hdi/Detect/Mask_result:/app/mx15hdi/Detect/Mask_result
|
||||||
|
# ── starsafire (열화상 카메라) ──────────────────────────────────────
|
||||||
|
- ./starsafire/Metadata/Image/Original_Images:/app/starsafire/Metadata/Image/Original_Images
|
||||||
|
- ./starsafire/Metadata/CSV:/app/starsafire/Metadata/CSV
|
||||||
|
- ./starsafire/Georeference/Tif:/app/starsafire/Georeference/Tif
|
||||||
|
- ./starsafire/Georeference/Mask_Tif:/app/starsafire/Georeference/Mask_Tif
|
||||||
|
- ./starsafire/Polygon/Shp:/app/starsafire/Polygon/Shp
|
||||||
|
- ./starsafire/Detect/result:/app/starsafire/Detect/result
|
||||||
|
- ./starsafire/Detect/Mask_result:/app/starsafire/Detect/Mask_result
|
||||||
|
# ── 스티칭 결과 ─────────────────────────────────────────────────────
|
||||||
|
- ./stitch:/app/stitch
|
||||||
|
|
||||||
|
# NVIDIA GPU 할당 (nvidia-container-toolkit 필수)
|
||||||
|
deploy:
|
||||||
|
resources:
|
||||||
|
reservations:
|
||||||
|
devices:
|
||||||
|
- driver: nvidia
|
||||||
|
count: 1
|
||||||
|
capabilities: [gpu]
|
||||||
|
|
||||||
|
restart: unless-stopped
|
||||||
97
prediction/image/extract_data.py
Normal file
97
prediction/image/extract_data.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import csv
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
import geopandas as gpd
|
||||||
|
import json
|
||||||
|
|
||||||
|
def get_metadata(camTy: str, fileId: str):
|
||||||
|
|
||||||
|
# CSV 파일 경로 설정
|
||||||
|
# base_dir = "mx15hdi" if pollId == "1" else "starsafire"
|
||||||
|
if camTy == "mx15hdi":
|
||||||
|
csv_path = f"{camTy}/Metadata/CSV/{fileId}/mx15hdi_interpolation.csv"
|
||||||
|
elif camTy == "starsafire":
|
||||||
|
csv_path = f"{camTy}/Metadata/CSV/{fileId}/Metadata_Extracted.csv"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# CSV 파일 읽기
|
||||||
|
with open(csv_path, 'r', newline='', encoding='utf-8-sig') as csvfile:
|
||||||
|
reader = csv.reader(csvfile)
|
||||||
|
next(reader, None)
|
||||||
|
row = next(reader, None)
|
||||||
|
return ','.join(row)
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"CSV file not found: {csv_path}")
|
||||||
|
raise
|
||||||
|
except ValueError as e:
|
||||||
|
print(f"Value error: {str(e)}")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing CSV: {e}")
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
def get_oil_type(camTy: str, fileId: str):
|
||||||
|
# Shapefile 경로 설정
|
||||||
|
path = f"{camTy}/Polygon/Shp/{fileId}"
|
||||||
|
shp_file = list(Path(path).glob("*.shp"))
|
||||||
|
if not shp_file:
|
||||||
|
return []
|
||||||
|
shp_path = f"{camTy}/Polygon/Shp/{fileId}/{shp_file[0].name}"
|
||||||
|
print(shp_path)
|
||||||
|
# if camTy == "mx15hdi":
|
||||||
|
# fileSub = f"{Path(fileName).stem}_gsd"
|
||||||
|
# elif camTy == "starsafire":
|
||||||
|
# fileSub = f"{Path(fileName).stem}"
|
||||||
|
|
||||||
|
# shp_path = f"{camTy}/Polygon/Shp/{fileId}/{fileSub}.shp"
|
||||||
|
|
||||||
|
# 두께 정보
|
||||||
|
class_thickness_mm = {
|
||||||
|
1: 1.0, # Black oil (Emulsion)
|
||||||
|
2: 0.1, # Brown oil (Crude)
|
||||||
|
3: 0.0003, # Rainbow oil (Slick)
|
||||||
|
4: 0.0001 # Silver oil (Slick)
|
||||||
|
}
|
||||||
|
# 알고리즘 정보
|
||||||
|
algorithm = {
|
||||||
|
1: "검정",
|
||||||
|
2: "갈색",
|
||||||
|
3: "무지개",
|
||||||
|
4: "은색"
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Shapefile 읽기
|
||||||
|
gdf = gpd.read_file(shp_path)
|
||||||
|
if gdf.crs != "epsg:4326":
|
||||||
|
gdf = gdf.to_crs("epsg:4326")
|
||||||
|
|
||||||
|
# 데이터 준비
|
||||||
|
data = []
|
||||||
|
for _, row in gdf.iterrows():
|
||||||
|
class_id = row.get('class_id', None)
|
||||||
|
area_m2 = row.get('area_m2', None)
|
||||||
|
volume_m3 = row.get('volume_m3', None)
|
||||||
|
note = row.get('note', None)
|
||||||
|
thickness_m = class_thickness_mm.get(class_id, 0) / 1000.0
|
||||||
|
geom_wkt = row.geometry.wkt if row.geometry else None
|
||||||
|
result = {
|
||||||
|
"classId": algorithm.get(class_id, 0),
|
||||||
|
"area": area_m2,
|
||||||
|
"volume": volume_m3,
|
||||||
|
"note": note,
|
||||||
|
"thickness": thickness_m,
|
||||||
|
"wkt": geom_wkt
|
||||||
|
}
|
||||||
|
data.append(result)
|
||||||
|
|
||||||
|
return data
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Shapefile not found: {shp_path}")
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error processing shapefile or database: {str(e)}")
|
||||||
|
raise
|
||||||
238
prediction/image/image_plan.md
Normal file
238
prediction/image/image_plan.md
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
# 이미지 업로드 유류 분석 기능 구현 계획
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
드론/항공 촬영 이미지를 업로드하면 AI 세그멘테이션으로 유류 확산 정보(위치·유종·면적·부피)를 자동 추출하고, 결과를 DB에 저장한 뒤 예측정보 입력 폼에 자동 채워주는 기능이다.
|
||||||
|
이미지 분석 서버(`prediction/image/api.py`, FastAPI, 포트 5001)는 이미 구현되어 있으며, 프론트↔백엔드↔이미지 분석 서버 연동 및 결과 자동 채우기를 구현한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 전체 흐름
|
||||||
|
|
||||||
|
```
|
||||||
|
[프론트] 이미지 선택 → 분석 요청 버튼
|
||||||
|
↓ POST /api/prediction/image-analyze (multipart: image)
|
||||||
|
[백엔드]
|
||||||
|
├─ fileId = UUID 생성
|
||||||
|
├─ camTy = "mx15hdi" (하드코딩, 추후 이미지 EXIF 카메라 정보로 자동 판별 예정)
|
||||||
|
├─ 이미지 분석 서버로 전달 POST http://IMAGE_API_URL/run-script/
|
||||||
|
├─ 응답 파싱: meta(위경도 DMS→십진수 변환), data[0].classId→유종
|
||||||
|
├─ ACDNT INSERT (lat/lon/임시사고명)
|
||||||
|
├─ SPIL_DATA INSERT (유종/면적/img_rslt_data JSONB)
|
||||||
|
└─ 응답: { acdntSn, lat, lon, oilType, area, volume }
|
||||||
|
↓
|
||||||
|
[프론트] 폼 자동 채우기 (좌표·유종·유출량)
|
||||||
|
→ 사용자가 나머지 입력 후 "확산예측 실행"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 구현 단계
|
||||||
|
|
||||||
|
### Step 1 — DB 마이그레이션 (`database/migration/017_spil_img_rslt.sql`)
|
||||||
|
|
||||||
|
`SPIL_DATA` 테이블에 이미지 분석 결과 컬럼 추가.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
ALTER TABLE wing.spil_data
|
||||||
|
ADD COLUMN IF NOT EXISTS img_rslt_data JSONB;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 2 — 백엔드: 이미지 분석 엔드포인트
|
||||||
|
|
||||||
|
**파일**: `backend/src/prediction/predictionRouter.ts` (라우트 등록)
|
||||||
|
**신규 파일**: `backend/src/prediction/imageAnalyzeService.ts`
|
||||||
|
|
||||||
|
#### 엔드포인트
|
||||||
|
|
||||||
|
```
|
||||||
|
POST /api/prediction/image-analyze
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
Body: image (file)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `imageAnalyzeService.ts` 핵심 로직
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 1. fileId 생성 (crypto.randomUUID)
|
||||||
|
|
||||||
|
// 2. 이미지 분석 서버 호출
|
||||||
|
// camTy는 현재 "mx15hdi"로 하드코딩한다.
|
||||||
|
// TODO: 추후 이미지 EXIF에서 카메라 모델명을 읽어 camTy를 자동 판별하는 로직을
|
||||||
|
// 이미지 분석 서버(api.py)에 추가할 예정이다. (check_camera_info 함수 활용)
|
||||||
|
// FormData: { camTy: 'mx15hdi', fileId, image }
|
||||||
|
// → POST ${IMAGE_API_URL}/run-script/
|
||||||
|
// 응답: { meta: string, data: OilPolygon[] }
|
||||||
|
|
||||||
|
// 3. meta 문자열 파싱 (mx15hdi CSV 컬럼 순서 사용)
|
||||||
|
// [Filename, Tlat_d, Tlat_m, Tlat_s, Tlon_d, Tlon_m, Tlon_s, ...]
|
||||||
|
// DMS → 십진수: d + m/60 + s/3600
|
||||||
|
|
||||||
|
// 4. 유종 매핑 (data[0].classId → UI 유종명)
|
||||||
|
// classId → oilType: { '검정': '벙커C유', '갈색': '벙커C유', '무지개': '경유', '은색': '등유' }
|
||||||
|
|
||||||
|
// 5. ACDNT INSERT (임시 사고명 = "이미지분석_YYYY-MM-DD HH:mm", lat, lon, occurredAt = 촬영시각)
|
||||||
|
// 6. SPIL_DATA INSERT (acdntSn, matTyCd, matVol=data[0].volume, imgRsltData=JSON.stringify(response))
|
||||||
|
|
||||||
|
// 7. 반환
|
||||||
|
interface ImageAnalyzeResult {
|
||||||
|
acdntSn: number;
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
oilType: string; // UI 유종명 (벙커C유 등)
|
||||||
|
area: number; // m²
|
||||||
|
volume: number; // m³
|
||||||
|
fileId: string;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 환경변수 추가 (`backend/.env`)
|
||||||
|
|
||||||
|
```
|
||||||
|
IMAGE_API_URL=http://localhost:5001
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 에러 처리
|
||||||
|
|
||||||
|
| 조건 | 응답 |
|
||||||
|
|------|------|
|
||||||
|
| 이미지에 GPS EXIF 없음 | 422 `{ error: 'GPS_NOT_FOUND' }` |
|
||||||
|
| 이미지 서버 타임아웃(300s) | 504 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 3 — 프론트엔드: API 서비스
|
||||||
|
|
||||||
|
**파일**: `frontend/src/tabs/prediction/services/predictionApi.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
interface ImageAnalyzeResult {
|
||||||
|
acdntSn: number;
|
||||||
|
lat: number;
|
||||||
|
lon: number;
|
||||||
|
oilType: string;
|
||||||
|
area: number;
|
||||||
|
volume: number;
|
||||||
|
fileId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const analyzeImage = async (
|
||||||
|
file: File
|
||||||
|
): Promise<ImageAnalyzeResult> => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('image', file);
|
||||||
|
const { data } = await api.post<ImageAnalyzeResult>(
|
||||||
|
'/prediction/image-analyze',
|
||||||
|
formData,
|
||||||
|
{ headers: { 'Content-Type': 'multipart/form-data' }, timeout: 330000 }
|
||||||
|
);
|
||||||
|
return data;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 4 — 프론트엔드: Props 타입 확장
|
||||||
|
|
||||||
|
**파일**: `frontend/src/tabs/prediction/components/leftPanelTypes.ts`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// 기존 Props에 추가
|
||||||
|
onImageAnalysisResult?: (result: ImageAnalyzeResult) => void;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 5 — 프론트엔드: PredictionInputSection 수정
|
||||||
|
|
||||||
|
**파일**: `frontend/src/tabs/prediction/components/PredictionInputSection.tsx`
|
||||||
|
|
||||||
|
#### 변경 사항
|
||||||
|
|
||||||
|
1. **"이미지 분석 실행" 버튼** (이미지 선택 후 활성화)
|
||||||
|
- 클릭 시 `analyzeImage(file)` 호출 (camTy는 백엔드에서 처리)
|
||||||
|
- 로딩 스피너 표시 (분석 소요시간 수십 초~수 분)
|
||||||
|
|
||||||
|
2. **분석 결과 표시** (성공 시)
|
||||||
|
- "분석 완료: 위도 XX.XXXX / 경도 XXX.XXXX / 유종: OO" 요약 메시지
|
||||||
|
|
||||||
|
3. **`onImageAnalysisResult` 콜백 호출**
|
||||||
|
- 분석 성공 시 부모로 결과 전달
|
||||||
|
|
||||||
|
4. **에러 처리**
|
||||||
|
- GPS_NOT_FOUND: "GPS 정보가 없는 이미지입니다" 메시지 표시
|
||||||
|
- 타임아웃: "분석 서버 응답 없음" 메시지 표시
|
||||||
|
|
||||||
|
5. **로컬 상태 교체**: `uploadedImage` (Base64 DataURL) 제거, `uploadedFile: File | null`로 교체
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Step 6 — 프론트엔드: OilSpillView 결과 처리
|
||||||
|
|
||||||
|
**파일**: `frontend/src/tabs/prediction/components/OilSpillView.tsx`
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
const handleImageAnalysisResult = useCallback((result: ImageAnalyzeResult) => {
|
||||||
|
// 1. 사고 좌표 자동 채우기
|
||||||
|
setIncidentCoord({ lat: result.lat, lon: result.lon })
|
||||||
|
setFlyToCoord({ lat: result.lat, lon: result.lon })
|
||||||
|
|
||||||
|
// 2. 유종/유출량 자동 채우기
|
||||||
|
setOilType(result.oilType)
|
||||||
|
setSpillAmount(parseFloat(result.volume.toFixed(4)))
|
||||||
|
setSpillUnit('m³')
|
||||||
|
|
||||||
|
// 3. 분석 선택 상태 갱신 (acdntSn 연결 — 시뮬레이션 실행 시 기존 사고 사용)
|
||||||
|
setSelectedAnalysis({
|
||||||
|
acdntSn: result.acdntSn,
|
||||||
|
acdntNm: '',
|
||||||
|
// ... 나머지 기본값
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
```
|
||||||
|
|
||||||
|
`LeftPanel`에 `onImageAnalysisResult={handleImageAnalysisResult}` 전달.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 수정 대상 파일 요약
|
||||||
|
|
||||||
|
| 파일 | 변경 유형 |
|
||||||
|
|------|---------|
|
||||||
|
| `database/migration/017_spil_img_rslt.sql` | **신규** — SPIL_DATA 컬럼 추가 |
|
||||||
|
| `backend/src/prediction/imageAnalyzeService.ts` | **신규** — 이미지 분석 서비스 |
|
||||||
|
| `backend/src/prediction/predictionRouter.ts` | **수정** — 라우트 추가 |
|
||||||
|
| `backend/.env` | **수정** — IMAGE_API_URL 추가 |
|
||||||
|
| `frontend/src/tabs/prediction/services/predictionApi.ts` | **수정** — analyzeImage 함수 추가 |
|
||||||
|
| `frontend/src/tabs/prediction/components/leftPanelTypes.ts` | **수정** — Props 타입 추가 |
|
||||||
|
| `frontend/src/tabs/prediction/components/PredictionInputSection.tsx` | **수정** — 분석 실행 UI |
|
||||||
|
| `frontend/src/tabs/prediction/components/OilSpillView.tsx` | **수정** — 결과 처리 핸들러 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 검증 방법
|
||||||
|
|
||||||
|
1. **이미지 분석 서버 직접 테스트**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:5001/run-script/ \
|
||||||
|
-F "camTy=mx15hdi" -F "fileId=test001" -F "image=@drone_image.jpg"
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **백엔드 엔드포인트 테스트**
|
||||||
|
```bash
|
||||||
|
curl -X POST http://localhost:3001/api/prediction/image-analyze \
|
||||||
|
-F "image=@drone_image.jpg" \
|
||||||
|
-H "Cookie: <auth_cookie>"
|
||||||
|
```
|
||||||
|
- 응답: `{ acdntSn, lat, lon, oilType, area, volume, fileId }`
|
||||||
|
- DB 확인: ACDNT, SPIL_DATA 레코드 생성 여부
|
||||||
|
|
||||||
|
3. **프론트엔드 E2E 테스트**
|
||||||
|
- 이미지 업로드 모드 선택 → GPS EXIF 있는 이미지 업로드 → "이미지 분석 실행" 클릭
|
||||||
|
- 로딩 표시 → 완료 시: 지도 이동, 유종/좌표 폼 자동 채워짐 확인
|
||||||
|
- 나머지 필드(예상시각·유출시간 등) 직접 입력 후 "확산예측 실행" → 정상 시뮬레이션 확인
|
||||||
|
|
||||||
|
4. **에러 케이스 확인**
|
||||||
|
- GPS 없는 이미지 → "GPS 정보가 없는 이미지입니다" 메시지
|
||||||
120
prediction/image/mx15hdi/Detect/Inference.py
Normal file
120
prediction/image/mx15hdi/Detect/Inference.py
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import os, mmcv, cv2, json
|
||||||
|
import numpy as np
|
||||||
|
from pathlib import Path
|
||||||
|
from PIL import Image
|
||||||
|
from tqdm import tqdm
|
||||||
|
from mmseg.apis import init_segmentor, inference_segmentor
|
||||||
|
from shapely.geometry import Polygon, mapping
|
||||||
|
import sys
|
||||||
|
|
||||||
|
_DETECT_DIR = Path(__file__).parent # mx15hdi/Detect/
|
||||||
|
_MX15HDI_DIR = _DETECT_DIR.parent # mx15hdi/
|
||||||
|
|
||||||
|
|
||||||
|
def load_model():
|
||||||
|
"""서버 시작 시 1회 호출. 로드된 모델 객체를 반환한다."""
|
||||||
|
config = str(_DETECT_DIR / 'V7_SPECIAL.py')
|
||||||
|
checkpoint = str(_DETECT_DIR / 'epoch_165.pth')
|
||||||
|
model = init_segmentor(config, checkpoint, device='cuda:0')
|
||||||
|
model.PALETTE = [
|
||||||
|
[0, 0, 0], # background
|
||||||
|
[0, 0, 204], # black
|
||||||
|
[180, 180, 180], # brown
|
||||||
|
[255, 255, 0], # rainbow
|
||||||
|
[178, 102, 255] # silver
|
||||||
|
]
|
||||||
|
return model
|
||||||
|
|
||||||
|
|
||||||
|
def blend_images(original_img, color_mask, alpha=0.6):
|
||||||
|
"""
|
||||||
|
Blend original image and color mask with alpha transparency.
|
||||||
|
Inputs: numpy arrays HWC uint8
|
||||||
|
"""
|
||||||
|
blended = cv2.addWeighted(original_img, 1 - alpha, color_mask, alpha, 0)
|
||||||
|
return blended
|
||||||
|
|
||||||
|
|
||||||
|
def run_inference(model, file_id: str, write_files: bool = False) -> dict:
|
||||||
|
"""
|
||||||
|
사전 로드된 모델로 file_id 폴더 내 이미지를 세그멘테이션한다.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model: load_model()로 로드된 모델 객체.
|
||||||
|
file_id: 처리할 이미지 폴더명.
|
||||||
|
write_files: True이면 Detect/result/ 와 Detect/Mask_result/ 에 중간 파일 저장.
|
||||||
|
False이면 디스크 쓰기 생략 (기본값).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
inference_cache: {image_filename: {'blended': ndarray, 'mask': ndarray, 'ext': str}}
|
||||||
|
이 값을 run_georeference()에 전달하면 중간 파일 읽기를 생략할 수 있다.
|
||||||
|
"""
|
||||||
|
img_path = str(_MX15HDI_DIR / 'Metadata' / 'Image' / 'Original_Images' / file_id)
|
||||||
|
output_folder = str(_DETECT_DIR / 'result' / file_id)
|
||||||
|
mask_folder = str(_DETECT_DIR / 'Mask_result' / file_id)
|
||||||
|
|
||||||
|
if not os.path.exists(img_path):
|
||||||
|
raise FileNotFoundError(f"이미지 폴더가 존재하지 않습니다: {img_path}")
|
||||||
|
|
||||||
|
if write_files:
|
||||||
|
os.makedirs(output_folder, exist_ok=True)
|
||||||
|
os.makedirs(mask_folder, exist_ok=True)
|
||||||
|
|
||||||
|
image_files = [
|
||||||
|
f for f in os.listdir(img_path)
|
||||||
|
if f.lower().endswith(('.jpg', '.jpeg', '.png', '.bmp', '.tif', '.tiff'))
|
||||||
|
]
|
||||||
|
|
||||||
|
# palette_array는 이미지마다 동일하므로 루프 외부에서 1회 생성
|
||||||
|
palette_array = np.array(model.PALETTE, dtype=np.uint8)
|
||||||
|
|
||||||
|
inference_cache = {}
|
||||||
|
|
||||||
|
for image_file in tqdm(image_files, desc="Processing images"):
|
||||||
|
image_path = os.path.join(img_path, image_file)
|
||||||
|
image_name, image_ext = os.path.splitext(image_file)
|
||||||
|
image_ext = image_ext.lower()
|
||||||
|
|
||||||
|
# 이미지를 1회만 읽어 inference와 blending 모두에 재사용
|
||||||
|
img_bgr = cv2.imread(image_path)
|
||||||
|
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
|
# 이미 로드된 배열을 inference_segmentor에 전달 (경로 전달 시 내부에서 재읽기 발생)
|
||||||
|
result = inference_segmentor(model, img_bgr)
|
||||||
|
seg_map = result[0]
|
||||||
|
|
||||||
|
# Create color mask from palette
|
||||||
|
color_mask = palette_array[seg_map]
|
||||||
|
|
||||||
|
# blended image
|
||||||
|
blended = blend_images(img_rgb, color_mask, alpha=0.6)
|
||||||
|
blended_bgr = cv2.cvtColor(blended, cv2.COLOR_RGB2BGR)
|
||||||
|
|
||||||
|
# mask — numpy 슬라이싱으로 cv2.cvtColor 호출 1회 제거
|
||||||
|
mask_bgr = color_mask[:, :, ::-1].copy()
|
||||||
|
|
||||||
|
# 결과를 메모리 캐시에 저장 (georeference 단계에서 재사용)
|
||||||
|
# mask는 palette 원본(RGB) 그대로 저장 — Oilshape의 class_colors가 RGB 기준이므로 BGR로 저장 시 색상 매칭 실패
|
||||||
|
inference_cache[image_file] = {
|
||||||
|
'blended': blended_bgr,
|
||||||
|
'mask': color_mask,
|
||||||
|
'ext': image_ext,
|
||||||
|
}
|
||||||
|
|
||||||
|
if write_files:
|
||||||
|
cv2.imwrite(
|
||||||
|
os.path.join(output_folder, f"{image_name}{image_ext}"),
|
||||||
|
blended_bgr,
|
||||||
|
[cv2.IMWRITE_JPEG_QUALITY, 85]
|
||||||
|
)
|
||||||
|
cv2.imwrite(os.path.join(mask_folder, f"{image_name}{image_ext}"), mask_bgr)
|
||||||
|
|
||||||
|
return inference_cache
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
raise ValueError("파라미터가 제공되지 않았습니다. 폴더 이름을 명령줄 인자로 입력해주세요.")
|
||||||
|
_model = load_model()
|
||||||
|
# CLI 단독 실행 시에는 중간 파일도 디스크에 저장
|
||||||
|
run_inference(_model, sys.argv[1], write_files=True)
|
||||||
196
prediction/image/mx15hdi/Detect/V7_SPECIAL.py
Normal file
196
prediction/image/mx15hdi/Detect/V7_SPECIAL.py
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet101_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=101,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='DAHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
pam_channels=64,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=5,
|
||||||
|
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
auxiliary_head=dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=5,
|
||||||
|
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=0.4)),
|
||||||
|
train_cfg=dict(),
|
||||||
|
test_cfg=dict(mode='whole'))
|
||||||
|
dataset_type = 'CustomDataset'
|
||||||
|
data_root = 'data/my_dataset_v7'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[119.54541993, 107.13545011, 96.71320316],
|
||||||
|
std=[60.3273945, 56.33692515, 55.71005772],
|
||||||
|
to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(512, 512)),
|
||||||
|
dict(type='RandomCrop', crop_size=(512, 512), cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', flip_ratio=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(
|
||||||
|
type='Normalize',
|
||||||
|
mean=[119.54541993, 107.13545011, 96.71320316],
|
||||||
|
std=[60.3273945, 56.33692515, 55.71005772],
|
||||||
|
to_rgb=True),
|
||||||
|
dict(type='Pad', size=(512, 512), pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(512, 512),
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(
|
||||||
|
type='Normalize',
|
||||||
|
mean=[119.54541993, 107.13545011, 96.71320316],
|
||||||
|
std=[60.3273945, 56.33692515, 55.71005772],
|
||||||
|
to_rgb=True),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type='CustomDataset',
|
||||||
|
data_root='data/my_dataset_v7',
|
||||||
|
img_dir='img_dir/train',
|
||||||
|
ann_dir='ann_dir/train',
|
||||||
|
pipeline=[
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(512, 512)),
|
||||||
|
dict(type='RandomCrop', crop_size=(512, 512), cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', flip_ratio=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(
|
||||||
|
type='Normalize',
|
||||||
|
mean=[119.54541993, 107.13545011, 96.71320316],
|
||||||
|
std=[60.3273945, 56.33692515, 55.71005772],
|
||||||
|
to_rgb=True),
|
||||||
|
dict(type='Pad', size=(512, 512), pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
|
||||||
|
]),
|
||||||
|
val=dict(
|
||||||
|
type='CustomDataset',
|
||||||
|
data_root='data/my_dataset_v7',
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=[
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(512, 512),
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(
|
||||||
|
type='Normalize',
|
||||||
|
mean=[119.54541993, 107.13545011, 96.71320316],
|
||||||
|
std=[60.3273945, 56.33692515, 55.71005772],
|
||||||
|
to_rgb=True),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
]),
|
||||||
|
test=dict(
|
||||||
|
type='CustomDataset',
|
||||||
|
data_root='data/my_dataset_v7',
|
||||||
|
img_dir='img_dir/test',
|
||||||
|
ann_dir='ann_dir/test',
|
||||||
|
pipeline=[
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(512, 512),
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(
|
||||||
|
type='Normalize',
|
||||||
|
mean=[119.54541993, 107.13545011, 96.71320316],
|
||||||
|
std=[60.3273945, 56.33692515, 55.71005772],
|
||||||
|
to_rgb=True),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
],
|
||||||
|
split=None,
|
||||||
|
img_suffix='.png',
|
||||||
|
seg_map_suffix='.png'))
|
||||||
|
dist_params = dict(backend='nccl')
|
||||||
|
log_level = 'INFO'
|
||||||
|
load_from = None
|
||||||
|
resume_from = None
|
||||||
|
#workflow = [('train', 1), ('val', 1)]
|
||||||
|
workflow = [('test', 1)]
|
||||||
|
cudnn_benchmark = True
|
||||||
|
optimizer = dict(
|
||||||
|
type='AdamW',
|
||||||
|
lr=3e-05,
|
||||||
|
betas=(0.9, 0.999),
|
||||||
|
weight_decay=0.01,
|
||||||
|
paramwise_cfg=dict(
|
||||||
|
custom_keys=dict(
|
||||||
|
pos_block=dict(decay_mult=0.0),
|
||||||
|
norm=dict(decay_mult=0.0),
|
||||||
|
head=dict(lr_mult=10.0))))
|
||||||
|
optimizer_config = dict()
|
||||||
|
lr_config = dict(
|
||||||
|
policy='poly',
|
||||||
|
warmup='linear',
|
||||||
|
warmup_iters=1500,
|
||||||
|
warmup_ratio=1e-06,
|
||||||
|
power=1.0,
|
||||||
|
min_lr=0.0,
|
||||||
|
by_epoch=False)
|
||||||
|
runner = dict(type='EpochBasedRunner', max_epochs=200)
|
||||||
|
checkpoint_config = dict(by_epoch=True, interval=1)
|
||||||
|
evaluation = dict(by_epoch=True, interval=1, metric='mIoU')
|
||||||
|
log_config = dict(
|
||||||
|
interval=1000,
|
||||||
|
hooks=[
|
||||||
|
dict(type='TextLoggerHook'),
|
||||||
|
dict(
|
||||||
|
type='WandbLoggerHook',
|
||||||
|
init_kwargs=dict(project='Oil_Spill', name='V7_SPECIAL'))
|
||||||
|
])
|
||||||
|
auto_resume = False
|
||||||
|
work_dir = 'work_dirs/V7_SPECIAL'
|
||||||
|
gpu_ids = [0]
|
||||||
@ -0,0 +1,161 @@
|
|||||||
|
version: 2.1
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
docker:
|
||||||
|
- image: cimg/python:3.7.4
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install dependencies
|
||||||
|
command: |
|
||||||
|
sudo apt-add-repository ppa:brightbox/ruby-ng -y
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ruby2.7
|
||||||
|
- run:
|
||||||
|
name: Install pre-commit hook
|
||||||
|
command: |
|
||||||
|
pip install pre-commit
|
||||||
|
pre-commit install
|
||||||
|
- run:
|
||||||
|
name: Linting
|
||||||
|
command: pre-commit run --all-files
|
||||||
|
- run:
|
||||||
|
name: Check docstring coverage
|
||||||
|
command: |
|
||||||
|
pip install interrogate
|
||||||
|
interrogate -v --ignore-init-method --ignore-module --ignore-nested-functions --ignore-regex "__repr__" --fail-under 50 mmseg
|
||||||
|
|
||||||
|
build_cpu:
|
||||||
|
parameters:
|
||||||
|
# The python version must match available image tags in
|
||||||
|
# https://circleci.com/developer/images/image/cimg/python
|
||||||
|
python:
|
||||||
|
type: string
|
||||||
|
default: "3.7.4"
|
||||||
|
torch:
|
||||||
|
type: string
|
||||||
|
torchvision:
|
||||||
|
type: string
|
||||||
|
docker:
|
||||||
|
- image: cimg/python:<< parameters.python >>
|
||||||
|
resource_class: large
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install Libraries
|
||||||
|
command: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx libjpeg-dev zlib1g-dev libtinfo-dev libncurses5
|
||||||
|
- run:
|
||||||
|
name: Configure Python & pip
|
||||||
|
command: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install wheel
|
||||||
|
- run:
|
||||||
|
name: Install PyTorch
|
||||||
|
command: |
|
||||||
|
python -V
|
||||||
|
python -m pip install torch==<< parameters.torch >>+cpu torchvision==<< parameters.torchvision >>+cpu -f https://download.pytorch.org/whl/torch_stable.html
|
||||||
|
- run:
|
||||||
|
name: Install mmseg dependencies
|
||||||
|
command: |
|
||||||
|
python -m pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cpu/torch<< parameters.torch >>/index.html
|
||||||
|
python -m pip install mmdet
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
- run:
|
||||||
|
name: Build and install
|
||||||
|
command: |
|
||||||
|
python -m pip install -e .
|
||||||
|
- run:
|
||||||
|
name: Run unittests
|
||||||
|
command: |
|
||||||
|
python -m pip install timm
|
||||||
|
python -m coverage run --branch --source mmseg -m pytest tests/
|
||||||
|
python -m coverage xml
|
||||||
|
python -m coverage report -m
|
||||||
|
|
||||||
|
build_cu101:
|
||||||
|
machine:
|
||||||
|
image: ubuntu-1604-cuda-10.1:201909-23
|
||||||
|
resource_class: gpu.nvidia.small
|
||||||
|
steps:
|
||||||
|
- checkout
|
||||||
|
- run:
|
||||||
|
name: Install Libraries
|
||||||
|
command: |
|
||||||
|
sudo apt-get update
|
||||||
|
sudo apt-get install -y git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 libgl1-mesa-glx
|
||||||
|
- run:
|
||||||
|
name: Configure Python & pip
|
||||||
|
command: |
|
||||||
|
pyenv global 3.7.0
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
python -m pip install wheel
|
||||||
|
- run:
|
||||||
|
name: Install PyTorch
|
||||||
|
command: |
|
||||||
|
python -V
|
||||||
|
python -m pip install torch==1.6.0+cu101 torchvision==0.7.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html
|
||||||
|
- run:
|
||||||
|
name: Install mmseg dependencies
|
||||||
|
# python -m pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu101/torch${{matrix.torch_version}}/index.html
|
||||||
|
command: |
|
||||||
|
python -m pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu101/torch1.6.0/index.html
|
||||||
|
python -m pip install mmdet
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
- run:
|
||||||
|
name: Build and install
|
||||||
|
command: |
|
||||||
|
python setup.py check -m -s
|
||||||
|
TORCH_CUDA_ARCH_LIST=7.0 python -m pip install -e .
|
||||||
|
- run:
|
||||||
|
name: Run unittests
|
||||||
|
command: |
|
||||||
|
python -m pip install timm
|
||||||
|
python -m pytest tests/
|
||||||
|
|
||||||
|
workflows:
|
||||||
|
unit_tests:
|
||||||
|
jobs:
|
||||||
|
- lint
|
||||||
|
- build_cpu:
|
||||||
|
name: build_cpu_th1.6
|
||||||
|
torch: 1.6.0
|
||||||
|
torchvision: 0.7.0
|
||||||
|
requires:
|
||||||
|
- lint
|
||||||
|
- build_cpu:
|
||||||
|
name: build_cpu_th1.7
|
||||||
|
torch: 1.7.0
|
||||||
|
torchvision: 0.8.1
|
||||||
|
requires:
|
||||||
|
- lint
|
||||||
|
- build_cpu:
|
||||||
|
name: build_cpu_th1.8_py3.9
|
||||||
|
torch: 1.8.0
|
||||||
|
torchvision: 0.9.0
|
||||||
|
python: "3.9.0"
|
||||||
|
requires:
|
||||||
|
- lint
|
||||||
|
- build_cpu:
|
||||||
|
name: build_cpu_th1.9_py3.8
|
||||||
|
torch: 1.9.0
|
||||||
|
torchvision: 0.10.0
|
||||||
|
python: "3.8.0"
|
||||||
|
requires:
|
||||||
|
- lint
|
||||||
|
- build_cpu:
|
||||||
|
name: build_cpu_th1.9_py3.9
|
||||||
|
torch: 1.9.0
|
||||||
|
torchvision: 0.10.0
|
||||||
|
python: "3.9.0"
|
||||||
|
requires:
|
||||||
|
- lint
|
||||||
|
- build_cu101:
|
||||||
|
requires:
|
||||||
|
- build_cpu_th1.6
|
||||||
|
- build_cpu_th1.7
|
||||||
|
- build_cpu_th1.8_py3.9
|
||||||
|
- build_cpu_th1.9_py3.8
|
||||||
|
- build_cpu_th1.9_py3.9
|
||||||
@ -0,0 +1,133 @@
|
|||||||
|
# yapf: disable
|
||||||
|
# Inference Speed is tested on NVIDIA V100
|
||||||
|
hrnet = [
|
||||||
|
dict(
|
||||||
|
config='configs/hrnet/fcn_hr18s_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='fcn_hr18s_512x512_160k_ade20k_20200614_214413-870f65ac.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=33.0),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/hrnet/fcn_hr18s_512x1024_160k_cityscapes.py',
|
||||||
|
checkpoint='fcn_hr18s_512x1024_160k_cityscapes_20200602_190901-4a0797ea.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=76.31),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/hrnet/fcn_hr48_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='fcn_hr48_512x512_160k_ade20k_20200614_214407-a52fc02c.pth',
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=42.02),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/hrnet/fcn_hr48_512x1024_160k_cityscapes.py',
|
||||||
|
checkpoint='fcn_hr48_512x1024_160k_cityscapes_20200602_190946-59b7973e.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=80.65),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
pspnet = [
|
||||||
|
dict(
|
||||||
|
config='configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py',
|
||||||
|
checkpoint='pspnet_r50-d8_512x1024_80k_cityscapes_20200606_112131-2376f12b.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=78.55),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py',
|
||||||
|
checkpoint='pspnet_r101-d8_512x1024_80k_cityscapes_20200606_112211-e1e1100f.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=79.76),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='pspnet_r101-d8_512x512_160k_ade20k_20200615_100650-967c316f.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=44.39),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='pspnet_r50-d8_512x512_160k_ade20k_20200615_184358-1890b0bd.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=42.48),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
resnest = [
|
||||||
|
dict(
|
||||||
|
config='configs/resnest/pspnet_s101-d8_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='pspnet_s101-d8_512x512_160k_ade20k_20200807_145416-a6daa92a.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=45.44),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes.py',
|
||||||
|
checkpoint='pspnet_s101-d8_512x1024_80k_cityscapes_20200807_140631-c75f3b99.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=78.57),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
fastscnn = [
|
||||||
|
dict(
|
||||||
|
config='configs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes.py',
|
||||||
|
checkpoint='fast_scnn_8x4_160k_lr0.12_cityscapes-0cec9937.pth',
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=70.96),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
deeplabv3plus = [
|
||||||
|
dict(
|
||||||
|
config='configs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes.py', # noqa
|
||||||
|
checkpoint='deeplabv3plus_r101-d8_769x769_80k_cityscapes_20200607_000405-a7573d20.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=80.98),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes.py', # noqa
|
||||||
|
checkpoint='deeplabv3plus_r101-d8_512x1024_80k_cityscapes_20200606_114143-068fcfe9.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=80.97),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes.py', # noqa
|
||||||
|
checkpoint='deeplabv3plus_r50-d8_512x1024_80k_cityscapes_20200606_114049-f9fb496d.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=80.09),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes.py', # noqa
|
||||||
|
checkpoint='deeplabv3plus_r50-d8_769x769_80k_cityscapes_20200606_210233-0e9dfdc4.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=79.83),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
vit = [
|
||||||
|
dict(
|
||||||
|
config='configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='upernet_vit-b16_ln_mln_512x512_160k_ade20k-f444c077.pth',
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=47.73),
|
||||||
|
),
|
||||||
|
dict(
|
||||||
|
config='configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py',
|
||||||
|
checkpoint='upernet_deit-s16_ln_mln_512x512_160k_ade20k-c0cd652f.pth',
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=43.52),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
fp16 = [
|
||||||
|
dict(
|
||||||
|
config='configs/deeplabv3plus/deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes.py', # noqa
|
||||||
|
checkpoint='deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes_20200717_230920-f1104f4b.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=80.46),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
swin = [
|
||||||
|
dict(
|
||||||
|
config='configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py', # noqa
|
||||||
|
checkpoint='upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K_20210531_112542-e380ad3e.pth', # noqa
|
||||||
|
eval='mIoU',
|
||||||
|
metric=dict(mIoU=44.41),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
# yapf: enable
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
configs/hrnet/fcn_hr18s_512x512_160k_ade20k.py
|
||||||
|
configs/hrnet/fcn_hr18s_512x1024_160k_cityscapes.py
|
||||||
|
configs/hrnet/fcn_hr48_512x512_160k_ade20k.py
|
||||||
|
configs/hrnet/fcn_hr48_512x1024_160k_cityscapes.py
|
||||||
|
configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py
|
||||||
|
configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py
|
||||||
|
configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py
|
||||||
|
configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py
|
||||||
|
configs/resnest/pspnet_s101-d8_512x512_160k_ade20k.py
|
||||||
|
configs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes.py
|
||||||
|
configs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes.py
|
||||||
|
configs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes.py
|
||||||
|
configs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes.py
|
||||||
|
configs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes.py
|
||||||
|
configs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes.py
|
||||||
|
configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py
|
||||||
|
configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py
|
||||||
|
configs/deeplabv3plus/deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes.py
|
||||||
|
configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
PARTITION=$1
|
||||||
|
CHECKPOINT_DIR=$2
|
||||||
|
|
||||||
|
echo 'configs/hrnet/fcn_hr18s_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION fcn_hr18s_512x512_160k_ade20k configs/hrnet/fcn_hr18s_512x512_160k_ade20k.py $CHECKPOINT_DIR/fcn_hr18s_512x512_160k_ade20k_20200614_214413-870f65ac.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/fcn_hr18s_512x512_160k_ade20k --cfg-options dist_params.port=28171 &
|
||||||
|
echo 'configs/hrnet/fcn_hr18s_512x1024_160k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION fcn_hr18s_512x1024_160k_cityscapes configs/hrnet/fcn_hr18s_512x1024_160k_cityscapes.py $CHECKPOINT_DIR/fcn_hr18s_512x1024_160k_cityscapes_20200602_190901-4a0797ea.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/fcn_hr18s_512x1024_160k_cityscapes --cfg-options dist_params.port=28172 &
|
||||||
|
echo 'configs/hrnet/fcn_hr48_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION fcn_hr48_512x512_160k_ade20k configs/hrnet/fcn_hr48_512x512_160k_ade20k.py $CHECKPOINT_DIR/fcn_hr48_512x512_160k_ade20k_20200614_214407-a52fc02c.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/fcn_hr48_512x512_160k_ade20k --cfg-options dist_params.port=28173 &
|
||||||
|
echo 'configs/hrnet/fcn_hr48_512x1024_160k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION fcn_hr48_512x1024_160k_cityscapes configs/hrnet/fcn_hr48_512x1024_160k_cityscapes.py $CHECKPOINT_DIR/fcn_hr48_512x1024_160k_cityscapes_20200602_190946-59b7973e.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/fcn_hr48_512x1024_160k_cityscapes --cfg-options dist_params.port=28174 &
|
||||||
|
echo 'configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION pspnet_r50-d8_512x1024_80k_cityscapes configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py $CHECKPOINT_DIR/pspnet_r50-d8_512x1024_80k_cityscapes_20200606_112131-2376f12b.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/pspnet_r50-d8_512x1024_80k_cityscapes --cfg-options dist_params.port=28175 &
|
||||||
|
echo 'configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION pspnet_r101-d8_512x1024_80k_cityscapes configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py $CHECKPOINT_DIR/pspnet_r101-d8_512x1024_80k_cityscapes_20200606_112211-e1e1100f.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/pspnet_r101-d8_512x1024_80k_cityscapes --cfg-options dist_params.port=28176 &
|
||||||
|
echo 'configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION pspnet_r101-d8_512x512_160k_ade20k configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py $CHECKPOINT_DIR/pspnet_r101-d8_512x512_160k_ade20k_20200615_100650-967c316f.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/pspnet_r101-d8_512x512_160k_ade20k --cfg-options dist_params.port=28177 &
|
||||||
|
echo 'configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION pspnet_r50-d8_512x512_160k_ade20k configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py $CHECKPOINT_DIR/pspnet_r50-d8_512x512_160k_ade20k_20200615_184358-1890b0bd.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/pspnet_r50-d8_512x512_160k_ade20k --cfg-options dist_params.port=28178 &
|
||||||
|
echo 'configs/resnest/pspnet_s101-d8_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION pspnet_s101-d8_512x512_160k_ade20k configs/resnest/pspnet_s101-d8_512x512_160k_ade20k.py $CHECKPOINT_DIR/pspnet_s101-d8_512x512_160k_ade20k_20200807_145416-a6daa92a.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/pspnet_s101-d8_512x512_160k_ade20k --cfg-options dist_params.port=28179 &
|
||||||
|
echo 'configs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION pspnet_s101-d8_512x1024_80k_cityscapes configs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes.py $CHECKPOINT_DIR/pspnet_s101-d8_512x1024_80k_cityscapes_20200807_140631-c75f3b99.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/pspnet_s101-d8_512x1024_80k_cityscapes --cfg-options dist_params.port=28180 &
|
||||||
|
echo 'configs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION fast_scnn_lr0.12_8x4_160k_cityscapes configs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes.py $CHECKPOINT_DIR/fast_scnn_8x4_160k_lr0.12_cityscapes-0cec9937.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/fast_scnn_lr0.12_8x4_160k_cityscapes --cfg-options dist_params.port=28181 &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION deeplabv3plus_r101-d8_769x769_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes.py $CHECKPOINT_DIR/deeplabv3plus_r101-d8_769x769_80k_cityscapes_20200607_000405-a7573d20.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/deeplabv3plus_r101-d8_769x769_80k_cityscapes --cfg-options dist_params.port=28182 &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION deeplabv3plus_r101-d8_512x1024_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes.py $CHECKPOINT_DIR/deeplabv3plus_r101-d8_512x1024_80k_cityscapes_20200606_114143-068fcfe9.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/deeplabv3plus_r101-d8_512x1024_80k_cityscapes --cfg-options dist_params.port=28183 &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION deeplabv3plus_r50-d8_512x1024_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes.py $CHECKPOINT_DIR/deeplabv3plus_r50-d8_512x1024_80k_cityscapes_20200606_114049-f9fb496d.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/deeplabv3plus_r50-d8_512x1024_80k_cityscapes --cfg-options dist_params.port=28184 &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION deeplabv3plus_r50-d8_769x769_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes.py $CHECKPOINT_DIR/deeplabv3plus_r50-d8_769x769_80k_cityscapes_20200606_210233-0e9dfdc4.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/deeplabv3plus_r50-d8_769x769_80k_cityscapes --cfg-options dist_params.port=28185 &
|
||||||
|
echo 'configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION upernet_vit-b16_ln_mln_512x512_160k_ade20k configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py $CHECKPOINT_DIR/upernet_vit-b16_ln_mln_512x512_160k_ade20k-f444c077.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/upernet_vit-b16_ln_mln_512x512_160k_ade20k --cfg-options dist_params.port=28186 &
|
||||||
|
echo 'configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION upernet_deit-s16_ln_mln_512x512_160k_ade20k configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py $CHECKPOINT_DIR/upernet_deit-s16_ln_mln_512x512_160k_ade20k-c0cd652f.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/upernet_deit-s16_ln_mln_512x512_160k_ade20k --cfg-options dist_params.port=28187 &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes.py $CHECKPOINT_DIR/deeplabv3plus_r101-d8_512x1024_80k_fp16_cityscapes-cc58bc8d.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/deeplabv3plus_r101-d8_512x1024_80k_fp16_cityscapes --cfg-options dist_params.port=28188 &
|
||||||
|
echo 'configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 tools/slurm_test.sh $PARTITION upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py $CHECKPOINT_DIR/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K_20210531_112542-e380ad3e.pth --eval mIoU --work-dir work_dirs/benchmark_evaluation/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K --cfg-options dist_params.port=28189 &
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import hashlib
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
import warnings
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from mmcv import Config
|
||||||
|
|
||||||
|
from mmseg.apis import inference_segmentor, init_segmentor, show_result_pyplot
|
||||||
|
from mmseg.utils import get_root_logger
|
||||||
|
|
||||||
|
# ignore warnings when segmentors inference
|
||||||
|
warnings.filterwarnings('ignore')
|
||||||
|
|
||||||
|
|
||||||
|
def download_checkpoint(checkpoint_name, model_name, config_name, collect_dir):
|
||||||
|
"""Download checkpoint and check if hash code is true."""
|
||||||
|
url = f'https://download.openmmlab.com/mmsegmentation/v0.5/{model_name}/{config_name}/{checkpoint_name}' # noqa
|
||||||
|
|
||||||
|
r = requests.get(url)
|
||||||
|
assert r.status_code != 403, f'{url} Access denied.'
|
||||||
|
|
||||||
|
with open(osp.join(collect_dir, checkpoint_name), 'wb') as code:
|
||||||
|
code.write(r.content)
|
||||||
|
|
||||||
|
true_hash_code = osp.splitext(checkpoint_name)[0].split('-')[1]
|
||||||
|
|
||||||
|
# check hash code
|
||||||
|
with open(osp.join(collect_dir, checkpoint_name), 'rb') as fp:
|
||||||
|
sha256_cal = hashlib.sha256()
|
||||||
|
sha256_cal.update(fp.read())
|
||||||
|
cur_hash_code = sha256_cal.hexdigest()[:8]
|
||||||
|
|
||||||
|
assert true_hash_code == cur_hash_code, f'{url} download failed, '
|
||||||
|
'incomplete downloaded file or url invalid.'
|
||||||
|
|
||||||
|
if cur_hash_code != true_hash_code:
|
||||||
|
os.remove(osp.join(collect_dir, checkpoint_name))
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = ArgumentParser()
|
||||||
|
parser.add_argument('config', help='test config file path')
|
||||||
|
parser.add_argument('checkpoint_root', help='Checkpoint file root path')
|
||||||
|
parser.add_argument(
|
||||||
|
'-i', '--img', default='demo/demo.png', help='Image file')
|
||||||
|
parser.add_argument('-a', '--aug', action='store_true', help='aug test')
|
||||||
|
parser.add_argument('-m', '--model-name', help='model name to inference')
|
||||||
|
parser.add_argument(
|
||||||
|
'-s', '--show', action='store_true', help='show results')
|
||||||
|
parser.add_argument(
|
||||||
|
'-d', '--device', default='cuda:0', help='Device used for inference')
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def inference_model(config_name, checkpoint, args, logger=None):
|
||||||
|
cfg = Config.fromfile(config_name)
|
||||||
|
if args.aug:
|
||||||
|
if 'flip' in cfg.data.test.pipeline[
|
||||||
|
1] and 'img_scale' in cfg.data.test.pipeline[1]:
|
||||||
|
cfg.data.test.pipeline[1].img_ratios = [
|
||||||
|
0.5, 0.75, 1.0, 1.25, 1.5, 1.75
|
||||||
|
]
|
||||||
|
cfg.data.test.pipeline[1].flip = True
|
||||||
|
else:
|
||||||
|
if logger is not None:
|
||||||
|
logger.error(f'{config_name}: unable to start aug test')
|
||||||
|
else:
|
||||||
|
print(f'{config_name}: unable to start aug test', flush=True)
|
||||||
|
|
||||||
|
model = init_segmentor(cfg, checkpoint, device=args.device)
|
||||||
|
# test a single image
|
||||||
|
result = inference_segmentor(model, args.img)
|
||||||
|
|
||||||
|
# show the results
|
||||||
|
if args.show:
|
||||||
|
show_result_pyplot(model, args.img, result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
# Sample test whether the inference code is correct
|
||||||
|
def main(args):
|
||||||
|
config = Config.fromfile(args.config)
|
||||||
|
|
||||||
|
if not os.path.exists(args.checkpoint_root):
|
||||||
|
os.makedirs(args.checkpoint_root, 0o775)
|
||||||
|
|
||||||
|
# test single model
|
||||||
|
if args.model_name:
|
||||||
|
if args.model_name in config:
|
||||||
|
model_infos = config[args.model_name]
|
||||||
|
if not isinstance(model_infos, list):
|
||||||
|
model_infos = [model_infos]
|
||||||
|
for model_info in model_infos:
|
||||||
|
config_name = model_info['config'].strip()
|
||||||
|
print(f'processing: {config_name}', flush=True)
|
||||||
|
checkpoint = osp.join(args.checkpoint_root,
|
||||||
|
model_info['checkpoint'].strip())
|
||||||
|
try:
|
||||||
|
# build the model from a config file and a checkpoint file
|
||||||
|
inference_model(config_name, checkpoint, args)
|
||||||
|
except Exception:
|
||||||
|
print(f'{config_name} test failed!')
|
||||||
|
continue
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise RuntimeError('model name input error.')
|
||||||
|
|
||||||
|
# test all model
|
||||||
|
logger = get_root_logger(
|
||||||
|
log_file='benchmark_inference_image.log', log_level=logging.ERROR)
|
||||||
|
|
||||||
|
for model_name in config:
|
||||||
|
model_infos = config[model_name]
|
||||||
|
|
||||||
|
if not isinstance(model_infos, list):
|
||||||
|
model_infos = [model_infos]
|
||||||
|
for model_info in model_infos:
|
||||||
|
print('processing: ', model_info['config'], flush=True)
|
||||||
|
config_path = model_info['config'].strip()
|
||||||
|
config_name = osp.splitext(osp.basename(config_path))[0]
|
||||||
|
checkpoint_name = model_info['checkpoint'].strip()
|
||||||
|
checkpoint = osp.join(args.checkpoint_root, checkpoint_name)
|
||||||
|
|
||||||
|
# ensure checkpoint exists
|
||||||
|
try:
|
||||||
|
if not osp.exists(checkpoint):
|
||||||
|
download_checkpoint(checkpoint_name, model_name,
|
||||||
|
config_name.rstrip('.py'),
|
||||||
|
args.checkpoint_root)
|
||||||
|
except Exception:
|
||||||
|
logger.error(f'{checkpoint_name} download error')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# test model inference with checkpoint
|
||||||
|
try:
|
||||||
|
# build the model from a config file and a checkpoint file
|
||||||
|
inference_model(config_path, checkpoint, args, logger)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f'{config_path} " : {repr(e)}')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse_args()
|
||||||
|
main(args)
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
PARTITION=$1
|
||||||
|
|
||||||
|
echo 'configs/hrnet/fcn_hr18s_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION fcn_hr18s_512x512_160k_ade20k configs/hrnet/fcn_hr18s_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24727 --work-dir work_dirs/hrnet/fcn_hr18s_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/hrnet/fcn_hr18s_512x1024_160k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION fcn_hr18s_512x1024_160k_cityscapes configs/hrnet/fcn_hr18s_512x1024_160k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24728 --work-dir work_dirs/hrnet/fcn_hr18s_512x1024_160k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/hrnet/fcn_hr48_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION fcn_hr48_512x512_160k_ade20k configs/hrnet/fcn_hr48_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24729 --work-dir work_dirs/hrnet/fcn_hr48_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/hrnet/fcn_hr48_512x1024_160k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION fcn_hr48_512x1024_160k_cityscapes configs/hrnet/fcn_hr48_512x1024_160k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24730 --work-dir work_dirs/hrnet/fcn_hr48_512x1024_160k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION pspnet_r50-d8_512x1024_80k_cityscapes configs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24731 --work-dir work_dirs/pspnet/pspnet_r50-d8_512x1024_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION pspnet_r101-d8_512x1024_80k_cityscapes configs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24732 --work-dir work_dirs/pspnet/pspnet_r101-d8_512x1024_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION pspnet_r101-d8_512x512_160k_ade20k configs/pspnet/pspnet_r101-d8_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24733 --work-dir work_dirs/pspnet/pspnet_r101-d8_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION pspnet_r50-d8_512x512_160k_ade20k configs/pspnet/pspnet_r50-d8_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24734 --work-dir work_dirs/pspnet/pspnet_r50-d8_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/resnest/pspnet_s101-d8_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION pspnet_s101-d8_512x512_160k_ade20k configs/resnest/pspnet_s101-d8_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24735 --work-dir work_dirs/resnest/pspnet_s101-d8_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION pspnet_s101-d8_512x1024_80k_cityscapes configs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24736 --work-dir work_dirs/resnest/pspnet_s101-d8_512x1024_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION fast_scnn_lr0.12_8x4_160k_cityscapes configs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24737 --work-dir work_dirs/fastscnn/fast_scnn_lr0.12_8x4_160k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION deeplabv3plus_r101-d8_769x769_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24738 --work-dir work_dirs/deeplabv3plus/deeplabv3plus_r101-d8_769x769_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION deeplabv3plus_r101-d8_512x1024_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24739 --work-dir work_dirs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION deeplabv3plus_r50-d8_512x1024_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24740 --work-dir work_dirs/deeplabv3plus/deeplabv3plus_r50-d8_512x1024_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION deeplabv3plus_r50-d8_769x769_80k_cityscapes configs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24741 --work-dir work_dirs/deeplabv3plus/deeplabv3plus_r50-d8_769x769_80k_cityscapes >/dev/null &
|
||||||
|
echo 'configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=8 GPUS_PER_NODE=8 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION upernet_vit-b16_ln_mln_512x512_160k_ade20k configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24742 --work-dir work_dirs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py' &
|
||||||
|
GPUS=8 GPUS_PER_NODE=8 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION upernet_deit-s16_ln_mln_512x512_160k_ade20k configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24743 --work-dir work_dirs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k >/dev/null &
|
||||||
|
echo 'configs/deeplabv3plus/deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes.py' &
|
||||||
|
GPUS=4 GPUS_PER_NODE=4 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION deeplabv3plus_r101-d8_512x1024_80k_fp16_cityscapes configs/deeplabv3plus/deeplabv3plus_r101-d8_fp16_512x1024_80k_cityscapes.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24744 --work-dir work_dirs/deeplabv3plus/deeplabv3plus_r101-d8_512x1024_80k_fp16_cityscapes >/dev/null &
|
||||||
|
echo 'configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py' &
|
||||||
|
GPUS=8 GPUS_PER_NODE=8 CPUS_PER_TASK=2 ./tools/slurm_train.sh $PARTITION upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py --cfg-options checkpoint_config.max_keep_ckpts=1 dist_params.port=24745 --work-dir work_dirs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K >/dev/null &
|
||||||
@ -0,0 +1,101 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import yaml as yml
|
||||||
|
|
||||||
|
from mmseg.utils import get_root_logger
|
||||||
|
|
||||||
|
|
||||||
|
def check_url(url):
|
||||||
|
"""Check url response status.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url (str): url needed to check.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
int, bool: status code and check flag.
|
||||||
|
"""
|
||||||
|
flag = True
|
||||||
|
r = requests.head(url)
|
||||||
|
status_code = r.status_code
|
||||||
|
if status_code == 403 or status_code == 404:
|
||||||
|
flag = False
|
||||||
|
|
||||||
|
return status_code, flag
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = ArgumentParser('url valid check.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-m',
|
||||||
|
'--model-name',
|
||||||
|
type=str,
|
||||||
|
help='Select the model needed to check')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
model_name = args.model_name
|
||||||
|
|
||||||
|
# yml path generate.
|
||||||
|
# If model_name is not set, script will check all of the models.
|
||||||
|
if model_name is not None:
|
||||||
|
yml_list = [(model_name, f'configs/{model_name}/{model_name}.yml')]
|
||||||
|
else:
|
||||||
|
# check all
|
||||||
|
yml_list = [(x, f'configs/{x}/{x}.yml') for x in os.listdir('configs/')
|
||||||
|
if x != '_base_']
|
||||||
|
|
||||||
|
logger = get_root_logger(log_file='url_check.log', log_level=logging.ERROR)
|
||||||
|
|
||||||
|
for model_name, yml_path in yml_list:
|
||||||
|
# Default yaml loader unsafe.
|
||||||
|
model_infos = yml.load(
|
||||||
|
open(yml_path, 'r'), Loader=yml.CLoader)['Models']
|
||||||
|
for model_info in model_infos:
|
||||||
|
config_name = model_info['Name']
|
||||||
|
checkpoint_url = model_info['Weights']
|
||||||
|
# checkpoint url check
|
||||||
|
status_code, flag = check_url(checkpoint_url)
|
||||||
|
if flag:
|
||||||
|
logger.info(f'checkpoint | {config_name} | {checkpoint_url} | '
|
||||||
|
f'{status_code} valid')
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'checkpoint | {config_name} | {checkpoint_url} | '
|
||||||
|
f'{status_code} | error')
|
||||||
|
# log_json check
|
||||||
|
checkpoint_name = checkpoint_url.split('/')[-1]
|
||||||
|
model_time = '-'.join(checkpoint_name.split('-')[:-1]).replace(
|
||||||
|
f'{config_name}_', '')
|
||||||
|
# two style of log_json name
|
||||||
|
# use '_' to link model_time (will be deprecated)
|
||||||
|
log_json_url_1 = f'https://download.openmmlab.com/mmsegmentation/v0.5/{model_name}/{config_name}/{config_name}_{model_time}.log.json' # noqa
|
||||||
|
status_code_1, flag_1 = check_url(log_json_url_1)
|
||||||
|
# use '-' to link model_time
|
||||||
|
log_json_url_2 = f'https://download.openmmlab.com/mmsegmentation/v0.5/{model_name}/{config_name}/{config_name}-{model_time}.log.json' # noqa
|
||||||
|
status_code_2, flag_2 = check_url(log_json_url_2)
|
||||||
|
if flag_1 or flag_2:
|
||||||
|
if flag_1:
|
||||||
|
logger.info(
|
||||||
|
f'log.json | {config_name} | {log_json_url_1} | '
|
||||||
|
f'{status_code_1} | valid')
|
||||||
|
else:
|
||||||
|
logger.info(
|
||||||
|
f'log.json | {config_name} | {log_json_url_2} | '
|
||||||
|
f'{status_code_2} | valid')
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
f'log.json | {config_name} | {log_json_url_1} & '
|
||||||
|
f'{log_json_url_2} | {status_code_1} & {status_code_2} | '
|
||||||
|
'error')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import os.path as osp
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
from mmcv import Config
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Gather benchmarked model evaluation results')
|
||||||
|
parser.add_argument('config', help='test config file path')
|
||||||
|
parser.add_argument(
|
||||||
|
'root',
|
||||||
|
type=str,
|
||||||
|
help='root path of benchmarked models to be gathered')
|
||||||
|
parser.add_argument(
|
||||||
|
'--out',
|
||||||
|
type=str,
|
||||||
|
default='benchmark_evaluation_info.json',
|
||||||
|
help='output path of gathered metrics and compared '
|
||||||
|
'results to be stored')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
root_path = args.root
|
||||||
|
metrics_out = args.out
|
||||||
|
result_dict = {}
|
||||||
|
|
||||||
|
cfg = Config.fromfile(args.config)
|
||||||
|
|
||||||
|
for model_key in cfg:
|
||||||
|
model_infos = cfg[model_key]
|
||||||
|
if not isinstance(model_infos, list):
|
||||||
|
model_infos = [model_infos]
|
||||||
|
for model_info in model_infos:
|
||||||
|
previous_metrics = model_info['metric']
|
||||||
|
config = model_info['config'].strip()
|
||||||
|
fname, _ = osp.splitext(osp.basename(config))
|
||||||
|
|
||||||
|
# Load benchmark evaluation json
|
||||||
|
metric_json_dir = osp.join(root_path, fname)
|
||||||
|
if not osp.exists(metric_json_dir):
|
||||||
|
print(f'{metric_json_dir} not existed.')
|
||||||
|
continue
|
||||||
|
|
||||||
|
json_list = glob.glob(osp.join(metric_json_dir, '*.json'))
|
||||||
|
if len(json_list) == 0:
|
||||||
|
print(f'There is no eval json in {metric_json_dir}.')
|
||||||
|
continue
|
||||||
|
|
||||||
|
log_json_path = list(sorted(json_list))[-1]
|
||||||
|
metric = mmcv.load(log_json_path)
|
||||||
|
if config not in metric.get('config', {}):
|
||||||
|
print(f'{config} not included in {log_json_path}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Compare between new benchmark results and previous metrics
|
||||||
|
differential_results = dict()
|
||||||
|
new_metrics = dict()
|
||||||
|
for record_metric_key in previous_metrics:
|
||||||
|
if record_metric_key not in metric['metric']:
|
||||||
|
raise KeyError('record_metric_key not exist, please '
|
||||||
|
'check your config')
|
||||||
|
old_metric = previous_metrics[record_metric_key]
|
||||||
|
new_metric = round(metric['metric'][record_metric_key] * 100,
|
||||||
|
2)
|
||||||
|
|
||||||
|
differential = new_metric - old_metric
|
||||||
|
flag = '+' if differential > 0 else '-'
|
||||||
|
differential_results[
|
||||||
|
record_metric_key] = f'{flag}{abs(differential):.2f}'
|
||||||
|
new_metrics[record_metric_key] = new_metric
|
||||||
|
|
||||||
|
result_dict[config] = dict(
|
||||||
|
differential=differential_results,
|
||||||
|
previous=previous_metrics,
|
||||||
|
new=new_metrics)
|
||||||
|
|
||||||
|
if metrics_out:
|
||||||
|
mmcv.dump(result_dict, metrics_out, indent=4)
|
||||||
|
print('===================================')
|
||||||
|
for config_name, metrics in result_dict.items():
|
||||||
|
print(config_name, metrics)
|
||||||
|
print('===================================')
|
||||||
@ -0,0 +1,100 @@
|
|||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import os.path as osp
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
from gather_models import get_final_results
|
||||||
|
from mmcv import Config
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Gather benchmarked models train results')
|
||||||
|
parser.add_argument('config', help='test config file path')
|
||||||
|
parser.add_argument(
|
||||||
|
'root',
|
||||||
|
type=str,
|
||||||
|
help='root path of benchmarked models to be gathered')
|
||||||
|
parser.add_argument(
|
||||||
|
'--out',
|
||||||
|
type=str,
|
||||||
|
default='benchmark_train_info.json',
|
||||||
|
help='output path of gathered metrics to be stored')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
args = parse_args()
|
||||||
|
|
||||||
|
root_path = args.root
|
||||||
|
metrics_out = args.out
|
||||||
|
|
||||||
|
evaluation_cfg = Config.fromfile(args.config)
|
||||||
|
|
||||||
|
result_dict = {}
|
||||||
|
for model_key in evaluation_cfg:
|
||||||
|
model_infos = evaluation_cfg[model_key]
|
||||||
|
if not isinstance(model_infos, list):
|
||||||
|
model_infos = [model_infos]
|
||||||
|
for model_info in model_infos:
|
||||||
|
config = model_info['config']
|
||||||
|
|
||||||
|
# benchmark train dir
|
||||||
|
model_name = osp.split(osp.dirname(config))[1]
|
||||||
|
config_name = osp.splitext(osp.basename(config))[0]
|
||||||
|
exp_dir = osp.join(root_path, model_name, config_name)
|
||||||
|
if not osp.exists(exp_dir):
|
||||||
|
print(f'{config} hasn\'t {exp_dir}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# parse config
|
||||||
|
cfg = mmcv.Config.fromfile(config)
|
||||||
|
total_iters = cfg.runner.max_iters
|
||||||
|
exp_metric = cfg.evaluation.metric
|
||||||
|
if not isinstance(exp_metric, list):
|
||||||
|
exp_metrics = [exp_metric]
|
||||||
|
|
||||||
|
# determine whether total_iters ckpt exists
|
||||||
|
ckpt_path = f'iter_{total_iters}.pth'
|
||||||
|
if not osp.exists(osp.join(exp_dir, ckpt_path)):
|
||||||
|
print(f'{config} hasn\'t {ckpt_path}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# only the last log json counts
|
||||||
|
log_json_path = list(
|
||||||
|
sorted(glob.glob(osp.join(exp_dir, '*.log.json'))))[-1]
|
||||||
|
|
||||||
|
# extract metric value
|
||||||
|
model_performance = get_final_results(log_json_path, total_iters)
|
||||||
|
if model_performance is None:
|
||||||
|
print(f'log file error: {log_json_path}')
|
||||||
|
continue
|
||||||
|
|
||||||
|
differential_results = dict()
|
||||||
|
old_results = dict()
|
||||||
|
new_results = dict()
|
||||||
|
for metric_key in model_performance:
|
||||||
|
if metric_key in ['mIoU']:
|
||||||
|
metric = round(model_performance[metric_key] * 100, 2)
|
||||||
|
old_metric = model_info['metric'][metric_key]
|
||||||
|
old_results[metric_key] = old_metric
|
||||||
|
new_results[metric_key] = metric
|
||||||
|
differential = metric - old_metric
|
||||||
|
flag = '+' if differential > 0 else '-'
|
||||||
|
differential_results[
|
||||||
|
metric_key] = f'{flag}{abs(differential):.2f}'
|
||||||
|
result_dict[config] = dict(
|
||||||
|
differential_results=differential_results,
|
||||||
|
old_results=old_results,
|
||||||
|
new_results=new_results,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 4 save or print results
|
||||||
|
if metrics_out:
|
||||||
|
mmcv.dump(result_dict, metrics_out, indent=4)
|
||||||
|
print('===================================')
|
||||||
|
for config_name, metrics in result_dict.items():
|
||||||
|
print(config_name, metrics)
|
||||||
|
print('===================================')
|
||||||
@ -0,0 +1,211 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import argparse
|
||||||
|
import glob
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import mmcv
|
||||||
|
import torch
|
||||||
|
|
||||||
|
# build schedule look-up table to automatically find the final model
|
||||||
|
RESULTS_LUT = ['mIoU', 'mAcc', 'aAcc']
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_file_sha256(file_path):
|
||||||
|
"""calculate file sha256 hash code."""
|
||||||
|
with open(file_path, 'rb') as fp:
|
||||||
|
sha256_cal = hashlib.sha256()
|
||||||
|
sha256_cal.update(fp.read())
|
||||||
|
return sha256_cal.hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def process_checkpoint(in_file, out_file):
|
||||||
|
checkpoint = torch.load(in_file, map_location='cpu')
|
||||||
|
# remove optimizer for smaller file size
|
||||||
|
if 'optimizer' in checkpoint:
|
||||||
|
del checkpoint['optimizer']
|
||||||
|
# if it is necessary to remove some sensitive data in checkpoint['meta'],
|
||||||
|
# add the code here.
|
||||||
|
torch.save(checkpoint, out_file)
|
||||||
|
# The hash code calculation and rename command differ on different system
|
||||||
|
# platform.
|
||||||
|
sha = calculate_file_sha256(out_file)
|
||||||
|
final_file = out_file.rstrip('.pth') + '-{}.pth'.format(sha[:8])
|
||||||
|
os.rename(out_file, final_file)
|
||||||
|
|
||||||
|
# Remove prefix and suffix
|
||||||
|
final_file_name = osp.split(final_file)[1]
|
||||||
|
final_file_name = osp.splitext(final_file_name)[0]
|
||||||
|
|
||||||
|
return final_file_name
|
||||||
|
|
||||||
|
|
||||||
|
def get_final_iter(config):
|
||||||
|
iter_num = config.split('_')[-2]
|
||||||
|
assert iter_num.endswith('k')
|
||||||
|
return int(iter_num[:-1]) * 1000
|
||||||
|
|
||||||
|
|
||||||
|
def get_final_results(log_json_path, iter_num):
|
||||||
|
result_dict = dict()
|
||||||
|
last_iter = 0
|
||||||
|
with open(log_json_path, 'r') as f:
|
||||||
|
for line in f.readlines():
|
||||||
|
log_line = json.loads(line)
|
||||||
|
if 'mode' not in log_line.keys():
|
||||||
|
continue
|
||||||
|
|
||||||
|
# When evaluation, the 'iter' of new log json is the evaluation
|
||||||
|
# steps on single gpu.
|
||||||
|
flag1 = ('aAcc' in log_line) or (log_line['mode'] == 'val')
|
||||||
|
flag2 = (last_iter == iter_num - 50) or (last_iter == iter_num)
|
||||||
|
if flag1 and flag2:
|
||||||
|
result_dict.update({
|
||||||
|
key: log_line[key]
|
||||||
|
for key in RESULTS_LUT if key in log_line
|
||||||
|
})
|
||||||
|
return result_dict
|
||||||
|
|
||||||
|
last_iter = log_line['iter']
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description='Gather benchmarked models')
|
||||||
|
parser.add_argument(
|
||||||
|
'-f', '--config-name', type=str, help='Process the selected config.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-w',
|
||||||
|
'--work-dir',
|
||||||
|
default='work_dirs/',
|
||||||
|
type=str,
|
||||||
|
help='Ckpt storage root folder of benchmarked models to be gathered.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-c',
|
||||||
|
'--collect-dir',
|
||||||
|
default='work_dirs/gather',
|
||||||
|
type=str,
|
||||||
|
help='Ckpt collect root folder of gathered models.')
|
||||||
|
parser.add_argument(
|
||||||
|
'--all', action='store_true', help='whether include .py and .log')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
work_dir = args.work_dir
|
||||||
|
collect_dir = args.collect_dir
|
||||||
|
selected_config_name = args.config_name
|
||||||
|
mmcv.mkdir_or_exist(collect_dir)
|
||||||
|
|
||||||
|
# find all models in the root directory to be gathered
|
||||||
|
raw_configs = list(mmcv.scandir('./configs', '.py', recursive=True))
|
||||||
|
|
||||||
|
# filter configs that is not trained in the experiments dir
|
||||||
|
used_configs = []
|
||||||
|
for raw_config in raw_configs:
|
||||||
|
config_name = osp.splitext(osp.basename(raw_config))[0]
|
||||||
|
if osp.exists(osp.join(work_dir, config_name)):
|
||||||
|
if (selected_config_name is None
|
||||||
|
or selected_config_name == config_name):
|
||||||
|
used_configs.append(raw_config)
|
||||||
|
print(f'Find {len(used_configs)} models to be gathered')
|
||||||
|
|
||||||
|
# find final_ckpt and log file for trained each config
|
||||||
|
# and parse the best performance
|
||||||
|
model_infos = []
|
||||||
|
for used_config in used_configs:
|
||||||
|
config_name = osp.splitext(osp.basename(used_config))[0]
|
||||||
|
exp_dir = osp.join(work_dir, config_name)
|
||||||
|
# check whether the exps is finished
|
||||||
|
final_iter = get_final_iter(used_config)
|
||||||
|
final_model = 'iter_{}.pth'.format(final_iter)
|
||||||
|
model_path = osp.join(exp_dir, final_model)
|
||||||
|
|
||||||
|
# skip if the model is still training
|
||||||
|
if not osp.exists(model_path):
|
||||||
|
print(f'{used_config} train not finished yet')
|
||||||
|
continue
|
||||||
|
|
||||||
|
# get logs
|
||||||
|
log_json_paths = glob.glob(osp.join(exp_dir, '*.log.json'))
|
||||||
|
log_json_path = log_json_paths[0]
|
||||||
|
model_performance = None
|
||||||
|
for idx, _log_json_path in enumerate(log_json_paths):
|
||||||
|
model_performance = get_final_results(_log_json_path, final_iter)
|
||||||
|
if model_performance is not None:
|
||||||
|
log_json_path = _log_json_path
|
||||||
|
break
|
||||||
|
|
||||||
|
if model_performance is None:
|
||||||
|
print(f'{used_config} model_performance is None')
|
||||||
|
continue
|
||||||
|
|
||||||
|
model_time = osp.split(log_json_path)[-1].split('.')[0]
|
||||||
|
model_infos.append(
|
||||||
|
dict(
|
||||||
|
config_name=config_name,
|
||||||
|
results=model_performance,
|
||||||
|
iters=final_iter,
|
||||||
|
model_time=model_time,
|
||||||
|
log_json_path=osp.split(log_json_path)[-1]))
|
||||||
|
|
||||||
|
# publish model for each checkpoint
|
||||||
|
publish_model_infos = []
|
||||||
|
for model in model_infos:
|
||||||
|
config_name = model['config_name']
|
||||||
|
model_publish_dir = osp.join(collect_dir, config_name)
|
||||||
|
|
||||||
|
publish_model_path = osp.join(model_publish_dir,
|
||||||
|
config_name + '_' + model['model_time'])
|
||||||
|
trained_model_path = osp.join(work_dir, config_name,
|
||||||
|
'iter_{}.pth'.format(model['iters']))
|
||||||
|
if osp.exists(model_publish_dir):
|
||||||
|
for file in os.listdir(model_publish_dir):
|
||||||
|
if file.endswith('.pth'):
|
||||||
|
print(f'model {file} found')
|
||||||
|
model['model_path'] = osp.abspath(
|
||||||
|
osp.join(model_publish_dir, file))
|
||||||
|
break
|
||||||
|
if 'model_path' not in model:
|
||||||
|
print(f'dir {model_publish_dir} exists, no model found')
|
||||||
|
|
||||||
|
else:
|
||||||
|
mmcv.mkdir_or_exist(model_publish_dir)
|
||||||
|
|
||||||
|
# convert model
|
||||||
|
final_model_path = process_checkpoint(trained_model_path,
|
||||||
|
publish_model_path)
|
||||||
|
model['model_path'] = final_model_path
|
||||||
|
|
||||||
|
new_json_path = f'{config_name}_{model["log_json_path"]}'
|
||||||
|
# copy log
|
||||||
|
shutil.copy(
|
||||||
|
osp.join(work_dir, config_name, model['log_json_path']),
|
||||||
|
osp.join(model_publish_dir, new_json_path))
|
||||||
|
|
||||||
|
if args.all:
|
||||||
|
new_txt_path = new_json_path.rstrip('.json')
|
||||||
|
shutil.copy(
|
||||||
|
osp.join(work_dir, config_name,
|
||||||
|
model['log_json_path'].rstrip('.json')),
|
||||||
|
osp.join(model_publish_dir, new_txt_path))
|
||||||
|
|
||||||
|
if args.all:
|
||||||
|
# copy config to guarantee reproducibility
|
||||||
|
raw_config = osp.join('./configs', f'{config_name}.py')
|
||||||
|
mmcv.Config.fromfile(raw_config).dump(
|
||||||
|
osp.join(model_publish_dir, osp.basename(raw_config)))
|
||||||
|
|
||||||
|
publish_model_infos.append(model)
|
||||||
|
|
||||||
|
models = dict(models=publish_model_infos)
|
||||||
|
mmcv.dump(models, osp.join(collect_dir, 'model_infos.json'), indent=4)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@ -0,0 +1,114 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import argparse
|
||||||
|
import os.path as osp
|
||||||
|
|
||||||
|
from mmcv import Config
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Convert benchmark test model list to script')
|
||||||
|
parser.add_argument('config', help='test config file path')
|
||||||
|
parser.add_argument('--port', type=int, default=28171, help='dist port')
|
||||||
|
parser.add_argument(
|
||||||
|
'--work-dir',
|
||||||
|
default='work_dirs/benchmark_evaluation',
|
||||||
|
help='the dir to save metric')
|
||||||
|
parser.add_argument(
|
||||||
|
'--out',
|
||||||
|
type=str,
|
||||||
|
default='.dev/benchmark_evaluation.sh',
|
||||||
|
help='path to save model benchmark script')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def process_model_info(model_info, work_dir):
|
||||||
|
config = model_info['config'].strip()
|
||||||
|
fname, _ = osp.splitext(osp.basename(config))
|
||||||
|
job_name = fname
|
||||||
|
checkpoint = model_info['checkpoint'].strip()
|
||||||
|
work_dir = osp.join(work_dir, fname)
|
||||||
|
if not isinstance(model_info['eval'], list):
|
||||||
|
evals = [model_info['eval']]
|
||||||
|
else:
|
||||||
|
evals = model_info['eval']
|
||||||
|
eval = ' '.join(evals)
|
||||||
|
return dict(
|
||||||
|
config=config,
|
||||||
|
job_name=job_name,
|
||||||
|
checkpoint=checkpoint,
|
||||||
|
work_dir=work_dir,
|
||||||
|
eval=eval)
|
||||||
|
|
||||||
|
|
||||||
|
def create_test_bash_info(commands, model_test_dict, port, script_name,
|
||||||
|
partition):
|
||||||
|
config = model_test_dict['config']
|
||||||
|
job_name = model_test_dict['job_name']
|
||||||
|
checkpoint = model_test_dict['checkpoint']
|
||||||
|
work_dir = model_test_dict['work_dir']
|
||||||
|
eval = model_test_dict['eval']
|
||||||
|
|
||||||
|
echo_info = f'\necho \'{config}\' &'
|
||||||
|
commands.append(echo_info)
|
||||||
|
commands.append('\n')
|
||||||
|
|
||||||
|
command_info = f'GPUS=4 GPUS_PER_NODE=4 ' \
|
||||||
|
f'CPUS_PER_TASK=2 {script_name} '
|
||||||
|
|
||||||
|
command_info += f'{partition} '
|
||||||
|
command_info += f'{job_name} '
|
||||||
|
command_info += f'{config} '
|
||||||
|
command_info += f'$CHECKPOINT_DIR/{checkpoint} '
|
||||||
|
|
||||||
|
command_info += f'--eval {eval} '
|
||||||
|
command_info += f'--work-dir {work_dir} '
|
||||||
|
command_info += f'--cfg-options dist_params.port={port} '
|
||||||
|
command_info += '&'
|
||||||
|
|
||||||
|
commands.append(command_info)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
if args.out:
|
||||||
|
out_suffix = args.out.split('.')[-1]
|
||||||
|
assert args.out.endswith('.sh'), \
|
||||||
|
f'Expected out file path suffix is .sh, but get .{out_suffix}'
|
||||||
|
|
||||||
|
commands = []
|
||||||
|
partition_name = 'PARTITION=$1'
|
||||||
|
commands.append(partition_name)
|
||||||
|
commands.append('\n')
|
||||||
|
|
||||||
|
checkpoint_root = 'CHECKPOINT_DIR=$2'
|
||||||
|
commands.append(checkpoint_root)
|
||||||
|
commands.append('\n')
|
||||||
|
|
||||||
|
script_name = osp.join('tools', 'slurm_test.sh')
|
||||||
|
port = args.port
|
||||||
|
work_dir = args.work_dir
|
||||||
|
|
||||||
|
cfg = Config.fromfile(args.config)
|
||||||
|
|
||||||
|
for model_key in cfg:
|
||||||
|
model_infos = cfg[model_key]
|
||||||
|
if not isinstance(model_infos, list):
|
||||||
|
model_infos = [model_infos]
|
||||||
|
for model_info in model_infos:
|
||||||
|
print('processing: ', model_info['config'])
|
||||||
|
model_test_dict = process_model_info(model_info, work_dir)
|
||||||
|
create_test_bash_info(commands, model_test_dict, port, script_name,
|
||||||
|
'$PARTITION')
|
||||||
|
port += 1
|
||||||
|
|
||||||
|
command_str = ''.join(commands)
|
||||||
|
if args.out:
|
||||||
|
with open(args.out, 'w') as f:
|
||||||
|
f.write(command_str + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@ -0,0 +1,91 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import argparse
|
||||||
|
import os.path as osp
|
||||||
|
|
||||||
|
# Default using 4 gpu when training
|
||||||
|
config_8gpu_list = [
|
||||||
|
'configs/swin/upernet_swin_tiny_patch4_window7_512x512_160k_ade20k_pretrain_224x224_1K.py', # noqa
|
||||||
|
'configs/vit/upernet_vit-b16_ln_mln_512x512_160k_ade20k.py',
|
||||||
|
'configs/vit/upernet_deit-s16_ln_mln_512x512_160k_ade20k.py',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Convert benchmark model json to script')
|
||||||
|
parser.add_argument(
|
||||||
|
'txt_path', type=str, help='txt path output by benchmark_filter')
|
||||||
|
parser.add_argument('--port', type=int, default=24727, help='dist port')
|
||||||
|
parser.add_argument(
|
||||||
|
'--out',
|
||||||
|
type=str,
|
||||||
|
default='.dev/benchmark_train.sh',
|
||||||
|
help='path to save model benchmark script')
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def create_train_bash_info(commands, config, script_name, partition, port):
|
||||||
|
cfg = config.strip()
|
||||||
|
|
||||||
|
# print cfg name
|
||||||
|
echo_info = f'echo \'{cfg}\' &'
|
||||||
|
commands.append(echo_info)
|
||||||
|
commands.append('\n')
|
||||||
|
|
||||||
|
_, model_name = osp.split(osp.dirname(cfg))
|
||||||
|
config_name, _ = osp.splitext(osp.basename(cfg))
|
||||||
|
# default setting
|
||||||
|
if cfg in config_8gpu_list:
|
||||||
|
command_info = f'GPUS=8 GPUS_PER_NODE=8 ' \
|
||||||
|
f'CPUS_PER_TASK=2 {script_name} '
|
||||||
|
else:
|
||||||
|
command_info = f'GPUS=4 GPUS_PER_NODE=4 ' \
|
||||||
|
f'CPUS_PER_TASK=2 {script_name} '
|
||||||
|
command_info += f'{partition} '
|
||||||
|
command_info += f'{config_name} '
|
||||||
|
command_info += f'{cfg} '
|
||||||
|
command_info += f'--cfg-options ' \
|
||||||
|
f'checkpoint_config.max_keep_ckpts=1 ' \
|
||||||
|
f'dist_params.port={port} '
|
||||||
|
command_info += f'--work-dir work_dirs/{model_name}/{config_name} '
|
||||||
|
# Let the script shut up
|
||||||
|
command_info += '>/dev/null &'
|
||||||
|
|
||||||
|
commands.append(command_info)
|
||||||
|
commands.append('\n')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
if args.out:
|
||||||
|
out_suffix = args.out.split('.')[-1]
|
||||||
|
assert args.out.endswith('.sh'), \
|
||||||
|
f'Expected out file path suffix is .sh, but get .{out_suffix}'
|
||||||
|
|
||||||
|
root_name = './tools'
|
||||||
|
script_name = osp.join(root_name, 'slurm_train.sh')
|
||||||
|
port = args.port
|
||||||
|
partition_name = 'PARTITION=$1'
|
||||||
|
|
||||||
|
commands = []
|
||||||
|
commands.append(partition_name)
|
||||||
|
commands.append('\n')
|
||||||
|
commands.append('\n')
|
||||||
|
|
||||||
|
with open(args.txt_path, 'r') as f:
|
||||||
|
model_cfgs = f.readlines()
|
||||||
|
for i, cfg in enumerate(model_cfgs):
|
||||||
|
create_train_bash_info(commands, cfg, script_name, '$PARTITION',
|
||||||
|
port)
|
||||||
|
port += 1
|
||||||
|
|
||||||
|
command_str = ''.join(commands)
|
||||||
|
if args.out:
|
||||||
|
with open(args.out, 'w') as f:
|
||||||
|
f.write(command_str)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
work_dir = '../../work_dirs'
|
||||||
|
metric = 'mIoU'
|
||||||
|
|
||||||
|
# specify the log files we would like to collect in `log_items`
|
||||||
|
log_items = [
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_cnn_lr_with_warmup',
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_cnn_no_warmup_lr',
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_mit_trans_lr',
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_swin_trans_lr'
|
||||||
|
]
|
||||||
|
# or specify ignore_keywords, then the folders whose name contain
|
||||||
|
# `'segformer'` won't be collected
|
||||||
|
# ignore_keywords = ['segformer']
|
||||||
|
|
||||||
|
# should not include metric
|
||||||
|
other_info_keys = ['mAcc']
|
||||||
|
markdown_file = 'markdowns/lr_in_trans.json.md'
|
||||||
|
json_file = 'jsons/trans_in_cnn.json'
|
||||||
@ -0,0 +1,143 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import argparse
|
||||||
|
import datetime
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
from utils import load_config
|
||||||
|
|
||||||
|
# automatically collect all the results
|
||||||
|
|
||||||
|
# The structure of the directory:
|
||||||
|
# ├── work-dir
|
||||||
|
# │ ├── config_1
|
||||||
|
# │ │ ├── time1.log.json
|
||||||
|
# │ │ ├── time2.log.json
|
||||||
|
# │ │ ├── time3.log.json
|
||||||
|
# │ │ ├── time4.log.json
|
||||||
|
# │ ├── config_2
|
||||||
|
# │ │ ├── time5.log.json
|
||||||
|
# │ │ ├── time6.log.json
|
||||||
|
# │ │ ├── time7.log.json
|
||||||
|
# │ │ ├── time8.log.json
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description='extract info from log.json')
|
||||||
|
parser.add_argument('config_dir')
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def has_keyword(name: str, keywords: list):
|
||||||
|
for a_keyword in keywords:
|
||||||
|
if a_keyword in name:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
cfg = load_config(args.config_dir)
|
||||||
|
work_dir = cfg['work_dir']
|
||||||
|
metric = cfg['metric']
|
||||||
|
log_items = cfg.get('log_items', [])
|
||||||
|
ignore_keywords = cfg.get('ignore_keywords', [])
|
||||||
|
other_info_keys = cfg.get('other_info_keys', [])
|
||||||
|
markdown_file = cfg.get('markdown_file', None)
|
||||||
|
json_file = cfg.get('json_file', None)
|
||||||
|
|
||||||
|
if json_file and osp.split(json_file)[0] != '':
|
||||||
|
os.makedirs(osp.split(json_file)[0], exist_ok=True)
|
||||||
|
if markdown_file and osp.split(markdown_file)[0] != '':
|
||||||
|
os.makedirs(osp.split(markdown_file)[0], exist_ok=True)
|
||||||
|
|
||||||
|
assert not (log_items and ignore_keywords), \
|
||||||
|
'log_items and ignore_keywords cannot be specified at the same time'
|
||||||
|
assert metric not in other_info_keys, \
|
||||||
|
'other_info_keys should not contain metric'
|
||||||
|
|
||||||
|
if ignore_keywords and isinstance(ignore_keywords, str):
|
||||||
|
ignore_keywords = [ignore_keywords]
|
||||||
|
if other_info_keys and isinstance(other_info_keys, str):
|
||||||
|
other_info_keys = [other_info_keys]
|
||||||
|
if log_items and isinstance(log_items, str):
|
||||||
|
log_items = [log_items]
|
||||||
|
|
||||||
|
if not log_items:
|
||||||
|
log_items = [
|
||||||
|
item for item in sorted(os.listdir(work_dir))
|
||||||
|
if not has_keyword(item, ignore_keywords)
|
||||||
|
]
|
||||||
|
|
||||||
|
experiment_info_list = []
|
||||||
|
for config_dir in log_items:
|
||||||
|
preceding_path = os.path.join(work_dir, config_dir)
|
||||||
|
log_list = [
|
||||||
|
item for item in os.listdir(preceding_path)
|
||||||
|
if item.endswith('.log.json')
|
||||||
|
]
|
||||||
|
log_list = sorted(
|
||||||
|
log_list,
|
||||||
|
key=lambda time_str: datetime.datetime.strptime(
|
||||||
|
time_str, '%Y%m%d_%H%M%S.log.json'))
|
||||||
|
val_list = []
|
||||||
|
last_iter = 0
|
||||||
|
for log_name in log_list:
|
||||||
|
with open(os.path.join(preceding_path, log_name), 'r') as f:
|
||||||
|
# ignore the info line
|
||||||
|
f.readline()
|
||||||
|
all_lines = f.readlines()
|
||||||
|
val_list.extend([
|
||||||
|
json.loads(line) for line in all_lines
|
||||||
|
if json.loads(line)['mode'] == 'val'
|
||||||
|
])
|
||||||
|
for index in range(len(all_lines) - 1, -1, -1):
|
||||||
|
line_dict = json.loads(all_lines[index])
|
||||||
|
if line_dict['mode'] == 'train':
|
||||||
|
last_iter = max(last_iter, line_dict['iter'])
|
||||||
|
break
|
||||||
|
|
||||||
|
new_log_dict = dict(
|
||||||
|
method=config_dir, metric_used=metric, last_iter=last_iter)
|
||||||
|
for index, log in enumerate(val_list, 1):
|
||||||
|
new_ordered_dict = OrderedDict()
|
||||||
|
new_ordered_dict['eval_index'] = index
|
||||||
|
new_ordered_dict[metric] = log[metric]
|
||||||
|
for key in other_info_keys:
|
||||||
|
if key in log:
|
||||||
|
new_ordered_dict[key] = log[key]
|
||||||
|
val_list[index - 1] = new_ordered_dict
|
||||||
|
|
||||||
|
assert len(val_list) >= 1, \
|
||||||
|
f"work dir {config_dir} doesn't contain any evaluation."
|
||||||
|
new_log_dict['last eval'] = val_list[-1]
|
||||||
|
new_log_dict['best eval'] = max(val_list, key=lambda x: x[metric])
|
||||||
|
experiment_info_list.append(new_log_dict)
|
||||||
|
print(f'{config_dir} is processed')
|
||||||
|
|
||||||
|
if json_file:
|
||||||
|
with open(json_file, 'w') as f:
|
||||||
|
json.dump(experiment_info_list, f, indent=4)
|
||||||
|
|
||||||
|
if markdown_file:
|
||||||
|
lines_to_write = []
|
||||||
|
for index, log in enumerate(experiment_info_list, 1):
|
||||||
|
lines_to_write.append(
|
||||||
|
f"|{index}|{log['method']}|{log['best eval'][metric]}"
|
||||||
|
f"|{log['best eval']['eval_index']}|"
|
||||||
|
f"{log['last eval'][metric]}|"
|
||||||
|
f"{log['last eval']['eval_index']}|{log['last_iter']}|\n")
|
||||||
|
with open(markdown_file, 'w') as f:
|
||||||
|
f.write(f'|exp_num|method|{metric} best|best index|'
|
||||||
|
f'{metric} last|last index|last iter num|\n')
|
||||||
|
f.write('|:---:|:---:|:---:|:---:|:---:|:---:|:---:|\n')
|
||||||
|
f.writelines(lines_to_write)
|
||||||
|
|
||||||
|
print('processed successfully')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@ -0,0 +1,144 @@
|
|||||||
|
# Log Collector
|
||||||
|
|
||||||
|
## Function
|
||||||
|
|
||||||
|
Automatically collect logs and write the result in a json file or markdown file.
|
||||||
|
|
||||||
|
If there are several `.log.json` files in one folder, Log Collector assumes that the `.log.json` files other than the first one are resume from the preceding `.log.json` file. Log Collector returns the result considering all `.log.json` files.
|
||||||
|
|
||||||
|
## Usage:
|
||||||
|
|
||||||
|
To use log collector, you need to write a config file to configure the log collector first.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
example_config.py:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# The work directory that contains folders that contains .log.json files.
|
||||||
|
work_dir = '../../work_dirs'
|
||||||
|
# The metric used to find the best evaluation.
|
||||||
|
metric = 'mIoU'
|
||||||
|
|
||||||
|
# **Don't specify the log_items and ignore_keywords at the same time.**
|
||||||
|
# Specify the log files we would like to collect in `log_items`.
|
||||||
|
# The folders specified should be the subdirectories of `work_dir`.
|
||||||
|
log_items = [
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_cnn_lr_with_warmup',
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_cnn_no_warmup_lr',
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_mit_trans_lr',
|
||||||
|
'segformer_mit-b5_512x512_160k_ade20k_swin_trans_lr'
|
||||||
|
]
|
||||||
|
# Or specify `ignore_keywords`. The folders whose name contain one
|
||||||
|
# of the keywords in the `ignore_keywords` list(e.g., `'segformer'`)
|
||||||
|
# won't be collected.
|
||||||
|
# ignore_keywords = ['segformer']
|
||||||
|
|
||||||
|
# Other log items in .log.json that you want to collect.
|
||||||
|
# should not include metric.
|
||||||
|
other_info_keys = ["mAcc"]
|
||||||
|
# The output markdown file's name.
|
||||||
|
markdown_file ='markdowns/lr_in_trans.json.md'
|
||||||
|
# The output json file's name. (optional)
|
||||||
|
json_file = 'jsons/trans_in_cnn.json'
|
||||||
|
```
|
||||||
|
|
||||||
|
The structure of the work-dir directory should be like:
|
||||||
|
|
||||||
|
```text
|
||||||
|
├── work-dir
|
||||||
|
│ ├── folder1
|
||||||
|
│ │ ├── time1.log.json
|
||||||
|
│ │ ├── time2.log.json
|
||||||
|
│ │ ├── time3.log.json
|
||||||
|
│ │ ├── time4.log.json
|
||||||
|
│ ├── folder2
|
||||||
|
│ │ ├── time5.log.json
|
||||||
|
│ │ ├── time6.log.json
|
||||||
|
│ │ ├── time7.log.json
|
||||||
|
│ │ ├── time8.log.json
|
||||||
|
```
|
||||||
|
|
||||||
|
Then , cd to the log collector folder.
|
||||||
|
|
||||||
|
Now you can run log_collector.py by using command:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python log_collector.py ./example_config.py
|
||||||
|
```
|
||||||
|
|
||||||
|
The output markdown file is like:
|
||||||
|
|
||||||
|
| exp_num | method | mIoU best | best index | mIoU last | last index | last iter num |
|
||||||
|
| :-----: | :-----------------------------------------------------: | :-------: | :--------: | :-------: | :--------: | :-----------: |
|
||||||
|
| 1 | segformer_mit-b5_512x512_160k_ade20k_cnn_lr_with_warmup | 0.2776 | 10 | 0.2776 | 10 | 160000 |
|
||||||
|
| 2 | segformer_mit-b5_512x512_160k_ade20k_cnn_no_warmup_lr | 0.2802 | 10 | 0.2802 | 10 | 160000 |
|
||||||
|
| 3 | segformer_mit-b5_512x512_160k_ade20k_mit_trans_lr | 0.4943 | 11 | 0.4943 | 11 | 160000 |
|
||||||
|
| 4 | segformer_mit-b5_512x512_160k_ade20k_swin_trans_lr | 0.4883 | 11 | 0.4883 | 11 | 160000 |
|
||||||
|
|
||||||
|
The output json file is like:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"method": "segformer_mit-b5_512x512_160k_ade20k_cnn_lr_with_warmup",
|
||||||
|
"metric_used": "mIoU",
|
||||||
|
"last_iter": 160000,
|
||||||
|
"last eval": {
|
||||||
|
"eval_index": 10,
|
||||||
|
"mIoU": 0.2776,
|
||||||
|
"mAcc": 0.3779
|
||||||
|
},
|
||||||
|
"best eval": {
|
||||||
|
"eval_index": 10,
|
||||||
|
"mIoU": 0.2776,
|
||||||
|
"mAcc": 0.3779
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "segformer_mit-b5_512x512_160k_ade20k_cnn_no_warmup_lr",
|
||||||
|
"metric_used": "mIoU",
|
||||||
|
"last_iter": 160000,
|
||||||
|
"last eval": {
|
||||||
|
"eval_index": 10,
|
||||||
|
"mIoU": 0.2802,
|
||||||
|
"mAcc": 0.3764
|
||||||
|
},
|
||||||
|
"best eval": {
|
||||||
|
"eval_index": 10,
|
||||||
|
"mIoU": 0.2802,
|
||||||
|
"mAcc": 0.3764
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "segformer_mit-b5_512x512_160k_ade20k_mit_trans_lr",
|
||||||
|
"metric_used": "mIoU",
|
||||||
|
"last_iter": 160000,
|
||||||
|
"last eval": {
|
||||||
|
"eval_index": 11,
|
||||||
|
"mIoU": 0.4943,
|
||||||
|
"mAcc": 0.6097
|
||||||
|
},
|
||||||
|
"best eval": {
|
||||||
|
"eval_index": 11,
|
||||||
|
"mIoU": 0.4943,
|
||||||
|
"mAcc": 0.6097
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"method": "segformer_mit-b5_512x512_160k_ade20k_swin_trans_lr",
|
||||||
|
"metric_used": "mIoU",
|
||||||
|
"last_iter": 160000,
|
||||||
|
"last eval": {
|
||||||
|
"eval_index": 11,
|
||||||
|
"mIoU": 0.4883,
|
||||||
|
"mAcc": 0.6061
|
||||||
|
},
|
||||||
|
"best eval": {
|
||||||
|
"eval_index": 11,
|
||||||
|
"mIoU": 0.4883,
|
||||||
|
"mAcc": 0.6061
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
# modified from https://github.dev/open-mmlab/mmcv
|
||||||
|
import os.path as osp
|
||||||
|
import sys
|
||||||
|
from importlib import import_module
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(cfg_dir: str) -> dict:
|
||||||
|
assert cfg_dir.endswith('.py')
|
||||||
|
root_path, file_name = osp.split(cfg_dir)
|
||||||
|
temp_module = osp.splitext(file_name)[0]
|
||||||
|
sys.path.insert(0, root_path)
|
||||||
|
mod = import_module(temp_module)
|
||||||
|
sys.path.pop(0)
|
||||||
|
cfg_dict = {
|
||||||
|
k: v
|
||||||
|
for k, v in mod.__dict__.items() if not k.startswith('__')
|
||||||
|
}
|
||||||
|
del sys.modules[temp_module]
|
||||||
|
return cfg_dict
|
||||||
317
prediction/image/mx15hdi/Detect/mmsegmentation/.dev/md2yml.py
Normal file
317
prediction/image/mx15hdi/Detect/mmsegmentation/.dev/md2yml.py
Normal file
@ -0,0 +1,317 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
# This tool is used to update model-index.yml which is required by MIM, and
|
||||||
|
# will be automatically called as a pre-commit hook. The updating will be
|
||||||
|
# triggered if any change of model information (.md files in configs/) has been
|
||||||
|
# detected before a commit.
|
||||||
|
|
||||||
|
import glob
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from lxml import etree
|
||||||
|
from mmcv.fileio import dump
|
||||||
|
|
||||||
|
MMSEG_ROOT = osp.dirname(osp.dirname((osp.dirname(__file__))))
|
||||||
|
|
||||||
|
COLLECTIONS = [
|
||||||
|
'ANN', 'APCNet', 'BiSeNetV1', 'BiSeNetV2', 'CCNet', 'CGNet', 'DANet',
|
||||||
|
'DeepLabV3', 'DeepLabV3+', 'DMNet', 'DNLNet', 'DPT', 'EMANet', 'EncNet',
|
||||||
|
'ERFNet', 'FastFCN', 'FastSCNN', 'FCN', 'GCNet', 'ICNet', 'ISANet', 'KNet',
|
||||||
|
'NonLocalNet', 'OCRNet', 'PointRend', 'PSANet', 'PSPNet', 'Segformer',
|
||||||
|
'Segmenter', 'FPN', 'SETR', 'STDC', 'UNet', 'UPerNet'
|
||||||
|
]
|
||||||
|
COLLECTIONS_TEMP = []
|
||||||
|
|
||||||
|
|
||||||
|
def dump_yaml_and_check_difference(obj, filename, sort_keys=False):
|
||||||
|
"""Dump object to a yaml file, and check if the file content is different
|
||||||
|
from the original.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (any): The python object to be dumped.
|
||||||
|
filename (str): YAML filename to dump the object to.
|
||||||
|
sort_keys (str); Sort key by dictionary order.
|
||||||
|
Returns:
|
||||||
|
Bool: If the target YAML file is different from the original.
|
||||||
|
"""
|
||||||
|
|
||||||
|
str_dump = dump(obj, None, file_format='yaml', sort_keys=sort_keys)
|
||||||
|
if osp.isfile(filename):
|
||||||
|
file_exists = True
|
||||||
|
with open(filename, 'r', encoding='utf-8') as f:
|
||||||
|
str_orig = f.read()
|
||||||
|
else:
|
||||||
|
file_exists = False
|
||||||
|
str_orig = None
|
||||||
|
|
||||||
|
if file_exists and str_orig == str_dump:
|
||||||
|
is_different = False
|
||||||
|
else:
|
||||||
|
is_different = True
|
||||||
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(str_dump)
|
||||||
|
|
||||||
|
return is_different
|
||||||
|
|
||||||
|
|
||||||
|
def parse_md(md_file):
|
||||||
|
"""Parse .md file and convert it to a .yml file which can be used for MIM.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
md_file (str): Path to .md file.
|
||||||
|
Returns:
|
||||||
|
Bool: If the target YAML file is different from the original.
|
||||||
|
"""
|
||||||
|
collection_name = osp.split(osp.dirname(md_file))[1]
|
||||||
|
configs = os.listdir(osp.dirname(md_file))
|
||||||
|
|
||||||
|
collection = dict(
|
||||||
|
Name=collection_name,
|
||||||
|
Metadata={'Training Data': []},
|
||||||
|
Paper={
|
||||||
|
'URL': '',
|
||||||
|
'Title': ''
|
||||||
|
},
|
||||||
|
README=md_file,
|
||||||
|
Code={
|
||||||
|
'URL': '',
|
||||||
|
'Version': ''
|
||||||
|
})
|
||||||
|
collection.update({'Converted From': {'Weights': '', 'Code': ''}})
|
||||||
|
models = []
|
||||||
|
datasets = []
|
||||||
|
paper_url = None
|
||||||
|
paper_title = None
|
||||||
|
code_url = None
|
||||||
|
code_version = None
|
||||||
|
repo_url = None
|
||||||
|
|
||||||
|
# To avoid re-counting number of backbone model in OpenMMLab,
|
||||||
|
# if certain model in configs folder is backbone whose name is already
|
||||||
|
# recorded in MMClassification, then the `COLLECTION` dict of this model
|
||||||
|
# in MMSegmentation should be deleted, and `In Collection` in `Models`
|
||||||
|
# should be set with head or neck of this config file.
|
||||||
|
is_backbone = None
|
||||||
|
|
||||||
|
with open(md_file, 'r', encoding='UTF-8') as md:
|
||||||
|
lines = md.readlines()
|
||||||
|
i = 0
|
||||||
|
current_dataset = ''
|
||||||
|
while i < len(lines):
|
||||||
|
line = lines[i].strip()
|
||||||
|
# In latest README.md the title and url are in the third line.
|
||||||
|
if i == 2:
|
||||||
|
paper_url = lines[i].split('](')[1].split(')')[0]
|
||||||
|
paper_title = lines[i].split('](')[0].split('[')[1]
|
||||||
|
if len(line) == 0:
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
elif line[:3] == '<a ':
|
||||||
|
content = etree.HTML(line)
|
||||||
|
node = content.xpath('//a')[0]
|
||||||
|
if node.text == 'Code Snippet':
|
||||||
|
code_url = node.get('href', None)
|
||||||
|
assert code_url is not None, (
|
||||||
|
f'{collection_name} hasn\'t code snippet url.')
|
||||||
|
# version extraction
|
||||||
|
filter_str = r'blob/(.*)/mm'
|
||||||
|
pattern = re.compile(filter_str)
|
||||||
|
code_version = pattern.findall(code_url)
|
||||||
|
assert len(code_version) == 1, (
|
||||||
|
f'false regular expression ({filter_str}) use.')
|
||||||
|
code_version = code_version[0]
|
||||||
|
elif node.text == 'Official Repo':
|
||||||
|
repo_url = node.get('href', None)
|
||||||
|
assert repo_url is not None, (
|
||||||
|
f'{collection_name} hasn\'t official repo url.')
|
||||||
|
i += 1
|
||||||
|
elif line[:4] == '### ':
|
||||||
|
datasets.append(line[4:])
|
||||||
|
current_dataset = line[4:]
|
||||||
|
i += 2
|
||||||
|
elif line[:15] == '<!-- [BACKBONE]':
|
||||||
|
is_backbone = True
|
||||||
|
i += 1
|
||||||
|
elif (line[0] == '|' and (i + 1) < len(lines)
|
||||||
|
and lines[i + 1][:3] == '| -' and 'Method' in line
|
||||||
|
and 'Crop Size' in line and 'Mem (GB)' in line):
|
||||||
|
cols = [col.strip() for col in line.split('|')]
|
||||||
|
method_id = cols.index('Method')
|
||||||
|
backbone_id = cols.index('Backbone')
|
||||||
|
crop_size_id = cols.index('Crop Size')
|
||||||
|
lr_schd_id = cols.index('Lr schd')
|
||||||
|
mem_id = cols.index('Mem (GB)')
|
||||||
|
fps_id = cols.index('Inf time (fps)')
|
||||||
|
try:
|
||||||
|
ss_id = cols.index('mIoU')
|
||||||
|
except ValueError:
|
||||||
|
ss_id = cols.index('Dice')
|
||||||
|
try:
|
||||||
|
ms_id = cols.index('mIoU(ms+flip)')
|
||||||
|
except ValueError:
|
||||||
|
ms_id = False
|
||||||
|
config_id = cols.index('config')
|
||||||
|
download_id = cols.index('download')
|
||||||
|
j = i + 2
|
||||||
|
while j < len(lines) and lines[j][0] == '|':
|
||||||
|
els = [el.strip() for el in lines[j].split('|')]
|
||||||
|
config = ''
|
||||||
|
model_name = ''
|
||||||
|
weight = ''
|
||||||
|
for fn in configs:
|
||||||
|
if fn in els[config_id]:
|
||||||
|
left = els[download_id].index(
|
||||||
|
'https://download.openmmlab.com')
|
||||||
|
right = els[download_id].index('.pth') + 4
|
||||||
|
weight = els[download_id][left:right]
|
||||||
|
config = f'configs/{collection_name}/{fn}'
|
||||||
|
model_name = fn[:-3]
|
||||||
|
fps = els[fps_id] if els[fps_id] != '-' and els[
|
||||||
|
fps_id] != '' else -1
|
||||||
|
mem = els[mem_id].split(
|
||||||
|
'\\'
|
||||||
|
)[0] if els[mem_id] != '-' and els[mem_id] != '' else -1
|
||||||
|
crop_size = els[crop_size_id].split('x')
|
||||||
|
assert len(crop_size) == 2
|
||||||
|
method = els[method_id].split()[0].split('-')[-1]
|
||||||
|
model = {
|
||||||
|
'Name':
|
||||||
|
model_name,
|
||||||
|
'In Collection':
|
||||||
|
method,
|
||||||
|
'Metadata': {
|
||||||
|
'backbone': els[backbone_id],
|
||||||
|
'crop size': f'({crop_size[0]},{crop_size[1]})',
|
||||||
|
'lr schd': int(els[lr_schd_id]),
|
||||||
|
},
|
||||||
|
'Results': [
|
||||||
|
{
|
||||||
|
'Task': 'Semantic Segmentation',
|
||||||
|
'Dataset': current_dataset,
|
||||||
|
'Metrics': {
|
||||||
|
cols[ss_id]: float(els[ss_id]),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
'Config':
|
||||||
|
config,
|
||||||
|
'Weights':
|
||||||
|
weight,
|
||||||
|
}
|
||||||
|
if fps != -1:
|
||||||
|
try:
|
||||||
|
fps = float(fps)
|
||||||
|
except Exception:
|
||||||
|
j += 1
|
||||||
|
continue
|
||||||
|
model['Metadata']['inference time (ms/im)'] = [{
|
||||||
|
'value':
|
||||||
|
round(1000 / float(fps), 2),
|
||||||
|
'hardware':
|
||||||
|
'V100',
|
||||||
|
'backend':
|
||||||
|
'PyTorch',
|
||||||
|
'batch size':
|
||||||
|
1,
|
||||||
|
'mode':
|
||||||
|
'FP32' if 'fp16' not in config else 'FP16',
|
||||||
|
'resolution':
|
||||||
|
f'({crop_size[0]},{crop_size[1]})'
|
||||||
|
}]
|
||||||
|
if mem != -1:
|
||||||
|
model['Metadata']['Training Memory (GB)'] = float(mem)
|
||||||
|
# Only have semantic segmentation now
|
||||||
|
if ms_id and els[ms_id] != '-' and els[ms_id] != '':
|
||||||
|
model['Results'][0]['Metrics'][
|
||||||
|
'mIoU(ms+flip)'] = float(els[ms_id])
|
||||||
|
models.append(model)
|
||||||
|
j += 1
|
||||||
|
i = j
|
||||||
|
else:
|
||||||
|
i += 1
|
||||||
|
flag = (code_url is not None) and (paper_url is not None) and (repo_url
|
||||||
|
is not None)
|
||||||
|
assert flag, f'{collection_name} readme error'
|
||||||
|
collection['Name'] = method
|
||||||
|
collection['Metadata']['Training Data'] = datasets
|
||||||
|
collection['Code']['URL'] = code_url
|
||||||
|
collection['Code']['Version'] = code_version
|
||||||
|
collection['Paper']['URL'] = paper_url
|
||||||
|
collection['Paper']['Title'] = paper_title
|
||||||
|
collection['Converted From']['Code'] = repo_url
|
||||||
|
# ['Converted From']['Weights] miss
|
||||||
|
# remove empty attribute
|
||||||
|
check_key_list = ['Code', 'Paper', 'Converted From']
|
||||||
|
for check_key in check_key_list:
|
||||||
|
key_list = list(collection[check_key].keys())
|
||||||
|
for key in key_list:
|
||||||
|
if check_key not in collection:
|
||||||
|
break
|
||||||
|
if collection[check_key][key] == '':
|
||||||
|
if len(collection[check_key].keys()) == 1:
|
||||||
|
collection.pop(check_key)
|
||||||
|
else:
|
||||||
|
collection[check_key].pop(key)
|
||||||
|
yml_file = f'{md_file[:-9]}{collection_name}.yml'
|
||||||
|
if is_backbone:
|
||||||
|
if collection['Name'] not in COLLECTIONS:
|
||||||
|
result = {
|
||||||
|
'Collections': [collection],
|
||||||
|
'Models': models,
|
||||||
|
'Yml': yml_file
|
||||||
|
}
|
||||||
|
COLLECTIONS_TEMP.append(result)
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
result = {'Models': models}
|
||||||
|
else:
|
||||||
|
COLLECTIONS.append(collection['Name'])
|
||||||
|
result = {'Collections': [collection], 'Models': models}
|
||||||
|
return dump_yaml_and_check_difference(result, yml_file)
|
||||||
|
|
||||||
|
|
||||||
|
def update_model_index():
|
||||||
|
"""Update model-index.yml according to model .md files.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Bool: If the updated model-index.yml is different from the original.
|
||||||
|
"""
|
||||||
|
configs_dir = osp.join(MMSEG_ROOT, 'configs')
|
||||||
|
yml_files = glob.glob(osp.join(configs_dir, '**', '*.yml'), recursive=True)
|
||||||
|
yml_files.sort()
|
||||||
|
|
||||||
|
# add .replace('\\', '/') to avoid Windows Style path
|
||||||
|
model_index = {
|
||||||
|
'Import': [
|
||||||
|
osp.relpath(yml_file, MMSEG_ROOT).replace('\\', '/')
|
||||||
|
for yml_file in yml_files
|
||||||
|
]
|
||||||
|
}
|
||||||
|
model_index_file = osp.join(MMSEG_ROOT, 'model-index.yml')
|
||||||
|
is_different = dump_yaml_and_check_difference(model_index,
|
||||||
|
model_index_file)
|
||||||
|
|
||||||
|
return is_different
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
file_list = [fn for fn in sys.argv[1:] if osp.basename(fn) == 'README.md']
|
||||||
|
if not file_list:
|
||||||
|
sys.exit(0)
|
||||||
|
file_modified = False
|
||||||
|
for fn in file_list:
|
||||||
|
file_modified |= parse_md(fn)
|
||||||
|
|
||||||
|
for result in COLLECTIONS_TEMP:
|
||||||
|
collection = result['Collections'][0]
|
||||||
|
yml_file = result.pop('Yml', None)
|
||||||
|
if collection['Name'] in COLLECTIONS:
|
||||||
|
result.pop('Collections')
|
||||||
|
file_modified |= dump_yaml_and_check_difference(result, yml_file)
|
||||||
|
|
||||||
|
file_modified |= update_model_index()
|
||||||
|
sys.exit(1 if file_modified else 0)
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
# Copyright (c) OpenMMLab. All rights reserved.
|
||||||
|
import argparse
|
||||||
|
import os
|
||||||
|
import os.path as osp
|
||||||
|
|
||||||
|
import oss2
|
||||||
|
|
||||||
|
ACCESS_KEY_ID = os.getenv('OSS_ACCESS_KEY_ID', None)
|
||||||
|
ACCESS_KEY_SECRET = os.getenv('OSS_ACCESS_KEY_SECRET', None)
|
||||||
|
BUCKET_NAME = 'openmmlab'
|
||||||
|
ENDPOINT = 'https://oss-accelerate.aliyuncs.com'
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args():
|
||||||
|
parser = argparse.ArgumentParser(description='Upload models to OSS')
|
||||||
|
parser.add_argument('model_zoo', type=str, help='model_zoo input')
|
||||||
|
parser.add_argument(
|
||||||
|
'--dst-folder',
|
||||||
|
type=str,
|
||||||
|
default='mmsegmentation/v0.5',
|
||||||
|
help='destination folder')
|
||||||
|
args = parser.parse_args()
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
args = parse_args()
|
||||||
|
model_zoo = args.model_zoo
|
||||||
|
dst_folder = args.dst_folder
|
||||||
|
bucket = oss2.Bucket(
|
||||||
|
oss2.Auth(ACCESS_KEY_ID, ACCESS_KEY_SECRET), ENDPOINT, BUCKET_NAME)
|
||||||
|
|
||||||
|
for root, dirs, files in os.walk(model_zoo):
|
||||||
|
for file in files:
|
||||||
|
file_path = osp.relpath(osp.join(root, file), model_zoo)
|
||||||
|
print(f'Uploading {file_path}')
|
||||||
|
|
||||||
|
oss2.resumable_upload(bucket, osp.join(dst_folder, file_path),
|
||||||
|
osp.join(model_zoo, file_path))
|
||||||
|
bucket.put_object_acl(
|
||||||
|
osp.join(dst_folder, file_path), oss2.OBJECT_ACL_PUBLIC_READ)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
76
prediction/image/mx15hdi/Detect/mmsegmentation/.github/CODE_OF_CONDUCT.md
vendored
Normal file
76
prediction/image/mx15hdi/Detect/mmsegmentation/.github/CODE_OF_CONDUCT.md
vendored
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
In the interest of fostering an open and welcoming environment, we as
|
||||||
|
contributors and maintainers pledge to making participation in our project and
|
||||||
|
our community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, disability, ethnicity, sex characteristics, gender identity and expression,
|
||||||
|
level of experience, education, socio-economic status, nationality, personal
|
||||||
|
appearance, race, religion, or sexual identity and orientation.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to creating a positive environment
|
||||||
|
include:
|
||||||
|
|
||||||
|
- Using welcoming and inclusive language
|
||||||
|
- Being respectful of differing viewpoints and experiences
|
||||||
|
- Gracefully accepting constructive criticism
|
||||||
|
- Focusing on what is best for the community
|
||||||
|
- Showing empathy towards other community members
|
||||||
|
|
||||||
|
Examples of unacceptable behavior by participants include:
|
||||||
|
|
||||||
|
- The use of sexualized language or imagery and unwelcome sexual attention or
|
||||||
|
advances
|
||||||
|
- Trolling, insulting/derogatory comments, and personal or political attacks
|
||||||
|
- Public or private harassment
|
||||||
|
- Publishing others' private information, such as a physical or electronic
|
||||||
|
address, without explicit permission
|
||||||
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Our Responsibilities
|
||||||
|
|
||||||
|
Project maintainers are responsible for clarifying the standards of acceptable
|
||||||
|
behavior and are expected to take appropriate and fair corrective action in
|
||||||
|
response to any instances of unacceptable behavior.
|
||||||
|
|
||||||
|
Project maintainers have the right and responsibility to remove, edit, or
|
||||||
|
reject comments, commits, code, wiki edits, issues, and other contributions
|
||||||
|
that are not aligned to this Code of Conduct, or to ban temporarily or
|
||||||
|
permanently any contributor for other behaviors that they deem inappropriate,
|
||||||
|
threatening, offensive, or harmful.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies both within project spaces and in public spaces
|
||||||
|
when an individual is representing the project or its community. Examples of
|
||||||
|
representing a project or community include using an official project e-mail
|
||||||
|
address, posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event. Representation of a project may be
|
||||||
|
further defined and clarified by project maintainers.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported by contacting the project team at chenkaidev@gmail.com. All
|
||||||
|
complaints will be reviewed and investigated and will result in a response that
|
||||||
|
is deemed necessary and appropriate to the circumstances. The project team is
|
||||||
|
obligated to maintain confidentiality with regard to the reporter of an incident.
|
||||||
|
Further details of specific enforcement policies may be posted separately.
|
||||||
|
|
||||||
|
Project maintainers who do not follow or enforce the Code of Conduct in good
|
||||||
|
faith may face temporary or permanent repercussions as determined by other
|
||||||
|
members of the project's leadership.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
||||||
|
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see
|
||||||
|
https://www.contributor-covenant.org/faq
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
58
prediction/image/mx15hdi/Detect/mmsegmentation/.github/CONTRIBUTING.md
vendored
Normal file
58
prediction/image/mx15hdi/Detect/mmsegmentation/.github/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
# Contributing to mmsegmentation
|
||||||
|
|
||||||
|
All kinds of contributions are welcome, including but not limited to the following.
|
||||||
|
|
||||||
|
- Fixes (typo, bugs)
|
||||||
|
- New features and components
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
1. fork and pull the latest mmsegmentation
|
||||||
|
2. checkout a new branch (do not use master branch for PRs)
|
||||||
|
3. commit your changes
|
||||||
|
4. create a PR
|
||||||
|
|
||||||
|
:::{note}
|
||||||
|
|
||||||
|
- If you plan to add some new features that involve large changes, it is encouraged to open an issue for discussion first.
|
||||||
|
- If you are the author of some papers and would like to include your method to mmsegmentation,
|
||||||
|
please contact Kai Chen (chenkaidev\[at\]gmail\[dot\]com). We will much appreciate your contribution.
|
||||||
|
:::
|
||||||
|
|
||||||
|
## Code style
|
||||||
|
|
||||||
|
### Python
|
||||||
|
|
||||||
|
We adopt [PEP8](https://www.python.org/dev/peps/pep-0008/) as the preferred code style.
|
||||||
|
|
||||||
|
We use the following tools for linting and formatting:
|
||||||
|
|
||||||
|
- [flake8](http://flake8.pycqa.org/en/latest/): linter
|
||||||
|
- [yapf](https://github.com/google/yapf): formatter
|
||||||
|
- [isort](https://github.com/timothycrosley/isort): sort imports
|
||||||
|
|
||||||
|
Style configurations of yapf and isort can be found in [setup.cfg](../setup.cfg) and [.isort.cfg](../.isort.cfg).
|
||||||
|
|
||||||
|
We use [pre-commit hook](https://pre-commit.com/) that checks and formats for `flake8`, `yapf`, `isort`, `trailing whitespaces`,
|
||||||
|
fixes `end-of-files`, sorts `requirments.txt` automatically on every commit.
|
||||||
|
The config for a pre-commit hook is stored in [.pre-commit-config](../.pre-commit-config.yaml).
|
||||||
|
|
||||||
|
After you clone the repository, you will need to install initialize pre-commit hook.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pip install -U pre-commit
|
||||||
|
```
|
||||||
|
|
||||||
|
From the repository folder
|
||||||
|
|
||||||
|
```shell
|
||||||
|
pre-commit install
|
||||||
|
```
|
||||||
|
|
||||||
|
After this on every commit check code linters and formatter will be enforced.
|
||||||
|
|
||||||
|
> Before you create a PR, make sure that your code lints and is formatted by yapf.
|
||||||
|
|
||||||
|
### C++ and CUDA
|
||||||
|
|
||||||
|
We follow the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html).
|
||||||
6
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
6
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
blank_issues_enabled: false
|
||||||
|
|
||||||
|
contact_links:
|
||||||
|
- name: MMSegmentation Documentation
|
||||||
|
url: https://mmsegmentation.readthedocs.io
|
||||||
|
about: Check the docs and FAQ to see if you question is already answered.
|
||||||
48
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/error-report.md
vendored
Normal file
48
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/error-report.md
vendored
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
---
|
||||||
|
name: Error report
|
||||||
|
about: Create a report to help us improve
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
Thanks for your error report and we appreciate it a lot.
|
||||||
|
|
||||||
|
**Checklist**
|
||||||
|
|
||||||
|
1. I have searched related issues but cannot get the expected help.
|
||||||
|
2. The bug has not been fixed in the latest version.
|
||||||
|
|
||||||
|
**Describe the bug**
|
||||||
|
A clear and concise description of what the bug is.
|
||||||
|
|
||||||
|
**Reproduction**
|
||||||
|
|
||||||
|
1. What command or script did you run?
|
||||||
|
|
||||||
|
```none
|
||||||
|
A placeholder for the command.
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Did you make any modifications on the code or config? Did you understand what you have modified?
|
||||||
|
|
||||||
|
3. What dataset did you use?
|
||||||
|
|
||||||
|
**Environment**
|
||||||
|
|
||||||
|
1. Please run `python mmseg/utils/collect_env.py` to collect necessary environment information and paste it here.
|
||||||
|
2. You may add addition that may be helpful for locating the problem, such as
|
||||||
|
- How you installed PyTorch \[e.g., pip, conda, source\]
|
||||||
|
- Other environment variables that may be related (such as `$PATH`, `$LD_LIBRARY_PATH`, `$PYTHONPATH`, etc.)
|
||||||
|
|
||||||
|
**Error traceback**
|
||||||
|
|
||||||
|
If applicable, paste the error trackback here.
|
||||||
|
|
||||||
|
```none
|
||||||
|
A placeholder for trackback.
|
||||||
|
```
|
||||||
|
|
||||||
|
**Bug fix**
|
||||||
|
|
||||||
|
If you have already identified the reason, you can provide the information here. If you are willing to create a PR to fix it, please also leave a comment here and that would be much appreciated!
|
||||||
21
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
21
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
---
|
||||||
|
name: Feature request
|
||||||
|
about: Suggest an idea for this project
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
# Describe the feature
|
||||||
|
|
||||||
|
**Motivation**
|
||||||
|
A clear and concise description of the motivation of the feature.
|
||||||
|
Ex1. It is inconvenient when \[....\].
|
||||||
|
Ex2. There is a recent paper \[....\], which is very helpful for \[....\].
|
||||||
|
|
||||||
|
**Related resources**
|
||||||
|
If there is an official code release or third-party implementations, please also provide the information here, which would be very helpful.
|
||||||
|
|
||||||
|
**Additional context**
|
||||||
|
Add any other context or screenshots about the feature request here.
|
||||||
|
If you would like to implement the feature and create a PR, please leave a comment here and that would be much appreciated.
|
||||||
7
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/general_questions.md
vendored
Normal file
7
prediction/image/mx15hdi/Detect/mmsegmentation/.github/ISSUE_TEMPLATE/general_questions.md
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
name: General questions
|
||||||
|
about: Ask general questions to get help
|
||||||
|
title: ''
|
||||||
|
labels: ''
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
---
|
||||||
|
name: Reimplementation Questions
|
||||||
|
about: Ask about questions during model reimplementation
|
||||||
|
title: ''
|
||||||
|
labels: reimplementation
|
||||||
|
assignees: ''
|
||||||
|
---
|
||||||
|
|
||||||
|
If you feel we have helped you, give us a STAR! :satisfied:
|
||||||
|
|
||||||
|
**Notice**
|
||||||
|
|
||||||
|
There are several common situations in the reimplementation issues as below
|
||||||
|
|
||||||
|
1. Reimplement a model in the model zoo using the provided configs
|
||||||
|
2. Reimplement a model in the model zoo on other datasets (e.g., custom datasets)
|
||||||
|
3. Reimplement a custom model but all the components are implemented in MMSegmentation
|
||||||
|
4. Reimplement a custom model with new modules implemented by yourself
|
||||||
|
|
||||||
|
There are several things to do for different cases as below.
|
||||||
|
|
||||||
|
- For cases 1 & 3, please follow the steps in the following sections thus we could help to quickly identify the issue.
|
||||||
|
- For cases 2 & 4, please understand that we are not able to do much help here because we usually do not know the full code, and the users should be responsible for the code they write.
|
||||||
|
- One suggestion for cases 2 & 4 is that the users should first check whether the bug lies in the self-implemented code or the original code. For example, users can first make sure that the same model runs well on supported datasets. If you still need help, please describe what you have done and what you obtain in the issue, and follow the steps in the following sections, and try as clear as possible so that we can better help you.
|
||||||
|
|
||||||
|
**Checklist**
|
||||||
|
|
||||||
|
1. I have searched related issues but cannot get the expected help.
|
||||||
|
2. The issue has not been fixed in the latest version.
|
||||||
|
|
||||||
|
**Describe the issue**
|
||||||
|
|
||||||
|
A clear and concise description of the problem you meet and what you have done.
|
||||||
|
|
||||||
|
**Reproduction**
|
||||||
|
|
||||||
|
1. What command or script did you run?
|
||||||
|
|
||||||
|
```
|
||||||
|
A placeholder for the command.
|
||||||
|
```
|
||||||
|
|
||||||
|
2. What config dir you run?
|
||||||
|
|
||||||
|
```
|
||||||
|
A placeholder for the config.
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Did you make any modifications to the code or config? Did you understand what you have modified?
|
||||||
|
4. What dataset did you use?
|
||||||
|
|
||||||
|
**Environment**
|
||||||
|
|
||||||
|
1. Please run `PYTHONPATH=${PWD}:$PYTHONPATH python mmseg/utils/collect_env.py` to collect the necessary environment information and paste it here.
|
||||||
|
2. You may add an addition that may be helpful for locating the problem, such as
|
||||||
|
1. How you installed PyTorch \[e.g., pip, conda, source\]
|
||||||
|
2. Other environment variables that may be related (such as `$PATH`, `$LD_LIBRARY_PATH`, `$PYTHONPATH`, etc.)
|
||||||
|
|
||||||
|
**Results**
|
||||||
|
|
||||||
|
If applicable, paste the related results here, e.g., what you expect and what you get.
|
||||||
|
|
||||||
|
```
|
||||||
|
A placeholder for results comparison
|
||||||
|
```
|
||||||
|
|
||||||
|
**Issue fix**
|
||||||
|
|
||||||
|
If you have already identified the reason, you can provide the information here. If you are willing to create a PR to fix it, please also leave a comment here and that would be much appreciated!
|
||||||
25
prediction/image/mx15hdi/Detect/mmsegmentation/.github/pull_request_template.md
vendored
Normal file
25
prediction/image/mx15hdi/Detect/mmsegmentation/.github/pull_request_template.md
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
Thanks for your contribution and we appreciate it a lot. The following instructions would make your pull request more healthy and more easily get feedback. If you do not understand some items, don't worry, just make the pull request and seek help from maintainers.
|
||||||
|
|
||||||
|
## Motivation
|
||||||
|
|
||||||
|
Please describe the motivation of this PR and the goal you want to achieve through this PR.
|
||||||
|
|
||||||
|
## Modification
|
||||||
|
|
||||||
|
Please briefly describe what modification is made in this PR.
|
||||||
|
|
||||||
|
## BC-breaking (Optional)
|
||||||
|
|
||||||
|
Does the modification introduce changes that break the backward-compatibility of the downstream repos?
|
||||||
|
If so, please describe how it breaks the compatibility and how the downstream projects should modify their code to keep compatibility with this PR.
|
||||||
|
|
||||||
|
## Use cases (Optional)
|
||||||
|
|
||||||
|
If this PR introduces a new feature, it is better to list some use cases here, and update the documentation.
|
||||||
|
|
||||||
|
## Checklist
|
||||||
|
|
||||||
|
1. Pre-commit or other linting tools are used to fix the potential lint issues.
|
||||||
|
2. The modification is covered by complete unit tests. If not, please add more unit test to ensure the correctness.
|
||||||
|
3. If the modification has potential influence on downstream projects, this PR should be tested with downstream projects, like MMDet or MMDet3D.
|
||||||
|
4. The documentation has been modified accordingly, like docstring or example tutorials.
|
||||||
257
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/build.yml
vendored
Normal file
257
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/build.yml
vendored
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
name: build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths-ignore:
|
||||||
|
- 'demo/**'
|
||||||
|
- '.dev/**'
|
||||||
|
- 'docker/**'
|
||||||
|
- 'tools/**'
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- 'demo/**'
|
||||||
|
- '.dev/**'
|
||||||
|
- 'docker/**'
|
||||||
|
- 'tools/**'
|
||||||
|
- 'docs/**'
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_cpu:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.7]
|
||||||
|
torch: [1.5.1, 1.6.0, 1.7.0, 1.8.0, 1.9.0]
|
||||||
|
include:
|
||||||
|
- torch: 1.5.1
|
||||||
|
torch_version: torch1.5
|
||||||
|
torchvision: 0.6.1
|
||||||
|
- torch: 1.6.0
|
||||||
|
torch_version: torch1.6
|
||||||
|
torchvision: 0.7.0
|
||||||
|
- torch: 1.7.0
|
||||||
|
torch_version: torch1.7
|
||||||
|
torchvision: 0.8.1
|
||||||
|
- torch: 1.8.0
|
||||||
|
torch_version: torch1.8
|
||||||
|
torchvision: 0.9.0
|
||||||
|
- torch: 1.9.0
|
||||||
|
torch_version: torch1.9
|
||||||
|
torchvision: 0.10.0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Upgrade pip
|
||||||
|
run: pip install pip --upgrade
|
||||||
|
- name: Install PyTorch
|
||||||
|
run: pip install torch==${{matrix.torch}}+cpu torchvision==${{matrix.torchvision}}+cpu -f https://download.pytorch.org/whl/torch_stable.html
|
||||||
|
- name: Install MMCV
|
||||||
|
run: |
|
||||||
|
pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cpu/${{matrix.torch_version}}/index.html
|
||||||
|
python -c 'import mmcv; print(mmcv.__version__)'
|
||||||
|
- name: Install unittest dependencies
|
||||||
|
run: |
|
||||||
|
pip install -r requirements.txt
|
||||||
|
- name: Build and install
|
||||||
|
run: rm -rf .eggs && pip install -e .
|
||||||
|
- name: Run unittests and generate coverage report
|
||||||
|
run: |
|
||||||
|
pip install timm
|
||||||
|
coverage run --branch --source mmseg -m pytest tests/
|
||||||
|
coverage xml
|
||||||
|
coverage report -m
|
||||||
|
if: ${{matrix.torch >= '1.5.0'}}
|
||||||
|
- name: Skip timm unittests and generate coverage report
|
||||||
|
run: |
|
||||||
|
coverage run --branch --source mmseg -m pytest tests/ --ignore tests/test_models/test_backbones/test_timm_backbone.py
|
||||||
|
coverage xml
|
||||||
|
coverage report -m
|
||||||
|
if: ${{matrix.torch < '1.5.0'}}
|
||||||
|
|
||||||
|
build_cuda101:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
container:
|
||||||
|
image: pytorch/pytorch:1.6.0-cuda10.1-cudnn7-devel
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.7]
|
||||||
|
torch:
|
||||||
|
[
|
||||||
|
1.5.1+cu101,
|
||||||
|
1.6.0+cu101,
|
||||||
|
1.7.0+cu101,
|
||||||
|
1.8.0+cu101
|
||||||
|
]
|
||||||
|
include:
|
||||||
|
- torch: 1.5.1+cu101
|
||||||
|
torch_version: torch1.5
|
||||||
|
torchvision: 0.6.1+cu101
|
||||||
|
- torch: 1.6.0+cu101
|
||||||
|
torch_version: torch1.6
|
||||||
|
torchvision: 0.7.0+cu101
|
||||||
|
- torch: 1.7.0+cu101
|
||||||
|
torch_version: torch1.7
|
||||||
|
torchvision: 0.8.1+cu101
|
||||||
|
- torch: 1.8.0+cu101
|
||||||
|
torch_version: torch1.8
|
||||||
|
torchvision: 0.9.0+cu101
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Fetch GPG keys
|
||||||
|
run: |
|
||||||
|
apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub
|
||||||
|
apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
apt-get update && apt-get install -y libgl1-mesa-glx ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6 python${{matrix.python-version}}-dev
|
||||||
|
apt-get clean
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
- name: Install Pillow
|
||||||
|
run: python -m pip install Pillow==6.2.2
|
||||||
|
if: ${{matrix.torchvision < 0.5}}
|
||||||
|
- name: Install PyTorch
|
||||||
|
run: python -m pip install torch==${{matrix.torch}} torchvision==${{matrix.torchvision}} -f https://download.pytorch.org/whl/torch_stable.html
|
||||||
|
- name: Install mmseg dependencies
|
||||||
|
run: |
|
||||||
|
python -V
|
||||||
|
python -m pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu101/${{matrix.torch_version}}/index.html
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
python -c 'import mmcv; print(mmcv.__version__)'
|
||||||
|
- name: Build and install
|
||||||
|
run: |
|
||||||
|
rm -rf .eggs
|
||||||
|
python setup.py check -m -s
|
||||||
|
TORCH_CUDA_ARCH_LIST=7.0 pip install .
|
||||||
|
- name: Run unittests and generate coverage report
|
||||||
|
run: |
|
||||||
|
python -m pip install timm
|
||||||
|
coverage run --branch --source mmseg -m pytest tests/
|
||||||
|
coverage xml
|
||||||
|
coverage report -m
|
||||||
|
if: ${{matrix.torch >= '1.5.0'}}
|
||||||
|
- name: Skip timm unittests and generate coverage report
|
||||||
|
run: |
|
||||||
|
coverage run --branch --source mmseg -m pytest tests/ --ignore tests/test_models/test_backbones/test_timm_backbone.py
|
||||||
|
coverage xml
|
||||||
|
coverage report -m
|
||||||
|
if: ${{matrix.torch < '1.5.0'}}
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v1.0.10
|
||||||
|
with:
|
||||||
|
file: ./coverage.xml
|
||||||
|
flags: unittests
|
||||||
|
env_vars: OS,PYTHON
|
||||||
|
name: codecov-umbrella
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
build_cuda102:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
container:
|
||||||
|
image: pytorch/pytorch:1.9.0-cuda10.2-cudnn7-devel
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.6, 3.7, 3.8, 3.9]
|
||||||
|
torch: [1.9.0+cu102]
|
||||||
|
include:
|
||||||
|
- torch: 1.9.0+cu102
|
||||||
|
torch_version: torch1.9
|
||||||
|
torchvision: 0.10.0+cu102
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Fetch GPG keys
|
||||||
|
run: |
|
||||||
|
apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu1804/x86_64/3bf863cc.pub
|
||||||
|
apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/machine-learning/repos/ubuntu1804/x86_64/7fa2af80.pub
|
||||||
|
- name: Install system dependencies
|
||||||
|
run: |
|
||||||
|
apt-get update && apt-get install -y libgl1-mesa-glx ffmpeg libsm6 libxext6 git ninja-build libglib2.0-0 libsm6 libxrender-dev libxext6
|
||||||
|
apt-get clean
|
||||||
|
rm -rf /var/lib/apt/lists/*
|
||||||
|
- name: Install Pillow
|
||||||
|
run: python -m pip install Pillow==6.2.2
|
||||||
|
if: ${{matrix.torchvision < 0.5}}
|
||||||
|
- name: Install PyTorch
|
||||||
|
run: python -m pip install torch==${{matrix.torch}} torchvision==${{matrix.torchvision}} -f https://download.pytorch.org/whl/torch_stable.html
|
||||||
|
- name: Install mmseg dependencies
|
||||||
|
run: |
|
||||||
|
python -V
|
||||||
|
python -m pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cu102/${{matrix.torch_version}}/index.html
|
||||||
|
python -m pip install -r requirements.txt
|
||||||
|
python -c 'import mmcv; print(mmcv.__version__)'
|
||||||
|
- name: Build and install
|
||||||
|
run: |
|
||||||
|
rm -rf .eggs
|
||||||
|
python setup.py check -m -s
|
||||||
|
TORCH_CUDA_ARCH_LIST=7.0 pip install .
|
||||||
|
- name: Run unittests and generate coverage report
|
||||||
|
run: |
|
||||||
|
python -m pip install timm
|
||||||
|
coverage run --branch --source mmseg -m pytest tests/
|
||||||
|
coverage xml
|
||||||
|
coverage report -m
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v2
|
||||||
|
with:
|
||||||
|
files: ./coverage.xml
|
||||||
|
flags: unittests
|
||||||
|
env_vars: OS,PYTHON
|
||||||
|
name: codecov-umbrella
|
||||||
|
fail_ci_if_error: false
|
||||||
|
|
||||||
|
test_windows:
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
os: [windows-2022]
|
||||||
|
python: [3.8]
|
||||||
|
platform: [cpu, cu111]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python }}
|
||||||
|
- name: Upgrade pip
|
||||||
|
run: python -m pip install pip --upgrade --user
|
||||||
|
- name: Install OpenCV
|
||||||
|
run: pip install opencv-python>=3
|
||||||
|
- name: Install PyTorch
|
||||||
|
# As a complement to Linux CI, we test on PyTorch LTS version
|
||||||
|
run: pip install torch==1.8.2+${{ matrix.platform }} torchvision==0.9.2+${{ matrix.platform }} -f https://download.pytorch.org/whl/lts/1.8/torch_lts.html
|
||||||
|
- name: Install MMCV
|
||||||
|
run: |
|
||||||
|
pip install mmcv-full -f https://download.openmmlab.com/mmcv/dist/cpu/torch1.8/index.html --only-binary mmcv-full
|
||||||
|
- name: Install unittest dependencies
|
||||||
|
run: pip install -r requirements/tests.txt -r requirements/optional.txt
|
||||||
|
- name: Build and install
|
||||||
|
run: pip install -e .
|
||||||
|
- name: Run unittests
|
||||||
|
run: |
|
||||||
|
python -m pip install timm
|
||||||
|
coverage run --branch --source mmseg -m pytest tests/
|
||||||
|
- name: Generate coverage report
|
||||||
|
run: |
|
||||||
|
coverage xml
|
||||||
|
coverage report -m
|
||||||
26
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/deploy.yml
vendored
Normal file
26
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/deploy.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
name: deploy
|
||||||
|
|
||||||
|
on: push
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-n-publish:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: startsWith(github.event.ref, 'refs/tags')
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.7
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
- name: Build MMSegmentation
|
||||||
|
run: |
|
||||||
|
pip install wheel
|
||||||
|
python setup.py sdist bdist_wheel
|
||||||
|
- name: Publish distribution to PyPI
|
||||||
|
run: |
|
||||||
|
pip install twine
|
||||||
|
twine upload dist/* -u __token__ -p ${{ secrets.pypi_password }}
|
||||||
28
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/lint.yml
vendored
Normal file
28
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/lint.yml
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
name: lint
|
||||||
|
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
lint:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python 3.7
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: 3.7
|
||||||
|
- name: Install pre-commit hook
|
||||||
|
run: |
|
||||||
|
pip install pre-commit
|
||||||
|
pre-commit install
|
||||||
|
- name: Linting
|
||||||
|
run: |
|
||||||
|
pre-commit run --all-files
|
||||||
|
- name: Check docstring coverage
|
||||||
|
run: |
|
||||||
|
pip install interrogate
|
||||||
|
interrogate -v --ignore-init-method --ignore-module --ignore-nested-functions --exclude mmseg/ops --ignore-regex "__repr__" --fail-under 80 mmseg
|
||||||
44
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/test_mim.yml
vendored
Normal file
44
prediction/image/mx15hdi/Detect/mmsegmentation/.github/workflows/test_mim.yml
vendored
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
name: test-mim
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'model-index.yml'
|
||||||
|
- 'configs/**'
|
||||||
|
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- 'model-index.yml'
|
||||||
|
- 'configs/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build_cpu:
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
python-version: [3.7]
|
||||||
|
torch: [1.8.0]
|
||||||
|
include:
|
||||||
|
- torch: 1.8.0
|
||||||
|
torch_version: torch1.8
|
||||||
|
torchvision: 0.9.0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Set up Python ${{ matrix.python-version }}
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.python-version }}
|
||||||
|
- name: Upgrade pip
|
||||||
|
run: pip install pip --upgrade
|
||||||
|
- name: Install PyTorch
|
||||||
|
run: pip install torch==${{matrix.torch}}+cpu torchvision==${{matrix.torchvision}}+cpu -f https://download.pytorch.org/whl/torch_stable.html
|
||||||
|
- name: Install openmim
|
||||||
|
run: pip install openmim
|
||||||
|
- name: Build and install
|
||||||
|
run: rm -rf .eggs && mim install -e .
|
||||||
|
- name: test commands of mim
|
||||||
|
run: mim search mmsegmentation
|
||||||
120
prediction/image/mx15hdi/Detect/mmsegmentation/.gitignore
vendored
Normal file
120
prediction/image/mx15hdi/Detect/mmsegmentation/.gitignore
vendored
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/en/_build/
|
||||||
|
docs/zh_cn/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
|
||||||
|
data
|
||||||
|
.vscode
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# custom
|
||||||
|
*.pkl
|
||||||
|
*.pkl.json
|
||||||
|
*.log.json
|
||||||
|
work_dirs/
|
||||||
|
mmseg/.mim
|
||||||
|
|
||||||
|
# Pytorch
|
||||||
|
*.pth
|
||||||
11
prediction/image/mx15hdi/Detect/mmsegmentation/.owners.yml
Normal file
11
prediction/image/mx15hdi/Detect/mmsegmentation/.owners.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
assign:
|
||||||
|
strategy:
|
||||||
|
# random
|
||||||
|
# round-robin
|
||||||
|
daily-shift-based
|
||||||
|
assignees:
|
||||||
|
- MengzhangLI
|
||||||
|
- xiexinch
|
||||||
|
- MeowZheng
|
||||||
|
- MengzhangLI
|
||||||
|
- xiexinch
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
repos:
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8.git
|
||||||
|
rev: 3.8.3
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
- repo: https://github.com/PyCQA/isort
|
||||||
|
rev: 5.10.1
|
||||||
|
hooks:
|
||||||
|
- id: isort
|
||||||
|
- repo: https://github.com/pre-commit/mirrors-yapf
|
||||||
|
rev: v0.30.0
|
||||||
|
hooks:
|
||||||
|
- id: yapf
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.1.0
|
||||||
|
hooks:
|
||||||
|
- id: trailing-whitespace
|
||||||
|
- id: check-yaml
|
||||||
|
- id: end-of-file-fixer
|
||||||
|
- id: requirements-txt-fixer
|
||||||
|
- id: double-quote-string-fixer
|
||||||
|
- id: check-merge-conflict
|
||||||
|
- id: fix-encoding-pragma
|
||||||
|
args: ["--remove"]
|
||||||
|
- id: mixed-line-ending
|
||||||
|
args: ["--fix=lf"]
|
||||||
|
- repo: https://github.com/executablebooks/mdformat
|
||||||
|
rev: 0.7.9
|
||||||
|
hooks:
|
||||||
|
- id: mdformat
|
||||||
|
args: ["--number"]
|
||||||
|
additional_dependencies:
|
||||||
|
- mdformat-openmmlab
|
||||||
|
- mdformat_frontmatter
|
||||||
|
- linkify-it-py
|
||||||
|
- repo: https://github.com/codespell-project/codespell
|
||||||
|
rev: v2.1.0
|
||||||
|
hooks:
|
||||||
|
- id: codespell
|
||||||
|
- repo: https://github.com/myint/docformatter
|
||||||
|
rev: v1.3.1
|
||||||
|
hooks:
|
||||||
|
- id: docformatter
|
||||||
|
args: ["--in-place", "--wrap-descriptions", "79"]
|
||||||
|
- repo: local
|
||||||
|
hooks:
|
||||||
|
- id: update-model-index
|
||||||
|
name: update-model-index
|
||||||
|
description: Collect model information and update model-index.yml
|
||||||
|
entry: .dev/md2yml.py
|
||||||
|
additional_dependencies: [mmcv, lxml, opencv-python]
|
||||||
|
language: python
|
||||||
|
files: ^configs/.*\.md$
|
||||||
|
require_serial: true
|
||||||
|
- repo: https://github.com/open-mmlab/pre-commit-hooks
|
||||||
|
rev: v0.2.0 # Use the rev to fix revision
|
||||||
|
hooks:
|
||||||
|
- id: check-algo-readme
|
||||||
|
- id: check-copyright
|
||||||
|
args: ["mmseg", "tools", "tests", "demo"] # the dir_to_check with expected directory to check
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
formats: all
|
||||||
|
|
||||||
|
python:
|
||||||
|
version: 3.7
|
||||||
|
install:
|
||||||
|
- requirements: requirements/docs.txt
|
||||||
|
- requirements: requirements/readthedocs.txt
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
cff-version: 1.2.0
|
||||||
|
message: "If you use this software, please cite it as below."
|
||||||
|
authors:
|
||||||
|
- name: "MMSegmentation Contributors"
|
||||||
|
title: "OpenMMLab Semantic Segmentation Toolbox and Benchmark"
|
||||||
|
date-released: 2020-07-10
|
||||||
|
url: "https://github.com/open-mmlab/mmsegmentation"
|
||||||
|
license: Apache-2.0
|
||||||
203
prediction/image/mx15hdi/Detect/mmsegmentation/LICENSE
Normal file
203
prediction/image/mx15hdi/Detect/mmsegmentation/LICENSE
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
Copyright 2020 The MMSegmentation Authors. All rights reserved.
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2020 The MMSegmentation Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
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.
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
# Licenses for special features
|
||||||
|
|
||||||
|
In this file, we list the features with other licenses instead of Apache 2.0. Users should be careful about adopting these features in any commercial matters.
|
||||||
|
|
||||||
|
| Feature | Files | License |
|
||||||
|
| :-------: | :-------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------: |
|
||||||
|
| SegFormer | [mmseg/models/decode_heads/segformer_head.py](https://github.com/open-mmlab/mmsegmentation/blob/master/mmseg/models/decode_heads/segformer_head.py) | [NVIDIA License](https://github.com/NVlabs/SegFormer#license) |
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
include requirements/*.txt
|
||||||
|
include mmseg/.mim/model-index.yml
|
||||||
|
recursive-include mmseg/.mim/configs *.py *.yml
|
||||||
|
recursive-include mmseg/.mim/tools *.py *.sh
|
||||||
229
prediction/image/mx15hdi/Detect/mmsegmentation/README.md
Normal file
229
prediction/image/mx15hdi/Detect/mmsegmentation/README.md
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="resources/mmseg-logo.png" width="600"/>
|
||||||
|
<div> </div>
|
||||||
|
<div align="center">
|
||||||
|
<b><font size="5">OpenMMLab website</font></b>
|
||||||
|
<sup>
|
||||||
|
<a href="https://openmmlab.com">
|
||||||
|
<i><font size="4">HOT</font></i>
|
||||||
|
</a>
|
||||||
|
</sup>
|
||||||
|
|
||||||
|
<b><font size="5">OpenMMLab platform</font></b>
|
||||||
|
<sup>
|
||||||
|
<a href="https://platform.openmmlab.com">
|
||||||
|
<i><font size="4">TRY IT OUT</font></i>
|
||||||
|
</a>
|
||||||
|
</sup>
|
||||||
|
</div>
|
||||||
|
<div> </div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
[](https://pypi.org/project/mmsegmentation/)
|
||||||
|
[](https://pypi.org/project/mmsegmentation)
|
||||||
|
[](https://mmsegmentation.readthedocs.io/en/latest/)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/actions)
|
||||||
|
[](https://codecov.io/gh/open-mmlab/mmsegmentation)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/blob/master/LICENSE)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/issues)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/issues)
|
||||||
|
|
||||||
|
[📘Documentation](https://mmsegmentation.readthedocs.io/en/latest/) |
|
||||||
|
[🛠️Installation](https://mmsegmentation.readthedocs.io/en/latest/get_started.html) |
|
||||||
|
[👀Model Zoo](https://mmsegmentation.readthedocs.io/en/latest/model_zoo.html) |
|
||||||
|
[🆕Update News](https://mmsegmentation.readthedocs.io/en/latest/changelog.html) |
|
||||||
|
[🤔Reporting Issues](https://github.com/open-mmlab/mmsegmentation/issues/new/choose)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
|
||||||
|
English | [简体中文](README_zh-CN.md)
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## Introduction
|
||||||
|
|
||||||
|
MMSegmentation is an open source semantic segmentation toolbox based on PyTorch.
|
||||||
|
It is a part of the [OpenMMLab](https://openmmlab.com/) project.
|
||||||
|
|
||||||
|
The master branch works with **PyTorch 1.5+**.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary>Major features</summary>
|
||||||
|
|
||||||
|
- **Unified Benchmark**
|
||||||
|
|
||||||
|
We provide a unified benchmark toolbox for various semantic segmentation methods.
|
||||||
|
|
||||||
|
- **Modular Design**
|
||||||
|
|
||||||
|
We decompose the semantic segmentation framework into different components and one can easily construct a customized semantic segmentation framework by combining different modules.
|
||||||
|
|
||||||
|
- **Support of multiple methods out of box**
|
||||||
|
|
||||||
|
The toolbox directly supports popular and contemporary semantic segmentation frameworks, *e.g.* PSPNet, DeepLabV3, PSANet, DeepLabV3+, etc.
|
||||||
|
|
||||||
|
- **High efficiency**
|
||||||
|
|
||||||
|
The training speed is faster than or comparable to other codebases.
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## What's New
|
||||||
|
|
||||||
|
v0.25.0 was released in 6/2/2022:
|
||||||
|
|
||||||
|
- Support PyTorch backend on MLU
|
||||||
|
|
||||||
|
Please refer to [changelog.md](docs/en/changelog.md) for details and release history.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Please refer to [get_started.md](docs/en/get_started.md#installation) for installation and [dataset_prepare.md](docs/en/dataset_prepare.md#prepare-datasets) for dataset preparation.
|
||||||
|
|
||||||
|
## Get Started
|
||||||
|
|
||||||
|
Please see [train.md](docs/en/train.md) and [inference.md](docs/en/inference.md) for the basic usage of MMSegmentation.
|
||||||
|
There are also tutorials for:
|
||||||
|
|
||||||
|
- [customizing dataset](docs/en/tutorials/customize_datasets.md)
|
||||||
|
- [designing data pipeline](docs/en/tutorials/data_pipeline.md)
|
||||||
|
- [customizing modules](docs/en/tutorials/customize_models.md)
|
||||||
|
- [customizing runtime](docs/en/tutorials/customize_runtime.md)
|
||||||
|
- [training tricks](docs/en/tutorials/training_tricks.md)
|
||||||
|
- [useful tools](docs/en/useful_tools.md)
|
||||||
|
|
||||||
|
A Colab tutorial is also provided. You may preview the notebook [here](demo/MMSegmentation_Tutorial.ipynb) or directly [run](https://colab.research.google.com/github/open-mmlab/mmsegmentation/blob/master/demo/MMSegmentation_Tutorial.ipynb) on Colab.
|
||||||
|
|
||||||
|
## Benchmark and model zoo
|
||||||
|
|
||||||
|
Results and models are available in the [model zoo](docs/en/model_zoo.md).
|
||||||
|
|
||||||
|
Supported backbones:
|
||||||
|
|
||||||
|
- [x] ResNet (CVPR'2016)
|
||||||
|
- [x] ResNeXt (CVPR'2017)
|
||||||
|
- [x] [HRNet (CVPR'2019)](configs/hrnet)
|
||||||
|
- [x] [ResNeSt (ArXiv'2020)](configs/resnest)
|
||||||
|
- [x] [MobileNetV2 (CVPR'2018)](configs/mobilenet_v2)
|
||||||
|
- [x] [MobileNetV3 (ICCV'2019)](configs/mobilenet_v3)
|
||||||
|
- [x] [Vision Transformer (ICLR'2021)](configs/vit)
|
||||||
|
- [x] [Swin Transformer (ICCV'2021)](configs/swin)
|
||||||
|
- [x] [Twins (NeurIPS'2021)](configs/twins)
|
||||||
|
- [x] [BEiT (ICLR'2022)](configs/beit)
|
||||||
|
- [x] [ConvNeXt (CVPR'2022)](configs/convnext)
|
||||||
|
- [x] [MAE (CVPR'2022)](configs/mae)
|
||||||
|
|
||||||
|
Supported methods:
|
||||||
|
|
||||||
|
- [x] [FCN (CVPR'2015/TPAMI'2017)](configs/fcn)
|
||||||
|
- [x] [ERFNet (T-ITS'2017)](configs/erfnet)
|
||||||
|
- [x] [UNet (MICCAI'2016/Nat. Methods'2019)](configs/unet)
|
||||||
|
- [x] [PSPNet (CVPR'2017)](configs/pspnet)
|
||||||
|
- [x] [DeepLabV3 (ArXiv'2017)](configs/deeplabv3)
|
||||||
|
- [x] [BiSeNetV1 (ECCV'2018)](configs/bisenetv1)
|
||||||
|
- [x] [PSANet (ECCV'2018)](configs/psanet)
|
||||||
|
- [x] [DeepLabV3+ (CVPR'2018)](configs/deeplabv3plus)
|
||||||
|
- [x] [UPerNet (ECCV'2018)](configs/upernet)
|
||||||
|
- [x] [ICNet (ECCV'2018)](configs/icnet)
|
||||||
|
- [x] [NonLocal Net (CVPR'2018)](configs/nonlocal_net)
|
||||||
|
- [x] [EncNet (CVPR'2018)](configs/encnet)
|
||||||
|
- [x] [Semantic FPN (CVPR'2019)](configs/sem_fpn)
|
||||||
|
- [x] [DANet (CVPR'2019)](configs/danet)
|
||||||
|
- [x] [APCNet (CVPR'2019)](configs/apcnet)
|
||||||
|
- [x] [EMANet (ICCV'2019)](configs/emanet)
|
||||||
|
- [x] [CCNet (ICCV'2019)](configs/ccnet)
|
||||||
|
- [x] [DMNet (ICCV'2019)](configs/dmnet)
|
||||||
|
- [x] [ANN (ICCV'2019)](configs/ann)
|
||||||
|
- [x] [GCNet (ICCVW'2019/TPAMI'2020)](configs/gcnet)
|
||||||
|
- [x] [FastFCN (ArXiv'2019)](configs/fastfcn)
|
||||||
|
- [x] [Fast-SCNN (ArXiv'2019)](configs/fastscnn)
|
||||||
|
- [x] [ISANet (ArXiv'2019/IJCV'2021)](configs/isanet)
|
||||||
|
- [x] [OCRNet (ECCV'2020)](configs/ocrnet)
|
||||||
|
- [x] [DNLNet (ECCV'2020)](configs/dnlnet)
|
||||||
|
- [x] [PointRend (CVPR'2020)](configs/point_rend)
|
||||||
|
- [x] [CGNet (TIP'2020)](configs/cgnet)
|
||||||
|
- [x] [BiSeNetV2 (IJCV'2021)](configs/bisenetv2)
|
||||||
|
- [x] [STDC (CVPR'2021)](configs/stdc)
|
||||||
|
- [x] [SETR (CVPR'2021)](configs/setr)
|
||||||
|
- [x] [DPT (ArXiv'2021)](configs/dpt)
|
||||||
|
- [x] [Segmenter (ICCV'2021)](configs/segmenter)
|
||||||
|
- [x] [SegFormer (NeurIPS'2021)](configs/segformer)
|
||||||
|
- [x] [K-Net (NeurIPS'2021)](configs/knet)
|
||||||
|
|
||||||
|
Supported datasets:
|
||||||
|
|
||||||
|
- [x] [Cityscapes](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#cityscapes)
|
||||||
|
- [x] [PASCAL VOC](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#pascal-voc)
|
||||||
|
- [x] [ADE20K](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#ade20k)
|
||||||
|
- [x] [Pascal Context](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#pascal-context)
|
||||||
|
- [x] [COCO-Stuff 10k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#coco-stuff-10k)
|
||||||
|
- [x] [COCO-Stuff 164k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#coco-stuff-164k)
|
||||||
|
- [x] [CHASE_DB1](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#chase-db1)
|
||||||
|
- [x] [DRIVE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#drive)
|
||||||
|
- [x] [HRF](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#hrf)
|
||||||
|
- [x] [STARE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#stare)
|
||||||
|
- [x] [Dark Zurich](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#dark-zurich)
|
||||||
|
- [x] [Nighttime Driving](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#nighttime-driving)
|
||||||
|
- [x] [LoveDA](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#loveda)
|
||||||
|
- [x] [Potsdam](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#isprs-potsdam)
|
||||||
|
- [x] [Vaihingen](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#isprs-vaihingen)
|
||||||
|
- [x] [iSAID](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/en/dataset_prepare.md#isaid)
|
||||||
|
|
||||||
|
## FAQ
|
||||||
|
|
||||||
|
Please refer to [FAQ](docs/en/faq.md) for frequently asked questions.
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
We appreciate all contributions to improve MMSegmentation. Please refer to [CONTRIBUTING.md](.github/CONTRIBUTING.md) for the contributing guideline.
|
||||||
|
|
||||||
|
## Acknowledgement
|
||||||
|
|
||||||
|
MMSegmentation is an open source project that welcome any contribution and feedback.
|
||||||
|
We wish that the toolbox and benchmark could serve the growing research
|
||||||
|
community by providing a flexible as well as standardized toolkit to reimplement existing methods
|
||||||
|
and develop their own new semantic segmentation methods.
|
||||||
|
|
||||||
|
## Citation
|
||||||
|
|
||||||
|
If you find this project useful in your research, please consider cite:
|
||||||
|
|
||||||
|
```bibtex
|
||||||
|
@misc{mmseg2020,
|
||||||
|
title={{MMSegmentation}: OpenMMLab Semantic Segmentation Toolbox and Benchmark},
|
||||||
|
author={MMSegmentation Contributors},
|
||||||
|
howpublished = {\url{https://github.com/open-mmlab/mmsegmentation}},
|
||||||
|
year={2020}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MMSegmentation is released under the Apache 2.0 license, while some specific features in this library are with other licenses. Please refer to [LICENSES.md](LICENSES.md) for the careful check, if you are using our code for commercial matters.
|
||||||
|
|
||||||
|
## Projects in OpenMMLab
|
||||||
|
|
||||||
|
- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab foundational library for computer vision.
|
||||||
|
- [MIM](https://github.com/open-mmlab/mim): MIM installs OpenMMLab packages.
|
||||||
|
- [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab image classification toolbox and benchmark.
|
||||||
|
- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab detection toolbox and benchmark.
|
||||||
|
- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab's next-generation platform for general 3D object detection.
|
||||||
|
- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab rotated object detection toolbox and benchmark.
|
||||||
|
- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab semantic segmentation toolbox and benchmark.
|
||||||
|
- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab text detection, recognition, and understanding toolbox.
|
||||||
|
- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab pose estimation toolbox and benchmark.
|
||||||
|
- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 3D human parametric model toolbox and benchmark.
|
||||||
|
- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab self-supervised learning toolbox and benchmark.
|
||||||
|
- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab model compression toolbox and benchmark.
|
||||||
|
- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab fewshot learning toolbox and benchmark.
|
||||||
|
- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab's next-generation action understanding toolbox and benchmark.
|
||||||
|
- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab video perception toolbox and benchmark.
|
||||||
|
- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab optical flow toolbox and benchmark.
|
||||||
|
- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab image and video editing toolbox.
|
||||||
|
- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab image and video generative models toolbox.
|
||||||
|
- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab Model Deployment Framework.
|
||||||
242
prediction/image/mx15hdi/Detect/mmsegmentation/README_zh-CN.md
Normal file
242
prediction/image/mx15hdi/Detect/mmsegmentation/README_zh-CN.md
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
<div align="center">
|
||||||
|
<img src="resources/mmseg-logo.png" width="600"/>
|
||||||
|
<div> </div>
|
||||||
|
<div align="center">
|
||||||
|
<b><font size="5">OpenMMLab 官网</font></b>
|
||||||
|
<sup>
|
||||||
|
<a href="https://openmmlab.com">
|
||||||
|
<i><font size="4">HOT</font></i>
|
||||||
|
</a>
|
||||||
|
</sup>
|
||||||
|
|
||||||
|
<b><font size="5">OpenMMLab 开放平台</font></b>
|
||||||
|
<sup>
|
||||||
|
<a href="https://platform.openmmlab.com">
|
||||||
|
<i><font size="4">TRY IT OUT</font></i>
|
||||||
|
</a>
|
||||||
|
</sup>
|
||||||
|
</div>
|
||||||
|
<div> </div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
[](https://pypi.org/project/mmsegmentation/)
|
||||||
|
[](https://pypi.org/project/mmsegmentation)
|
||||||
|
[](https://mmsegmentation.readthedocs.io/zh_CN/latest/)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/actions)
|
||||||
|
[](https://codecov.io/gh/open-mmlab/mmsegmentation)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/blob/master/LICENSE)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/issues)
|
||||||
|
[](https://github.com/open-mmlab/mmsegmentation/issues)
|
||||||
|
|
||||||
|
[📘使用文档](https://mmsegmentation.readthedocs.io/en/latest/) |
|
||||||
|
[🛠️安装指南](https://mmsegmentation.readthedocs.io/en/latest/get_started.html) |
|
||||||
|
[👀模型库](https://mmsegmentation.readthedocs.io/en/latest/model_zoo.html) |
|
||||||
|
[🆕更新日志](https://mmsegmentation.readthedocs.io/en/latest/changelog.html) |
|
||||||
|
[🤔报告问题](https://github.com/open-mmlab/mmsegmentation/issues/new/choose)
|
||||||
|
|
||||||
|
[English](README.md) | 简体中文
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
## 简介
|
||||||
|
|
||||||
|
MMSegmentation 是一个基于 PyTorch 的语义分割开源工具箱。它是 OpenMMLab 项目的一部分。
|
||||||
|
|
||||||
|
主分支代码目前支持 PyTorch 1.5 以上的版本。
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
<details open>
|
||||||
|
<summary>Major features</summary>
|
||||||
|
|
||||||
|
### 主要特性
|
||||||
|
|
||||||
|
- **统一的基准平台**
|
||||||
|
|
||||||
|
我们将各种各样的语义分割算法集成到了一个统一的工具箱,进行基准测试。
|
||||||
|
|
||||||
|
- **模块化设计**
|
||||||
|
|
||||||
|
MMSegmentation 将分割框架解耦成不同的模块组件,通过组合不同的模块组件,用户可以便捷地构建自定义的分割模型。
|
||||||
|
|
||||||
|
- **丰富的即插即用的算法和模型**
|
||||||
|
|
||||||
|
MMSegmentation 支持了众多主流的和最新的检测算法,例如 PSPNet,DeepLabV3,PSANet,DeepLabV3+ 等.
|
||||||
|
|
||||||
|
- **速度快**
|
||||||
|
|
||||||
|
训练速度比其他语义分割代码库更快或者相当。
|
||||||
|
|
||||||
|
</details>
|
||||||
|
|
||||||
|
## 最新进展
|
||||||
|
|
||||||
|
最新版本 v0.25.0 在 2022.6.2 发布:
|
||||||
|
|
||||||
|
- 支持 PyTorch MLU 后端
|
||||||
|
|
||||||
|
如果想了解更多版本更新细节和历史信息,请阅读[更新日志](docs/en/changelog.md)。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
请参考[快速入门文档](docs/zh_cn/get_started.md#installation)进行安装,参考[数据集准备](docs/zh_cn/dataset_prepare.md)处理数据。
|
||||||
|
|
||||||
|
## 快速入门
|
||||||
|
|
||||||
|
请参考[训练教程](docs/zh_cn/train.md)和[测试教程](docs/zh_cn/inference.md)学习 MMSegmentation 的基本使用。
|
||||||
|
我们也提供了一些进阶教程,内容覆盖了:
|
||||||
|
|
||||||
|
- [增加自定义数据集](docs/zh_cn/tutorials/customize_datasets.md)
|
||||||
|
- [设计新的数据预处理流程](docs/zh_cn/tutorials/data_pipeline.md)
|
||||||
|
- [增加自定义模型](docs/zh_cn/tutorials/customize_models.md)
|
||||||
|
- [增加自定义的运行时配置](docs/zh_cn/tutorials/customize_runtime.md)。
|
||||||
|
- [训练技巧说明](docs/zh_cn/tutorials/training_tricks.md)
|
||||||
|
- [有用的工具](docs/zh_cn/useful_tools.md)。
|
||||||
|
|
||||||
|
同时,我们提供了 Colab 教程。你可以在[这里](demo/MMSegmentation_Tutorial.ipynb)浏览教程,或者直接在 Colab 上[运行](https://colab.research.google.com/github/open-mmlab/mmsegmentation/blob/master/demo/MMSegmentation_Tutorial.ipynb)。
|
||||||
|
|
||||||
|
## 基准测试和模型库
|
||||||
|
|
||||||
|
测试结果和模型可以在[模型库](docs/zh_cn/model_zoo.md)中找到。
|
||||||
|
|
||||||
|
已支持的骨干网络:
|
||||||
|
|
||||||
|
- [x] ResNet (CVPR'2016)
|
||||||
|
- [x] ResNeXt (CVPR'2017)
|
||||||
|
- [x] [HRNet (CVPR'2019)](configs/hrnet)
|
||||||
|
- [x] [ResNeSt (ArXiv'2020)](configs/resnest)
|
||||||
|
- [x] [MobileNetV2 (CVPR'2018)](configs/mobilenet_v2)
|
||||||
|
- [x] [MobileNetV3 (ICCV'2019)](configs/mobilenet_v3)
|
||||||
|
- [x] [Vision Transformer (ICLR'2021)](configs/vit)
|
||||||
|
- [x] [Swin Transformer (ICCV'2021)](configs/swin)
|
||||||
|
- [x] [Twins (NeurIPS'2021)](configs/twins)
|
||||||
|
- [x] [BEiT (ICLR'2022)](configs/beit)
|
||||||
|
- [x] [ConvNeXt (CVPR'2022)](configs/convnext)
|
||||||
|
- [x] [MAE (CVPR'2022)](configs/mae)
|
||||||
|
|
||||||
|
已支持的算法:
|
||||||
|
|
||||||
|
- [x] [FCN (CVPR'2015/TPAMI'2017)](configs/fcn)
|
||||||
|
- [x] [ERFNet (T-ITS'2017)](configs/erfnet)
|
||||||
|
- [x] [UNet (MICCAI'2016/Nat. Methods'2019)](configs/unet)
|
||||||
|
- [x] [PSPNet (CVPR'2017)](configs/pspnet)
|
||||||
|
- [x] [DeepLabV3 (ArXiv'2017)](configs/deeplabv3)
|
||||||
|
- [x] [BiSeNetV1 (ECCV'2018)](configs/bisenetv1)
|
||||||
|
- [x] [PSANet (ECCV'2018)](configs/psanet)
|
||||||
|
- [x] [DeepLabV3+ (CVPR'2018)](configs/deeplabv3plus)
|
||||||
|
- [x] [UPerNet (ECCV'2018)](configs/upernet)
|
||||||
|
- [x] [ICNet (ECCV'2018)](configs/icnet)
|
||||||
|
- [x] [NonLocal Net (CVPR'2018)](configs/nonlocal_net)
|
||||||
|
- [x] [EncNet (CVPR'2018)](configs/encnet)
|
||||||
|
- [x] [Semantic FPN (CVPR'2019)](configs/sem_fpn)
|
||||||
|
- [x] [DANet (CVPR'2019)](configs/danet)
|
||||||
|
- [x] [APCNet (CVPR'2019)](configs/apcnet)
|
||||||
|
- [x] [EMANet (ICCV'2019)](configs/emanet)
|
||||||
|
- [x] [CCNet (ICCV'2019)](configs/ccnet)
|
||||||
|
- [x] [DMNet (ICCV'2019)](configs/dmnet)
|
||||||
|
- [x] [ANN (ICCV'2019)](configs/ann)
|
||||||
|
- [x] [GCNet (ICCVW'2019/TPAMI'2020)](configs/gcnet)
|
||||||
|
- [x] [FastFCN (ArXiv'2019)](configs/fastfcn)
|
||||||
|
- [x] [Fast-SCNN (ArXiv'2019)](configs/fastscnn)
|
||||||
|
- [x] [ISANet (ArXiv'2019/IJCV'2021)](configs/isanet)
|
||||||
|
- [x] [OCRNet (ECCV'2020)](configs/ocrnet)
|
||||||
|
- [x] [DNLNet (ECCV'2020)](configs/dnlnet)
|
||||||
|
- [x] [PointRend (CVPR'2020)](configs/point_rend)
|
||||||
|
- [x] [CGNet (TIP'2020)](configs/cgnet)
|
||||||
|
- [x] [BiSeNetV2 (IJCV'2021)](configs/bisenetv2)
|
||||||
|
- [x] [STDC (CVPR'2021)](configs/stdc)
|
||||||
|
- [x] [SETR (CVPR'2021)](configs/setr)
|
||||||
|
- [x] [DPT (ArXiv'2021)](configs/dpt)
|
||||||
|
- [x] [Segmenter (ICCV'2021)](configs/segmenter)
|
||||||
|
- [x] [SegFormer (NeurIPS'2021)](configs/segformer)
|
||||||
|
- [x] [K-Net (NeurIPS'2021)](configs/knet)
|
||||||
|
|
||||||
|
已支持的数据集:
|
||||||
|
|
||||||
|
- [x] [Cityscapes](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#cityscapes)
|
||||||
|
- [x] [PASCAL VOC](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#pascal-voc)
|
||||||
|
- [x] [ADE20K](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#ade20k)
|
||||||
|
- [x] [Pascal Context](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#pascal-context)
|
||||||
|
- [x] [COCO-Stuff 10k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#coco-stuff-10k)
|
||||||
|
- [x] [COCO-Stuff 164k](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#coco-stuff-164k)
|
||||||
|
- [x] [CHASE_DB1](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#chase-db1)
|
||||||
|
- [x] [DRIVE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#drive)
|
||||||
|
- [x] [HRF](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#hrf)
|
||||||
|
- [x] [STARE](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#stare)
|
||||||
|
- [x] [Dark Zurich](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#dark-zurich)
|
||||||
|
- [x] [Nighttime Driving](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#nighttime-driving)
|
||||||
|
- [x] [LoveDA](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#loveda)
|
||||||
|
- [x] [Potsdam](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#isprs-potsdam)
|
||||||
|
- [x] [Vaihingen](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#isprs-vaihingen)
|
||||||
|
- [x] [iSAID](https://github.com/open-mmlab/mmsegmentation/blob/master/docs/zh_cn/dataset_prepare.md#isaid)
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
如果遇到问题,请参考 [常见问题解答](docs/zh_cn/faq.md)。
|
||||||
|
|
||||||
|
## 贡献指南
|
||||||
|
|
||||||
|
我们感谢所有的贡献者为改进和提升 MMSegmentation 所作出的努力。请参考[贡献指南](.github/CONTRIBUTING.md)来了解参与项目贡献的相关指引。
|
||||||
|
|
||||||
|
## 致谢
|
||||||
|
|
||||||
|
MMSegmentation 是一个由来自不同高校和企业的研发人员共同参与贡献的开源项目。我们感谢所有为项目提供算法复现和新功能支持的贡献者,以及提供宝贵反馈的用户。 我们希望这个工具箱和基准测试可以为社区提供灵活的代码工具,供用户复现已有算法并开发自己的新模型,从而不断为开源社区提供贡献。
|
||||||
|
|
||||||
|
## 引用
|
||||||
|
|
||||||
|
如果你觉得本项目对你的研究工作有所帮助,请参考如下 bibtex 引用 MMSegmentation。
|
||||||
|
|
||||||
|
```bibtex
|
||||||
|
@misc{mmseg2020,
|
||||||
|
title={{MMSegmentation}: OpenMMLab Semantic Segmentation Toolbox and Benchmark},
|
||||||
|
author={MMSegmentation Contributors},
|
||||||
|
howpublished = {\url{https://github.com/open-mmlab/mmsegmentation}},
|
||||||
|
year={2020}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 开源许可证
|
||||||
|
|
||||||
|
`MMSegmentation` 目前以 Apache 2.0 的许可证发布,但是其中有一部分功能并不是使用的 Apache2.0 许可证,我们在 [许可证](LICENSES.md) 中详细地列出了这些功能以及他们对应的许可证,如果您正在从事盈利性活动,请谨慎参考此文档。
|
||||||
|
|
||||||
|
## OpenMMLab 的其他项目
|
||||||
|
|
||||||
|
- [MMCV](https://github.com/open-mmlab/mmcv): OpenMMLab 计算机视觉基础库
|
||||||
|
- [MIM](https://github.com/open-mmlab/mim): MIM 是 OpenMMlab 项目、算法、模型的统一入口
|
||||||
|
- [MMClassification](https://github.com/open-mmlab/mmclassification): OpenMMLab 图像分类工具箱
|
||||||
|
- [MMDetection](https://github.com/open-mmlab/mmdetection): OpenMMLab 目标检测工具箱
|
||||||
|
- [MMDetection3D](https://github.com/open-mmlab/mmdetection3d): OpenMMLab 新一代通用 3D 目标检测平台
|
||||||
|
- [MMRotate](https://github.com/open-mmlab/mmrotate): OpenMMLab 旋转框检测工具箱与测试基准
|
||||||
|
- [MMSegmentation](https://github.com/open-mmlab/mmsegmentation): OpenMMLab 语义分割工具箱
|
||||||
|
- [MMOCR](https://github.com/open-mmlab/mmocr): OpenMMLab 全流程文字检测识别理解工具包
|
||||||
|
- [MMPose](https://github.com/open-mmlab/mmpose): OpenMMLab 姿态估计工具箱
|
||||||
|
- [MMHuman3D](https://github.com/open-mmlab/mmhuman3d): OpenMMLab 人体参数化模型工具箱与测试基准
|
||||||
|
- [MMSelfSup](https://github.com/open-mmlab/mmselfsup): OpenMMLab 自监督学习工具箱与测试基准
|
||||||
|
- [MMRazor](https://github.com/open-mmlab/mmrazor): OpenMMLab 模型压缩工具箱与测试基准
|
||||||
|
- [MMFewShot](https://github.com/open-mmlab/mmfewshot): OpenMMLab 少样本学习工具箱与测试基准
|
||||||
|
- [MMAction2](https://github.com/open-mmlab/mmaction2): OpenMMLab 新一代视频理解工具箱
|
||||||
|
- [MMTracking](https://github.com/open-mmlab/mmtracking): OpenMMLab 一体化视频目标感知平台
|
||||||
|
- [MMFlow](https://github.com/open-mmlab/mmflow): OpenMMLab 光流估计工具箱与测试基准
|
||||||
|
- [MMEditing](https://github.com/open-mmlab/mmediting): OpenMMLab 图像视频编辑工具箱
|
||||||
|
- [MMGeneration](https://github.com/open-mmlab/mmgeneration): OpenMMLab 图片视频生成模型工具箱
|
||||||
|
- [MMDeploy](https://github.com/open-mmlab/mmdeploy): OpenMMLab 模型部署框架
|
||||||
|
|
||||||
|
## 欢迎加入 OpenMMLab 社区
|
||||||
|
|
||||||
|
扫描下方的二维码可关注 OpenMMLab 团队的 [知乎官方账号](https://www.zhihu.com/people/openmmlab),加入 [OpenMMLab 团队](https://jq.qq.com/?_wv=1027&k=aCvMxdr3) 以及 [MMSegmentation](https://jq.qq.com/?_wv=1027&k=9sprS2YO) 的 QQ 群。
|
||||||
|
|
||||||
|
<div align="center">
|
||||||
|
<img src="docs/zh_cn/imgs/zhihu_qrcode.jpg" height="400" /> <img src="docs/zh_cn/imgs/qq_group_qrcode.jpg" height="400" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
我们会在 OpenMMLab 社区为大家
|
||||||
|
|
||||||
|
- 📢 分享 AI 框架的前沿核心技术
|
||||||
|
- 💻 解读 PyTorch 常用模块源码
|
||||||
|
- 📰 发布 OpenMMLab 的相关新闻
|
||||||
|
- 🚀 介绍 OpenMMLab 开发的前沿算法
|
||||||
|
- 🏃 获取更高效的问题答疑和意见反馈
|
||||||
|
- 🔥 提供与各行各业开发者充分交流的平台
|
||||||
|
|
||||||
|
干货满满 📘,等你来撩 💗,OpenMMLab 社区期待您的加入 👬
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'CustomDataset' # need to change
|
||||||
|
data_root = 'data/my_dataset_v7' # need to change
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[127.93135507, 116.76565979, 103.67335042], std=[49.55883976, 47.7692082, 50.7934459], to_rgb=True) # need to calculate
|
||||||
|
crop_size = (512, 512) # need to change
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(512, 512)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', flip_ratio=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(512, 512), # need to change
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=2, # need to change
|
||||||
|
workers_per_gpu=1, # need to change
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/train', # need to change
|
||||||
|
ann_dir='ann_dir/train', # need to change
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',# need to change
|
||||||
|
ann_dir='ann_dir/val',# need to change
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/test',# need to change
|
||||||
|
ann_dir='ann_dir/test',# need to change
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'ADE20KDataset'
|
||||||
|
data_root = 'data/ade/ADEChallengeData2016'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 512),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/training',
|
||||||
|
ann_dir='annotations/training',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'ADE20KDataset'
|
||||||
|
data_root = 'data/ade/ADEChallengeData2016'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (640, 640)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=(2560, 640), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2560, 640),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/training',
|
||||||
|
ann_dir='annotations/training',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'ChaseDB1Dataset'
|
||||||
|
data_root = 'data/CHASE_DB1'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
img_scale = (960, 999)
|
||||||
|
crop_size = (128, 128)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=img_scale,
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type='RepeatDataset',
|
||||||
|
times=40000,
|
||||||
|
dataset=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/training',
|
||||||
|
ann_dir='annotations/training',
|
||||||
|
pipeline=train_pipeline)),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'CityscapesDataset'
|
||||||
|
data_root = 'data/cityscapes/'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 1024)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 1024),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=2,
|
||||||
|
workers_per_gpu=2,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='leftImg8bit/train',
|
||||||
|
ann_dir='gtFine/train',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='leftImg8bit/val',
|
||||||
|
ann_dir='gtFine/val',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='leftImg8bit/val',
|
||||||
|
ann_dir='gtFine/val',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
_base_ = './cityscapes.py'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (1024, 1024)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 1024),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
train=dict(pipeline=train_pipeline),
|
||||||
|
val=dict(pipeline=test_pipeline),
|
||||||
|
test=dict(pipeline=test_pipeline))
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
_base_ = './cityscapes.py'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (768, 768)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2049, 1025),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
train=dict(pipeline=train_pipeline),
|
||||||
|
val=dict(pipeline=test_pipeline),
|
||||||
|
test=dict(pipeline=test_pipeline))
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
_base_ = './cityscapes.py'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (769, 769)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2049, 1025), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2049, 1025),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
train=dict(pipeline=train_pipeline),
|
||||||
|
val=dict(pipeline=test_pipeline),
|
||||||
|
test=dict(pipeline=test_pipeline))
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
_base_ = './cityscapes.py'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (832, 832)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2048, 1024), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 1024),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
train=dict(pipeline=train_pipeline),
|
||||||
|
val=dict(pipeline=test_pipeline),
|
||||||
|
test=dict(pipeline=test_pipeline))
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'COCOStuffDataset'
|
||||||
|
data_root = 'data/coco_stuff10k'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 512),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
reduce_zero_label=True,
|
||||||
|
img_dir='images/train2014',
|
||||||
|
ann_dir='annotations/train2014',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
reduce_zero_label=True,
|
||||||
|
img_dir='images/test2014',
|
||||||
|
ann_dir='annotations/test2014',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
reduce_zero_label=True,
|
||||||
|
img_dir='images/test2014',
|
||||||
|
ann_dir='annotations/test2014',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'COCOStuffDataset'
|
||||||
|
data_root = 'data/coco_stuff164k'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 512),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/train2017',
|
||||||
|
ann_dir='annotations/train2017',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/val2017',
|
||||||
|
ann_dir='annotations/val2017',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/val2017',
|
||||||
|
ann_dir='annotations/val2017',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'DRIVEDataset'
|
||||||
|
data_root = 'data/DRIVE'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
img_scale = (584, 565)
|
||||||
|
crop_size = (64, 64)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=img_scale,
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type='RepeatDataset',
|
||||||
|
times=40000,
|
||||||
|
dataset=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/training',
|
||||||
|
ann_dir='annotations/training',
|
||||||
|
pipeline=train_pipeline)),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'HRFDataset'
|
||||||
|
data_root = 'data/HRF'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
img_scale = (2336, 3504)
|
||||||
|
crop_size = (256, 256)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=img_scale,
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type='RepeatDataset',
|
||||||
|
times=40000,
|
||||||
|
dataset=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/training',
|
||||||
|
ann_dir='annotations/training',
|
||||||
|
pipeline=train_pipeline)),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,62 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'iSAIDDataset'
|
||||||
|
data_root = 'data/iSAID'
|
||||||
|
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
"""
|
||||||
|
This crop_size setting is followed by the implementation of
|
||||||
|
`PointFlow: Flowing Semantics Through Points for Aerial Image
|
||||||
|
Segmentation <https://arxiv.org/pdf/2103.06564.pdf>`_.
|
||||||
|
"""
|
||||||
|
|
||||||
|
crop_size = (896, 896)
|
||||||
|
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(896, 896), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(896, 896),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/train',
|
||||||
|
ann_dir='ann_dir/train',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'LoveDADataset'
|
||||||
|
data_root = 'data/loveDA'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(1024, 1024),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/train',
|
||||||
|
ann_dir='ann_dir/train',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'PascalContextDataset'
|
||||||
|
data_root = 'data/VOCdevkit/VOC2010/'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
|
||||||
|
img_scale = (520, 520)
|
||||||
|
crop_size = (480, 480)
|
||||||
|
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=img_scale,
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClassContext',
|
||||||
|
split='ImageSets/SegmentationContext/train.txt',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClassContext',
|
||||||
|
split='ImageSets/SegmentationContext/val.txt',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClassContext',
|
||||||
|
split='ImageSets/SegmentationContext/val.txt',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'PascalContextDataset59'
|
||||||
|
data_root = 'data/VOCdevkit/VOC2010/'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
|
||||||
|
img_scale = (520, 520)
|
||||||
|
crop_size = (480, 480)
|
||||||
|
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=img_scale,
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClassContext',
|
||||||
|
split='ImageSets/SegmentationContext/train.txt',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClassContext',
|
||||||
|
split='ImageSets/SegmentationContext/val.txt',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClassContext',
|
||||||
|
split='ImageSets/SegmentationContext/val.txt',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,57 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'PascalVOCDataset'
|
||||||
|
data_root = 'data/VOCdevkit/VOC2012'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=(2048, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(2048, 512),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClass',
|
||||||
|
split='ImageSets/Segmentation/train.txt',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClass',
|
||||||
|
split='ImageSets/Segmentation/val.txt',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='JPEGImages',
|
||||||
|
ann_dir='SegmentationClass',
|
||||||
|
split='ImageSets/Segmentation/val.txt',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
_base_ = './pascal_voc12.py'
|
||||||
|
# dataset settings
|
||||||
|
data = dict(
|
||||||
|
train=dict(
|
||||||
|
ann_dir=['SegmentationClass', 'SegmentationClassAug'],
|
||||||
|
split=[
|
||||||
|
'ImageSets/Segmentation/train.txt',
|
||||||
|
'ImageSets/Segmentation/aug.txt'
|
||||||
|
]))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'PotsdamDataset'
|
||||||
|
data_root = 'data/potsdam'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=(512, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(512, 512),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/train',
|
||||||
|
ann_dir='ann_dir/train',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'STAREDataset'
|
||||||
|
data_root = 'data/STARE'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
img_scale = (605, 700)
|
||||||
|
crop_size = (128, 128)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations'),
|
||||||
|
dict(type='Resize', img_scale=img_scale, ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg'])
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=img_scale,
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75, 2.0],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img'])
|
||||||
|
])
|
||||||
|
]
|
||||||
|
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type='RepeatDataset',
|
||||||
|
times=40000,
|
||||||
|
dataset=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/training',
|
||||||
|
ann_dir='annotations/training',
|
||||||
|
pipeline=train_pipeline)),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='images/validation',
|
||||||
|
ann_dir='annotations/validation',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,54 @@
|
|||||||
|
# dataset settings
|
||||||
|
dataset_type = 'ISPRSDataset'
|
||||||
|
data_root = 'data/vaihingen'
|
||||||
|
img_norm_cfg = dict(
|
||||||
|
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
|
||||||
|
crop_size = (512, 512)
|
||||||
|
train_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(type='LoadAnnotations', reduce_zero_label=True),
|
||||||
|
dict(type='Resize', img_scale=(512, 512), ratio_range=(0.5, 2.0)),
|
||||||
|
dict(type='RandomCrop', crop_size=crop_size, cat_max_ratio=0.75),
|
||||||
|
dict(type='RandomFlip', prob=0.5),
|
||||||
|
dict(type='PhotoMetricDistortion'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='Pad', size=crop_size, pad_val=0, seg_pad_val=255),
|
||||||
|
dict(type='DefaultFormatBundle'),
|
||||||
|
dict(type='Collect', keys=['img', 'gt_semantic_seg']),
|
||||||
|
]
|
||||||
|
test_pipeline = [
|
||||||
|
dict(type='LoadImageFromFile'),
|
||||||
|
dict(
|
||||||
|
type='MultiScaleFlipAug',
|
||||||
|
img_scale=(512, 512),
|
||||||
|
# img_ratios=[0.5, 0.75, 1.0, 1.25, 1.5, 1.75],
|
||||||
|
flip=False,
|
||||||
|
transforms=[
|
||||||
|
dict(type='Resize', keep_ratio=True),
|
||||||
|
dict(type='RandomFlip'),
|
||||||
|
dict(type='Normalize', **img_norm_cfg),
|
||||||
|
dict(type='ImageToTensor', keys=['img']),
|
||||||
|
dict(type='Collect', keys=['img']),
|
||||||
|
])
|
||||||
|
]
|
||||||
|
data = dict(
|
||||||
|
samples_per_gpu=4,
|
||||||
|
workers_per_gpu=4,
|
||||||
|
train=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/train',
|
||||||
|
ann_dir='ann_dir/train',
|
||||||
|
pipeline=train_pipeline),
|
||||||
|
val=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline),
|
||||||
|
test=dict(
|
||||||
|
type=dataset_type,
|
||||||
|
data_root=data_root,
|
||||||
|
img_dir='img_dir/val',
|
||||||
|
ann_dir='ann_dir/val',
|
||||||
|
pipeline=test_pipeline))
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
# yapf:enable
|
||||||
|
dist_params = dict(backend='nccl')
|
||||||
|
log_level = 'INFO'
|
||||||
|
#load_from = 'checkpoints/danet_r50-d8_512x512_80k_ade20k_20200615_015125-edb18e08.pth'
|
||||||
|
load_from = None
|
||||||
|
resume_from = None
|
||||||
|
#workflow = [('train', 1)]
|
||||||
|
workflow = [('train', 1), ('val', 1)]
|
||||||
|
cudnn_benchmark = True
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet50_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=50,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='ANNHead',
|
||||||
|
in_channels=[1024, 2048],
|
||||||
|
in_index=[2, 3],
|
||||||
|
channels=512,
|
||||||
|
project_channels=256,
|
||||||
|
query_scales=(1, ),
|
||||||
|
key_pool_scales=(1, 3, 6, 8),
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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'))
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet50_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=50,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='APCHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
pool_scales=(1, 2, 3, 6),
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
auxiliary_head=dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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'))
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
backbone=dict(
|
||||||
|
type='BiSeNetV1',
|
||||||
|
in_channels=3,
|
||||||
|
context_channels=(128, 256, 512),
|
||||||
|
spatial_channels=(64, 64, 64, 128),
|
||||||
|
out_indices=(0, 1, 2),
|
||||||
|
out_channels=256,
|
||||||
|
backbone_cfg=dict(
|
||||||
|
type='ResNet',
|
||||||
|
in_channels=3,
|
||||||
|
depth=18,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 1, 1),
|
||||||
|
strides=(1, 2, 2, 2),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
align_corners=False,
|
||||||
|
init_cfg=None),
|
||||||
|
decode_head=dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=256,
|
||||||
|
in_index=0,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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=128,
|
||||||
|
channels=64,
|
||||||
|
num_convs=1,
|
||||||
|
num_classes=19,
|
||||||
|
in_index=1,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
concat_input=False,
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=128,
|
||||||
|
channels=64,
|
||||||
|
num_convs=1,
|
||||||
|
num_classes=19,
|
||||||
|
in_index=2,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
concat_input=False,
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
],
|
||||||
|
# model training and testing settings
|
||||||
|
train_cfg=dict(),
|
||||||
|
test_cfg=dict(mode='whole'))
|
||||||
@ -0,0 +1,80 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained=None,
|
||||||
|
backbone=dict(
|
||||||
|
type='BiSeNetV2',
|
||||||
|
detail_channels=(64, 64, 128),
|
||||||
|
semantic_channels=(16, 32, 64, 128),
|
||||||
|
semantic_expansion_ratio=6,
|
||||||
|
bga_channels=128,
|
||||||
|
out_indices=(0, 1, 2, 3, 4),
|
||||||
|
init_cfg=None,
|
||||||
|
align_corners=False),
|
||||||
|
decode_head=dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=128,
|
||||||
|
in_index=0,
|
||||||
|
channels=1024,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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=16,
|
||||||
|
channels=16,
|
||||||
|
num_convs=2,
|
||||||
|
num_classes=19,
|
||||||
|
in_index=1,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
concat_input=False,
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=32,
|
||||||
|
channels=64,
|
||||||
|
num_convs=2,
|
||||||
|
num_classes=19,
|
||||||
|
in_index=2,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
concat_input=False,
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=64,
|
||||||
|
channels=256,
|
||||||
|
num_convs=2,
|
||||||
|
num_classes=19,
|
||||||
|
in_index=3,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
concat_input=False,
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=128,
|
||||||
|
channels=1024,
|
||||||
|
num_convs=2,
|
||||||
|
num_classes=19,
|
||||||
|
in_index=4,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
concat_input=False,
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
],
|
||||||
|
# model training and testing settings
|
||||||
|
train_cfg=dict(),
|
||||||
|
test_cfg=dict(mode='whole'))
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet50_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=50,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='CCHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
recurrence=2,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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'))
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', eps=1e-03, requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
backbone=dict(
|
||||||
|
type='CGNet',
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
in_channels=3,
|
||||||
|
num_channels=(32, 64, 128),
|
||||||
|
num_blocks=(3, 21),
|
||||||
|
dilations=(2, 4),
|
||||||
|
reductions=(8, 16)),
|
||||||
|
decode_head=dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=256,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=0,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0,
|
||||||
|
num_classes=19,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss',
|
||||||
|
use_sigmoid=False,
|
||||||
|
loss_weight=1.0,
|
||||||
|
class_weight=[
|
||||||
|
2.5959933, 6.7415504, 3.5354059, 9.8663225, 9.690899, 9.369352,
|
||||||
|
10.289121, 9.953208, 4.3097677, 9.490387, 7.674431, 9.396905,
|
||||||
|
10.347791, 6.3927646, 10.226669, 10.241062, 10.280587,
|
||||||
|
10.396974, 10.055647
|
||||||
|
])),
|
||||||
|
# model training and testing settings
|
||||||
|
train_cfg=dict(sampler=None),
|
||||||
|
test_cfg=dict(mode='whole'))
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet101_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=101,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='DAHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
pam_channels=64,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=5, # Need to change
|
||||||
|
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=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=5, # Need to change
|
||||||
|
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'))
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet50_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=50,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='ASPPHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
dilations=(1, 12, 24, 36),
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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'))
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained=None,
|
||||||
|
backbone=dict(
|
||||||
|
type='UNet',
|
||||||
|
in_channels=3,
|
||||||
|
base_channels=64,
|
||||||
|
num_stages=5,
|
||||||
|
strides=(1, 1, 1, 1, 1),
|
||||||
|
enc_num_convs=(2, 2, 2, 2, 2),
|
||||||
|
dec_num_convs=(2, 2, 2, 2),
|
||||||
|
downsamples=(True, True, True, True),
|
||||||
|
enc_dilations=(1, 1, 1, 1, 1),
|
||||||
|
dec_dilations=(1, 1, 1, 1),
|
||||||
|
with_cp=False,
|
||||||
|
conv_cfg=None,
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
act_cfg=dict(type='ReLU'),
|
||||||
|
upsample_cfg=dict(type='InterpConv'),
|
||||||
|
norm_eval=False),
|
||||||
|
decode_head=dict(
|
||||||
|
type='ASPPHead',
|
||||||
|
in_channels=64,
|
||||||
|
in_index=4,
|
||||||
|
channels=16,
|
||||||
|
dilations=(1, 12, 24, 36),
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=2,
|
||||||
|
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=128,
|
||||||
|
in_index=3,
|
||||||
|
channels=64,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=2,
|
||||||
|
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='slide', crop_size=256, stride=170))
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet50_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=50,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='DepthwiseSeparableASPPHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
dilations=(1, 12, 24, 36),
|
||||||
|
c1_in_channels=256,
|
||||||
|
c1_channels=48,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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'))
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
# model settings
|
||||||
|
norm_cfg = dict(type='SyncBN', requires_grad=True)
|
||||||
|
model = dict(
|
||||||
|
type='EncoderDecoder',
|
||||||
|
pretrained='open-mmlab://resnet50_v1c',
|
||||||
|
backbone=dict(
|
||||||
|
type='ResNetV1c',
|
||||||
|
depth=50,
|
||||||
|
num_stages=4,
|
||||||
|
out_indices=(0, 1, 2, 3),
|
||||||
|
dilations=(1, 1, 2, 4),
|
||||||
|
strides=(1, 2, 1, 1),
|
||||||
|
norm_cfg=norm_cfg,
|
||||||
|
norm_eval=False,
|
||||||
|
style='pytorch',
|
||||||
|
contract_dilation=True),
|
||||||
|
decode_head=dict(
|
||||||
|
type='DMHead',
|
||||||
|
in_channels=2048,
|
||||||
|
in_index=3,
|
||||||
|
channels=512,
|
||||||
|
filter_sizes=(1, 3, 5, 7),
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
norm_cfg=dict(type='SyncBN', requires_grad=True),
|
||||||
|
align_corners=False,
|
||||||
|
loss_decode=dict(
|
||||||
|
type='CrossEntropyLoss', use_sigmoid=False, loss_weight=1.0)),
|
||||||
|
auxiliary_head=dict(
|
||||||
|
type='FCNHead',
|
||||||
|
in_channels=1024,
|
||||||
|
in_index=2,
|
||||||
|
channels=256,
|
||||||
|
num_convs=1,
|
||||||
|
concat_input=False,
|
||||||
|
dropout_ratio=0.1,
|
||||||
|
num_classes=19,
|
||||||
|
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'))
|
||||||
Some files were not shown because too many files have changed in this diff Show More
불러오는 중...
Reference in New Issue
Block a user