├── images ├── kindai_ss.PNG ├── kotenseki_ss.PNG └── ss.png ├── readme.md ├── readme_en.md ├── sample ├── kindai │ ├── 1029114 │ │ ├── 1029114_5.jpg │ │ ├── 1029114_5.xml │ │ ├── 1029114_6.jpg │ │ ├── 1029114_6.xml │ │ ├── 1029114_7.jpg │ │ ├── 1029114_7.xml │ │ ├── 1029114_8.jpg │ │ └── 1029114_8.xml │ └── 1029114_visualize │ │ ├── 1029114_5.png │ │ ├── 1029114_6.png │ │ ├── 1029114_7.png │ │ └── 1029114_8.png └── kotenseki │ ├── 2568591(機巧図彙 2巻首1巻[1]) │ ├── 2568591_14.jpg │ ├── 2568591_14.xml │ ├── 2568591_15.jpg │ ├── 2568591_15.xml │ ├── 2568591_16.jpg │ ├── 2568591_16.xml │ ├── 2568591_17.jpg │ ├── 2568591_17.xml │ ├── 2568591_18.jpg │ ├── 2568591_18.xml │ ├── 2568591_19.jpg │ ├── 2568591_19.xml │ ├── 2568591_20.jpg │ └── 2568591_20.xml │ ├── 2568591_visualize │ ├── 2568591_14.png │ ├── 2568591_15.png │ ├── 2568591_16.png │ ├── 2568591_17.png │ ├── 2568591_18.png │ ├── 2568591_19.png │ └── 2568591_20.png │ ├── 3508165(算法少女) │ ├── 3508165_10.jpg │ ├── 3508165_10.xml │ ├── 3508165_11.jpg │ ├── 3508165_11.xml │ ├── 3508165_12.jpg │ ├── 3508165_12.xml │ ├── 3508165_8.jpg │ ├── 3508165_8.xml │ ├── 3508165_9.jpg │ └── 3508165_9.xml │ └── 3508165_visualize │ ├── 3508165_10.png │ ├── 3508165_11.png │ ├── 3508165_12.png │ ├── 3508165_8.png │ └── 3508165_9.png └── tugidigi-annotation ├── LICENSE ├── README.md ├── pom.xml ├── sample ├── data │ └── 2544473 │ │ ├── binder.json │ │ ├── image │ │ ├── 2544473_01.jpg │ │ ├── 2544473_02.jpg │ │ ├── 2544473_03.jpg │ │ ├── 2544473_04.jpg │ │ └── 2544473_05.jpg │ │ └── xml │ │ ├── 2544473_01.xml │ │ ├── 2544473_02.xml │ │ ├── 2544473_03.xml │ │ ├── 2544473_04.xml │ │ └── 2544473_05.xml └── type.json ├── src └── main │ ├── java │ └── jp │ │ └── go │ │ └── ndl │ │ └── lab │ │ └── annotation │ │ ├── Application.java │ │ ├── batch │ │ ├── AddImageBatch.java │ │ ├── AddType.java │ │ ├── BackupBatch.java │ │ ├── ExportAnnotBatch.java │ │ ├── ListUnannotatedBatch.java │ │ └── UpdateLayoutBatch.java │ │ ├── controller │ │ ├── BinderController.java │ │ ├── ImageController.java │ │ ├── ServletController.java │ │ └── TypeController.java │ │ ├── domain │ │ ├── Action.java │ │ ├── AnnotationObject.java │ │ ├── AnnotationType.java │ │ ├── Bndbox.java │ │ ├── ImageBinder.java │ │ ├── ImageSize.java │ │ ├── ImageType.java │ │ ├── Status.java │ │ └── TargetImage.java │ │ ├── infra │ │ ├── BatchRunner.java │ │ ├── ESData.java │ │ ├── EsBulkIndexer.java │ │ ├── EsDataStore.java │ │ ├── EsDataStoreFactory.java │ │ ├── EsFacet.java │ │ ├── EsSearchQuery.java │ │ ├── EsSearchResult.java │ │ └── GenericEsClient.java │ │ ├── service │ │ ├── JobScheduler.java │ │ ├── PathService.java │ │ ├── StoreBeans.java │ │ └── ThumbService.java │ │ ├── tools │ │ ├── CreateIndexBatch.java │ │ └── TypeDefinitionTool.java │ │ └── utils │ │ ├── AbstractBatch.java │ │ ├── ColorSchema.java │ │ ├── HashCounter.java │ │ ├── ImageUtil.java │ │ ├── PascalVoxToAnnotationObject.java │ │ ├── RESTUtils.java │ │ ├── RWUtils.java │ │ └── TempUtils.java │ └── resources │ ├── config │ └── application.yml │ ├── logback-spring.xml │ ├── static │ └── assets │ │ └── app.bundle.js │ └── templates │ ├── index.html │ └── voc.xml └── web ├── dst └── index.html ├── package-lock.json ├── package.json ├── src ├── components │ ├── auto-saver.vue │ └── search │ │ ├── search-box.vue │ │ ├── search-detail.vue │ │ ├── search-facet.vue │ │ ├── search-keyword.vue │ │ ├── search-pagesize.vue │ │ ├── search-pagination.vue │ │ ├── search-sort.vue │ │ └── search-store.ts ├── config.ts ├── domain │ ├── action.ts │ ├── imagebinder.ts │ ├── imagetype.ts │ └── targetimage.ts ├── main.ts ├── model │ └── state.ts ├── pages │ ├── binder │ │ ├── binder.vue │ │ └── rect-editor.vue │ └── list.vue ├── router.vue ├── service │ ├── binder-service.ts │ ├── cache-util.ts │ ├── image-service.ts │ ├── local-storage-service.ts │ ├── search-utils.ts │ ├── service-utils.ts │ └── type-service.ts ├── sfc.d.ts ├── styles │ ├── _palette.scss │ └── main.scss ├── types.ts └── utils │ ├── element-util.scss │ ├── element-util.ts │ ├── misc.ts │ ├── objects.ts │ ├── polyfills.ts │ └── url.ts ├── tsconfig.json ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js /images/kindai_ss.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/images/kindai_ss.PNG -------------------------------------------------------------------------------- /images/kotenseki_ss.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/images/kotenseki_ss.PNG -------------------------------------------------------------------------------- /images/ss.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/images/ss.png -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | NDL-DocLデータセット(資料画像レイアウトデータセット) 2 | ================== 3 | Click [here](./readme_en.md) for the README(English version) 4 | 5 | ## データセットの概要 6 | 7 | NDL-DocLデータセットは以下のURLから公開しています。
8 | 現在(2019年12月9日)の最新バージョンは1.0です。
9 | 古典籍資料:https://lab.ndl.go.jp/dataset/dataset_kotenseki.zip 10 | 11 | 明治以降刊行資料:https://lab.ndl.go.jp/dataset/dataset_kindai.zip 12 | 13 | 14 | 1.概要 15 | ---- 16 | 17 | ### 1.1 データセットの提供元 18 | 19 | NDL-DocLデータセットは、国立国会図書館デジタルコレクション(以下「デジコレ」といいます。)<[*http://dl.ndl.go.jp/*](http://dl.ndl.go.jp/)>が提供している資料画像データの中から、「古典籍資料」「明治期以降刊行資料」の2種類を公開しています。 20 | 21 | アノテーションデータについては当館職員が新規に作成したものを公開しています。 22 | 23 | ### 1.2 データセットの内訳 24 | 25 | NDL-DocLデータセット(ver1.0)の内訳は以下のとおりです。 26 | 27 | |資料の種類 | 画像数 28 | |-------------------|---------- 29 | |古典籍資料 (https://lab.ndl.go.jp/dataset/dataset_kotenseki.zip) |1,219画像 30 | |明治期以降刊行資料(https://lab.ndl.go.jp/dataset/dataset_kindai.zip) |1,071画像 31 | 32 | ### 1.3 データセットの権利 33 | 「PDM(パブリック・ドメイン・マーク)」< https://creativecommons.org/publicdomain/mark/1.0/deed.ja > 34 | 35 | ※古典籍資料、明治期以降刊行資料ともに著作権保護期間満了資料のみを対象としています。 36 | 37 | NDL-DocLデータセットは、自由な二次利用が可能です。ただし、二次利用に際しては、次の事項へのご配慮をお願いいたします。これらのお願いは法的な契約ではありませんが、できる限りご留意の上でご利用くださるよう、ご協力をお願いします。 38 | 39 | - データを編集・加工等して利用する場合は、それを行ったことを記載してください。編集・加工等を、元となる作品・原資料の作者や当館が行なったかのような態様で公表しないようご留意ください。 40 | - 当該データが自由に二次利用可能であることの表記を保持してください。 41 | - 元となる作品や、その作者の名声を傷つける形での利用は行わないようご留意ください。また、元となる作品に関わる文化やコミュニティへの配慮を行ってください。 42 | - 著作権以外の権利(著作者人格権、著作隣接権、肖像権、パブリシティ権、プライバシー権、商標権等)にも留意し、関連法令を遵守してください。 43 | - 論文等に利用する場合には、先行研究や後続研究と比較を容易にするためNDL-DocLデータセットとバージョンの明記にご協力ください。 44 | 45 | 46 | 2 データセットの説明 47 | ------------------ 48 | 49 | ### 2.1 データセットの構成 50 | 51 | NDL-DocLデータセットは画像ごとに以下の2種類を含みます。 52 | 53 | (1) 資料画像(jpeg画像) 54 | 55 | (2) アノテーションデータ(xml形式) 56 | 57 | **ディレクトリ命名規則** 58 | 59 | 各ディレクトリ名は資料のPID(Persistent Identifier)を意味し、例えば2534020であれば 60 | 61 | http://dl.ndl.go.jp/info:ndljp/pid/2534020 62 | 63 | とすることで当該資料のデジコレ上のURLとしてアクセスできます。 64 | 65 | **ファイル命名規則** 66 | 67 | 命名規則は 68 | (PID)_(コマ番号) 69 | となっています。 70 | 例えば2534020_0003は、http://dl.ndl.go.jp/info:ndljp/pid/2534020 71 | のコマ番号3を意味します。 72 | 73 | **PIDと資料名の対応表** 74 | 75 | デジコレの書誌データは以下から提供しています。 76 | 77 | https://www.ndl.go.jp/jp/dlib/standards/opendataset/index.html 78 | 79 | 各メタデータは以下から、 80 | 81 | http://dl.ndl.go.jp/files/dataset/dataset_201907_k_internet.zip 82 | 83 | 84 | http://dl.ndl.go.jp/files/dataset/dataset_201907_t_internet.zip 85 | 86 | それぞれダウンロード可能です。 87 | 88 | 89 | ### 2.2 アノテーションデータの形式 90 | 91 | Pascal 92 | VOC形式でレイアウトの矩形情報とラベル名を記述したxmlを画像ごとに提供しています。1600\*1200サイズの画像から資料全体の含まれる矩形領域を記述した例を以下に挙げます。 93 | 94 | ```xml 95 | 96 | 97 | kotenseki 98 | 3510690_0036 99 | kotenseki/3510690/3510690_0036.jpg 100 | 101 | NDLDocL 102 | 103 | 104 | 1600 105 | 1200 106 | 107 | 0 108 | 109 | 1_overall 110 | 111 | 300 112 | 149 113 | 1299 114 | 1080 115 | 116 | 117 | 118 | ``` 119 | 120 | 同一資料内に複数のレイアウトが存在する場合、xml内にobjectを複数記述することで対応します。 121 | 122 | 付与されるラベル情報は「古典籍資料」と「明治期以降刊行資料」で異なります。「3各資料に関する詳細情報」を参照してください。 123 | 124 | 3 各資料に関する詳細情報 125 | ---------------------- 126 | 127 | ### 3.1 古典籍資料について 128 | 129 | #### (1) 特徴 130 | 131 | 明治期より前に出版された出版物であり、浮世絵や和書・漢籍資料が含まれます。 132 | 133 | 浮世絵の中に文字が書き込まれているなど、イラスト内部に文字ラインが入ることを許容しています。 134 | 135 | #### (2) 予測対象となるレイアウトのラベル 136 | 137 | |No | ラベル名 |説明 138 | |----| -----------------| -------------------------------------- 139 | |1 | 1\_overall | 資料範囲全体 140 | |2 | 2\_handwritten | くずし字の文字ライン 141 | |3 | 3\_typography | 楷書体・行書体の文字ライン 142 | |4 | 4\_illustration | イラスト 143 | |5 | 5\_stamp | 印影(蔵書印等) 144 | 145 | kotenseki sample image 146 | 147 | ### 3.2 明治期以降刊行資料について 148 | 149 | #### (1) 特徴 150 | 明治期以降に出版された、冊子の形態をとる出版物であり、マイクロ資料をデジタル化した資料など、強いノイズの乗った資料が多く存在します。 151 | 152 | 多くは昭和前期より前に刊行された資料からなりますが、一部戦後に刊行された刊行物を含みます。 153 | 154 | #### (2) 予測対象となるレイアウトのラベル 155 | 156 | |No |ラベル名 |説明 157 | |---- |-----------------| --------------------------------------------- 158 | |1 |1\_overall | 資料範囲全体 159 | |2 |4\_illustration | イラスト(写真を含む) 160 | |3 |5\_stamp | 印影(蔵書印等) 161 | |4 |6\_headline | 見出し 162 | |5 |7\_caption | 図表見出し 163 | |6 |8\_textline | 6\_headline, 7\_caption以外の文字ライン 164 | |7 |9\_table | 表 165 | 166 | ※ラベル名の先頭の数字は両資料で通し番号 167 | 168 | kindai sample image 169 | 170 | 171 | 172 | 173 | 4 過去のバージョン情報 174 | ---------------------- 175 | 20191209 ver1.0提供開始
176 | [古典籍資料](https://lab.ndl.go.jp/dataset/legacy/dataset_kotenseki_ver1.0.zip) 177 | [明治期以降刊行資料](https://lab.ndl.go.jp/dataset/legacy/dataset_kindai_ver1.0.zip) 178 | 179 | 20191108 ベータ版提供開始 180 | 181 | -------------------------------------------------------------------------------- /sample/kindai/1029114/1029114_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114/1029114_5.jpg -------------------------------------------------------------------------------- /sample/kindai/1029114/1029114_6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114/1029114_6.jpg -------------------------------------------------------------------------------- /sample/kindai/1029114/1029114_7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114/1029114_7.jpg -------------------------------------------------------------------------------- /sample/kindai/1029114/1029114_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114/1029114_8.jpg -------------------------------------------------------------------------------- /sample/kindai/1029114_visualize/1029114_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114_visualize/1029114_5.png -------------------------------------------------------------------------------- /sample/kindai/1029114_visualize/1029114_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114_visualize/1029114_6.png -------------------------------------------------------------------------------- /sample/kindai/1029114_visualize/1029114_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114_visualize/1029114_7.png -------------------------------------------------------------------------------- /sample/kindai/1029114_visualize/1029114_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kindai/1029114_visualize/1029114_8.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_14.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_15.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_16.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_17.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_18.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_19.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591(機巧図彙 2巻首1巻[1])/2568591_20.jpg -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_14.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_15.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_16.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_17.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_18.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_19.png -------------------------------------------------------------------------------- /sample/kotenseki/2568591_visualize/2568591_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/2568591_visualize/2568591_20.png -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165(算法少女)/3508165_10.jpg -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_10.xml: -------------------------------------------------------------------------------- 1 | 2 | 作業後2 3 | 3508165_10.jpg 4 | C:\Users\ndl\Desktop\あのてしょん\作業後2\3508165_10.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 241 21 | 142 22 | 1375 23 | 1076 24 | 25 | 26 | 27 | 4_illustration 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 265 33 | 299 34 | 399 35 | 543 36 | 37 | 38 | 39 | 2_handwritten 40 | Unspecified 41 | 0 42 | 0 43 | 44 | 1238 45 | 294 46 | 1268 47 | 1020 48 | 49 | 50 | 51 | 2_handwritten 52 | Unspecified 53 | 0 54 | 0 55 | 56 | 873 57 | 297 58 | 896 59 | 1022 60 | 61 | 62 | 63 | 2_handwritten 64 | Unspecified 65 | 0 66 | 0 67 | 68 | 1184 69 | 294 70 | 1209 71 | 1014 72 | 73 | 74 | 75 | 2_handwritten 76 | Unspecified 77 | 0 78 | 0 79 | 80 | 268 81 | 555 82 | 296 83 | 954 84 | 85 | 86 | 87 | 2_handwritten 88 | Unspecified 89 | 0 90 | 0 91 | 92 | 1030 93 | 299 94 | 1057 95 | 1020 96 | 97 | 98 | 99 | 2_handwritten 100 | Unspecified 101 | 0 102 | 0 103 | 104 | 1291 105 | 297 106 | 1316 107 | 1025 108 | 109 | 110 | 111 | 2_handwritten 112 | Unspecified 113 | 0 114 | 0 115 | 116 | 820 117 | 301 118 | 847 119 | 627 120 | 121 | 122 | 123 | 2_handwritten 124 | Unspecified 125 | 0 126 | 0 127 | 128 | 925 129 | 297 130 | 952 131 | 1019 132 | 133 | 134 | 135 | 2_handwritten 136 | Unspecified 137 | 0 138 | 0 139 | 140 | 320 141 | 563 142 | 347 143 | 1020 144 | 145 | 146 | 147 | 2_handwritten 148 | Unspecified 149 | 0 150 | 0 151 | 152 | 372 153 | 562 154 | 400 155 | 1023 156 | 157 | 158 | 159 | 2_handwritten 160 | Unspecified 161 | 0 162 | 0 163 | 164 | 632 165 | 301 166 | 656 167 | 1019 168 | 169 | 170 | 171 | 2_handwritten 172 | Unspecified 173 | 0 174 | 0 175 | 176 | 475 177 | 302 178 | 499 179 | 665 180 | 181 | 182 | 183 | 2_handwritten 184 | Unspecified 185 | 0 186 | 0 187 | 188 | 978 189 | 297 190 | 1003 191 | 1025 192 | 193 | 194 | 195 | 2_handwritten 196 | Unspecified 197 | 0 198 | 0 199 | 200 | 681 201 | 299 202 | 709 203 | 1012 204 | 205 | 206 | 207 | 2_handwritten 208 | Unspecified 209 | 0 210 | 0 211 | 212 | 1135 213 | 296 214 | 1160 215 | 452 216 | 217 | 218 | 219 | 2_handwritten 220 | Unspecified 221 | 0 222 | 0 223 | 224 | 528 225 | 301 226 | 551 227 | 1019 228 | 229 | 230 | 231 | 2_handwritten 232 | Unspecified 233 | 0 234 | 0 235 | 236 | 580 237 | 296 238 | 604 239 | 1015 240 | 241 | 242 | 243 | 2_handwritten 244 | Unspecified 245 | 0 246 | 0 247 | 248 | 1080 249 | 404 250 | 1108 251 | 476 252 | 253 | 254 | 255 | 2_handwritten 256 | Unspecified 257 | 0 258 | 0 259 | 260 | 734 261 | 408 262 | 761 263 | 476 264 | 265 | 266 | 267 | 2_handwritten 268 | Unspecified 269 | 0 270 | 0 271 | 272 | 420 273 | 400 274 | 444 275 | 476 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165(算法少女)/3508165_11.jpg -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_11.xml: -------------------------------------------------------------------------------- 1 | 2 | 作業後2 3 | 3508165_11.jpg 4 | C:\Users\ndl\Desktop\あのてしょん\作業後2\3508165_11.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 231 21 | 141 22 | 1363 23 | 1078 24 | 25 | 26 | 27 | 4_illustration 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 801 33 | 296 34 | 987 35 | 479 36 | 37 | 38 | 39 | 2_handwritten 40 | Unspecified 41 | 0 42 | 0 43 | 44 | 1172 45 | 293 46 | 1202 47 | 960 48 | 49 | 50 | 51 | 2_handwritten 52 | Unspecified 53 | 0 54 | 0 55 | 56 | 359 57 | 286 58 | 390 59 | 773 60 | 61 | 62 | 63 | 2_handwritten 64 | Unspecified 65 | 0 66 | 0 67 | 68 | 411 69 | 286 70 | 438 71 | 1011 72 | 73 | 74 | 75 | 2_handwritten 76 | Unspecified 77 | 0 78 | 0 79 | 80 | 307 81 | 325 82 | 337 83 | 1008 84 | 85 | 86 | 87 | 2_handwritten 88 | Unspecified 89 | 0 90 | 0 91 | 92 | 1226 93 | 298 94 | 1251 95 | 1024 96 | 97 | 98 | 99 | 2_handwritten 100 | Unspecified 101 | 0 102 | 0 103 | 104 | 623 105 | 286 106 | 651 107 | 1010 108 | 109 | 110 | 111 | 2_handwritten 112 | Unspecified 113 | 0 114 | 0 115 | 116 | 259 117 | 322 118 | 286 119 | 1006 120 | 121 | 122 | 123 | 2_handwritten 124 | Unspecified 125 | 0 126 | 0 127 | 128 | 674 129 | 288 130 | 705 131 | 1009 132 | 133 | 134 | 135 | 2_handwritten 136 | Unspecified 137 | 0 138 | 0 139 | 140 | 570 141 | 286 142 | 599 143 | 782 144 | 145 | 146 | 147 | 2_handwritten 148 | Unspecified 149 | 0 150 | 0 151 | 152 | 1123 153 | 296 154 | 1149 155 | 1022 156 | 157 | 158 | 159 | 2_handwritten 160 | Unspecified 161 | 0 162 | 0 163 | 164 | 913 165 | 510 166 | 944 167 | 1023 168 | 169 | 170 | 171 | 2_handwritten 172 | Unspecified 173 | 0 174 | 0 175 | 176 | 967 177 | 509 178 | 991 179 | 1023 180 | 181 | 182 | 183 | 2_handwritten 184 | Unspecified 185 | 0 186 | 0 187 | 188 | 1071 189 | 298 190 | 1096 191 | 463 192 | 193 | 194 | 195 | 2_handwritten 196 | Unspecified 197 | 0 198 | 0 199 | 200 | 865 201 | 508 202 | 887 203 | 1023 204 | 205 | 206 | 207 | 2_handwritten 208 | Unspecified 209 | 0 210 | 0 211 | 212 | 812 213 | 513 214 | 831 215 | 631 216 | 217 | 218 | 219 | 2_handwritten 220 | Unspecified 221 | 0 222 | 0 223 | 224 | 462 225 | 292 226 | 489 227 | 1010 228 | 229 | 230 | 231 | 2_handwritten 232 | Unspecified 233 | 0 234 | 0 235 | 236 | 1274 237 | 401 238 | 1303 239 | 473 240 | 241 | 242 | 243 | 2_handwritten 244 | Unspecified 245 | 0 246 | 0 247 | 248 | 1016 249 | 397 250 | 1044 251 | 481 252 | 253 | 254 | 255 | 2_handwritten 256 | Unspecified 257 | 0 258 | 0 259 | 260 | 728 261 | 381 262 | 756 263 | 466 264 | 265 | 266 | 267 | 2_handwritten 268 | Unspecified 269 | 0 270 | 0 271 | 272 | 516 273 | 396 274 | 542 275 | 466 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165(算法少女)/3508165_12.jpg -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_12.xml: -------------------------------------------------------------------------------- 1 | 2 | 作業後2 3 | 3508165_12.jpg 4 | C:\Users\ndl\Desktop\あのてしょん\作業後2\3508165_12.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 232 21 | 140 22 | 1365 23 | 1078 24 | 25 | 26 | 27 | 2_handwritten 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 1125 33 | 284 34 | 1151 35 | 1013 36 | 37 | 38 | 39 | 2_handwritten 40 | Unspecified 41 | 0 42 | 0 43 | 44 | 1176 45 | 286 46 | 1200 47 | 1004 48 | 49 | 50 | 51 | 2_handwritten 52 | Unspecified 53 | 0 54 | 0 55 | 56 | 808 57 | 328 58 | 832 59 | 1011 60 | 61 | 62 | 63 | 2_handwritten 64 | Unspecified 65 | 0 66 | 0 67 | 68 | 866 69 | 285 70 | 892 71 | 554 72 | 73 | 74 | 75 | 2_handwritten 76 | Unspecified 77 | 0 78 | 0 79 | 80 | 1279 81 | 327 82 | 1303 83 | 854 84 | 85 | 86 | 87 | 2_handwritten 88 | Unspecified 89 | 0 90 | 0 91 | 92 | 1072 93 | 290 94 | 1097 95 | 1013 96 | 97 | 98 | 99 | 2_handwritten 100 | Unspecified 101 | 0 102 | 0 103 | 104 | 1022 105 | 285 106 | 1049 107 | 512 108 | 109 | 110 | 111 | 2_handwritten 112 | Unspecified 113 | 0 114 | 0 115 | 116 | 677 117 | 358 118 | 700 119 | 1004 120 | 121 | 122 | 123 | 2_handwritten 124 | Unspecified 125 | 0 126 | 0 127 | 128 | 308 129 | 297 130 | 337 131 | 1014 132 | 133 | 134 | 135 | 2_handwritten 136 | Unspecified 137 | 0 138 | 0 139 | 140 | 914 141 | 285 142 | 942 143 | 1008 144 | 145 | 146 | 147 | 2_handwritten 148 | Unspecified 149 | 0 150 | 0 151 | 152 | 258 153 | 296 154 | 285 155 | 544 156 | 157 | 158 | 159 | 2_handwritten 160 | Unspecified 161 | 0 162 | 0 163 | 164 | 622 165 | 352 166 | 651 167 | 1004 168 | 169 | 170 | 171 | 2_handwritten 172 | Unspecified 173 | 0 174 | 0 175 | 176 | 575 177 | 367 178 | 599 179 | 1004 180 | 181 | 182 | 183 | 2_handwritten 184 | Unspecified 185 | 0 186 | 0 187 | 188 | 727 189 | 365 190 | 752 191 | 1004 192 | 193 | 194 | 195 | 2_handwritten 196 | Unspecified 197 | 0 198 | 0 199 | 200 | 521 201 | 362 202 | 542 203 | 723 204 | 205 | 206 | 207 | 2_handwritten 208 | Unspecified 209 | 0 210 | 0 211 | 212 | 360 213 | 296 214 | 386 215 | 1016 216 | 217 | 218 | 219 | 2_handwritten 220 | Unspecified 221 | 0 222 | 0 223 | 224 | 1230 225 | 392 226 | 1254 227 | 461 228 | 229 | 230 | 231 | 2_handwritten 232 | Unspecified 233 | 0 234 | 0 235 | 236 | 967 237 | 392 238 | 994 239 | 475 240 | 241 | 242 | 243 | 2_handwritten 244 | Unspecified 245 | 0 246 | 0 247 | 248 | 466 249 | 354 250 | 492 251 | 495 252 | 253 | 254 | 255 | 2_handwritten 256 | Unspecified 257 | 0 258 | 0 259 | 260 | 414 261 | 392 262 | 438 263 | 466 264 | 265 | 266 | 267 | -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165(算法少女)/3508165_8.jpg -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165(算法少女)/3508165_9.jpg -------------------------------------------------------------------------------- /sample/kotenseki/3508165(算法少女)/3508165_9.xml: -------------------------------------------------------------------------------- 1 | 2 | 作業後2 3 | 3508165_9.jpg 4 | C:\Users\ndl\Desktop\あのてしょん\作業後2\3508165_9.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 234 21 | 143 22 | 1366 23 | 1079 24 | 25 | 26 | 27 | 5_stamp 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 458 33 | 172 34 | 622 35 | 332 36 | 37 | 38 | 39 | 2_handwritten 40 | Unspecified 41 | 0 42 | 0 43 | 44 | 267 45 | 297 46 | 297 47 | 1023 48 | 49 | 50 | 51 | 2_handwritten 52 | Unspecified 53 | 0 54 | 0 55 | 56 | 370 57 | 298 58 | 406 59 | 1022 60 | 61 | 62 | 63 | 2_handwritten 64 | Unspecified 65 | 0 66 | 0 67 | 68 | 424 69 | 299 70 | 452 71 | 1009 72 | 73 | 74 | 75 | 2_handwritten 76 | Unspecified 77 | 0 78 | 0 79 | 80 | 321 81 | 300 82 | 348 83 | 1013 84 | 85 | 86 | 87 | 2_handwritten 88 | Unspecified 89 | 0 90 | 0 91 | 92 | 475 93 | 299 94 | 506 95 | 1013 96 | 97 | 98 | 99 | 2_handwritten 100 | Unspecified 101 | 0 102 | 0 103 | 104 | 528 105 | 302 106 | 561 107 | 1009 108 | 109 | 110 | 111 | 3_typography 112 | Unspecified 113 | 0 114 | 0 115 | 116 | 1281 117 | 319 118 | 1315 119 | 867 120 | 121 | 122 | 123 | 3_typography 124 | Unspecified 125 | 0 126 | 0 127 | 128 | 1207 129 | 396 130 | 1239 131 | 608 132 | 133 | 134 | 135 | 3_typography 136 | Unspecified 137 | 0 138 | 0 139 | 140 | 1203 141 | 648 142 | 1235 143 | 833 144 | 145 | 146 | 147 | 3_typography 148 | Unspecified 149 | 0 150 | 0 151 | 152 | 686 153 | 400 154 | 717 155 | 933 156 | 157 | 158 | 159 | 3_typography 160 | Unspecified 161 | 0 162 | 0 163 | 164 | 737 165 | 291 166 | 767 167 | 540 168 | 169 | 170 | 171 | 5_stamp 172 | Unspecified 173 | 0 174 | 0 175 | 176 | 1181 177 | 838 178 | 1254 179 | 913 180 | 181 | 182 | 183 | 2_handwritten 184 | Unspecified 185 | 0 186 | 0 187 | 188 | 628 189 | 367 190 | 661 191 | 545 192 | 193 | 194 | 195 | 2_handwritten 196 | Unspecified 197 | 0 198 | 0 199 | 200 | 577 201 | 397 202 | 604 203 | 468 204 | 205 | 206 | 207 | -------------------------------------------------------------------------------- /sample/kotenseki/3508165_visualize/3508165_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165_visualize/3508165_10.png -------------------------------------------------------------------------------- /sample/kotenseki/3508165_visualize/3508165_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165_visualize/3508165_11.png -------------------------------------------------------------------------------- /sample/kotenseki/3508165_visualize/3508165_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165_visualize/3508165_12.png -------------------------------------------------------------------------------- /sample/kotenseki/3508165_visualize/3508165_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165_visualize/3508165_8.png -------------------------------------------------------------------------------- /sample/kotenseki/3508165_visualize/3508165_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/sample/kotenseki/3508165_visualize/3508165_9.png -------------------------------------------------------------------------------- /tugidigi-annotation/README.md: -------------------------------------------------------------------------------- 1 | # Book Layout Annotator 2 | 3 | annotation editor image 4 | 5 | ## back 6 | 7 | Backend is spring-boot-based Single Jar Application. 8 | 9 | ### required 10 | 11 | - JDK(1.8 or later) 12 | - Apache Maven 13 | - [Elasticsearch](https://www.elastic.co/) (worked on 7.3.2) as Database 14 | 15 | ### build 16 | 17 | ``` 18 | mvn clean install 19 | ``` 20 | 21 | ### setup 22 | 23 | #### 1. elasticsearch 24 | 25 | After install & run elasticsearch, run following to create index 26 | 27 | ``` 28 | cd target 29 | java -jar tugidigi-annotator-0.1.jar batch create-index all 30 | ``` 31 | 32 | #### 2. annotation type 33 | 34 | To register annotation rule 35 | 36 | ``` 37 | java -jar tugidigi-annotator-0.1.jar batch add-type ../sample/type.json 38 | ``` 39 | 40 | See sample/type.json 41 | 42 | #### 3. target data 43 | 44 | To register target image data, you need following directory structure. See sample/data directory 45 | 46 | - target-dir 47 | - book-dir 48 | - binder.json : Metadata (1 per book) 49 | - xml-dir : Layout data (Pascal VOC format) if any. XML file name must be correspond to image file name. 50 | - xxxx_01.xml 51 | - xxxx_02.xml 52 | - ... 53 | - image-dir : Image Data. Image File Name must be UNIQUE in system. 54 | - xxxx_01.jpg 55 | - xxxx_02.jpg 56 | - ... 57 | 58 | ``` 59 | java -jar tugidigi-annotator-0.1.jar batch add-image ../sample/data true 60 | ``` 61 | 62 | This command iterate directories in sample/data. 63 | Each directory represents 1 book, and program try to find binder.json in each directory. 64 | 65 | ### run 66 | 67 | ``` 68 | java -jar tugidigi-annotator-0.1.jar web 69 | ``` 70 | 71 | Application is running on [http://localhost:9985/app/](http://localhost:9985/app) by default 72 | 73 | ### manage 74 | 75 | #### export annotated data 76 | 77 | ``` 78 | java -jar tugidigi-annotator-0.1.jar batch export all output_dir 79 | ``` 80 | 81 | "all" can be changed to registered annotation type name. 82 | 83 | #### update annotated data 84 | 85 | ``` 86 | java -jar tugidigi-annotator-0.1.jar batch update-layout xml_dir 87 | ``` 88 | 89 | xml_dir must contains xml files(Pascal VOC format), which has [image id].xml in name. 90 | 91 | ## front 92 | 93 | Frontend is SPA powered by vue & vue-router. 94 | 95 | ### required 96 | 97 | - [Node.js](https://nodejs.org/) (v10.15.0 or later) 98 | 99 | ### install 100 | 101 | ``` 102 | npm install 103 | ``` 104 | 105 | ### build 106 | 107 | ``` 108 | npm build 109 | ``` 110 | -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/binder.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "2544473", 3 | "name": "栄花物語 40巻", 4 | "tags": [], 5 | "seeAlso": "http://dl.ndl.go.jp/info:ndljp/pid/2544473", 6 | "imagePath": "sample/data/2544473/image" 7 | } -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/image/2544473_01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/tugidigi-annotation/sample/data/2544473/image/2544473_01.jpg -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/image/2544473_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/tugidigi-annotation/sample/data/2544473/image/2544473_02.jpg -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/image/2544473_03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/tugidigi-annotation/sample/data/2544473/image/2544473_03.jpg -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/image/2544473_04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/tugidigi-annotation/sample/data/2544473/image/2544473_04.jpg -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/image/2544473_05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ndl-lab/layout-dataset/39a10dbe8aeb9ad362e2b9b8c678b60aeff0e29e/tugidigi-annotation/sample/data/2544473/image/2544473_05.jpg -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/xml/2544473_01.xml: -------------------------------------------------------------------------------- 1 | 2 | work 3 | 2544473_1.jpg 4 | C:\Users\t-kawash\Desktop\work\2544473_1.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 76 21 | 77 22 | 798 23 | 1006 24 | 25 | 26 | 27 | 2_handwritten 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 376 33 | 97 34 | 498 35 | 706 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/xml/2544473_02.xml: -------------------------------------------------------------------------------- 1 | 2 | work 3 | 2544473_2.jpg 4 | C:\Users\t-kawash\Desktop\work\2544473_2.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 114 21 | 71 22 | 1472 23 | 1012 24 | 25 | 26 | 27 | 2_handwritten 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 646 33 | 257 34 | 700 35 | 493 36 | 37 | 38 | 39 | 2_handwritten 40 | Unspecified 41 | 0 42 | 0 43 | 44 | 525 45 | 315 46 | 567 47 | 444 48 | 49 | 50 | 51 | 2_handwritten 52 | Unspecified 53 | 0 54 | 0 55 | 56 | 583 57 | 319 58 | 618 59 | 466 60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/xml/2544473_03.xml: -------------------------------------------------------------------------------- 1 | 2 | work 3 | 2544473_3.jpg 4 | C:\Users\t-kawash\Desktop\work\2544473_3.jpg 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 104 21 | 65 22 | 1456 23 | 1012 24 | 25 | 26 | 27 | 2_handwritten 28 | Unspecified 29 | 0 30 | 0 31 | 32 | 602 33 | 229 34 | 658 35 | 941 36 | 37 | 38 | 39 | 2_handwritten 40 | Unspecified 41 | 0 42 | 0 43 | 44 | 393 45 | 225 46 | 445 47 | 935 48 | 49 | 50 | 51 | 2_handwritten 52 | Unspecified 53 | 0 54 | 0 55 | 56 | 241 57 | 227 58 | 286 59 | 931 60 | 61 | 62 | 63 | 2_handwritten 64 | Unspecified 65 | 0 66 | 0 67 | 68 | 344 69 | 230 70 | 387 71 | 931 72 | 73 | 74 | 75 | 2_handwritten 76 | Unspecified 77 | 0 78 | 0 79 | 80 | 452 81 | 230 82 | 492 83 | 931 84 | 85 | 86 | 87 | 2_handwritten 88 | Unspecified 89 | 0 90 | 0 91 | 92 | 296 93 | 223 94 | 334 95 | 934 96 | 97 | 98 | 99 | 2_handwritten 100 | Unspecified 101 | 0 102 | 0 103 | 104 | 666 105 | 302 106 | 704 107 | 389 108 | 109 | 110 | 111 | 5_stamp 112 | Unspecified 113 | 0 114 | 0 115 | 116 | 605 117 | 93 118 | 739 119 | 253 120 | 121 | 122 | 123 | 5_stamp 124 | Unspecified 125 | 0 126 | 0 127 | 128 | 349 129 | 111 130 | 519 131 | 268 132 | 133 | 134 | 135 | 5_stamp 136 | Unspecified 137 | 0 138 | 0 139 | 140 | 660 141 | 144 142 | 719 143 | 205 144 | 145 | 146 | 147 | 2_handwritten 148 | Unspecified 149 | 0 150 | 0 151 | 152 | 549 153 | 226 154 | 605 155 | 938 156 | 157 | 158 | 159 | 2_handwritten 160 | Unspecified 161 | 0 162 | 0 163 | 164 | 496 165 | 226 166 | 552 167 | 938 168 | 169 | 170 | 171 | 2_handwritten 172 | Unspecified 173 | 0 174 | 0 175 | 176 | 188 177 | 227 178 | 233 179 | 931 180 | 181 | 182 | 183 | 2_handwritten 184 | Unspecified 185 | 0 186 | 0 187 | 188 | 134 189 | 223 190 | 179 191 | 927 192 | 193 | 194 | 195 | -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/xml/2544473_04.xml: -------------------------------------------------------------------------------- 1 | annot2544473_42544473_4Unknown16001200301_overallUnspecified0010259146510132_handwrittenUnspecified002952214469352_handwrittenUnspecified00119520713549212_handwrittenUnspecified005032216519312_handwrittenUnspecified001402222379342_handwrittenUnspecified008352199909262_handwrittenUnspecified0099553111499382_handwrittenUnspecified00135321014119172_handwrittenUnspecified006602257079312_handwrittenUnspecified00115951611989192_handwrittenUnspecified002475992899322_handwrittenUnspecified002512232875662_handwrittenUnspecified008882189265532_handwrittenUnspecified004516494969312_handwrittenUnspecified0099121610325282_handwrittenUnspecified00104321810824942_handwrittenUnspecified00109621411354682_handwrittenUnspecified009372179775172_handwrittenUnspecified004572254934682_handwrittenUnspecified00114421411864753_typographyUnspecified004614534896963_typographyUnspecified00110744011366253_typographyUnspecified00115941111836163_typographyUnspecified003584993836713_typographyUnspecified002555172796333_typographyUnspecified003065173286383_typographyUnspecified00105249610786173_typographyUnspecified005654935955723_typographyUnspecified0010075151027600 -------------------------------------------------------------------------------- /tugidigi-annotation/sample/data/2544473/xml/2544473_05.xml: -------------------------------------------------------------------------------- 1 | annot2544473_52544473_5Unknown16001200301_overallUnspecified0010264146310112_handwrittenUnspecified0082821214069272_handwrittenUnspecified002962254459362_handwrittenUnspecified005052266029362_handwrittenUnspecified001922262869352_handwrittenUnspecified006602297109352_handwrittenUnspecified006102256539352_handwrittenUnspecified001402301869332_handwrittenUnspecified004535484929382_handwrittenUnspecified004572244924943_typographyUnspecified004654614856113_typographyUnspecified00362491384582 -------------------------------------------------------------------------------- /tugidigi-annotation/sample/type.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "kindai", 3 | "name": "明治以降資料", 4 | "types": [ 5 | { 6 | "id": "1_overall", 7 | "name": "資料範囲全体" 8 | }, 9 | { 10 | "id": "5_stamp", 11 | "name": "印影", 12 | "description":"蔵書印等" 13 | }, 14 | { 15 | "id": "6_headline", 16 | "name": "見出し" 17 | }, 18 | { 19 | "id": "4_illustration", 20 | "name": "イラスト" 21 | }, 22 | { 23 | "id": "7_caption", 24 | "name": "図表見出し", 25 | "description": "" 26 | }, 27 | { 28 | "id": "8_textline", 29 | "name": "テキスト行", 30 | "description": "6_headline, 7_caption以外のテキストライン" 31 | }, 32 | { 33 | "id": "9_table", 34 | "name": "表", 35 | "description": "※表の中身については8_ textlineを別に付与" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/Application.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation; 2 | 3 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.apache.commons.lang3.ArrayUtils; 6 | import org.springframework.beans.factory.NoSuchBeanDefinitionException; 7 | import org.springframework.boot.SpringApplication; 8 | import org.springframework.boot.WebApplicationType; 9 | import org.springframework.boot.autoconfigure.SpringBootApplication; 10 | import org.springframework.context.ConfigurableApplicationContext; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.scheduling.annotation.EnableAsync; 13 | import org.springframework.scheduling.annotation.EnableScheduling; 14 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 15 | 16 | @SpringBootApplication 17 | @Slf4j 18 | @EnableScheduling 19 | @EnableAsync 20 | public class Application { 21 | 22 | public static final String MODE_BATCH = "batch"; 23 | public static final String MODE_WEB = "web"; 24 | 25 | public static void main(String... args) { 26 | if (args.length == 0) { 27 | log.error("起動モードの指定がありません"); 28 | System.exit(-1); 29 | } 30 | 31 | SpringApplication springApplication = new SpringApplication(Application.class); 32 | springApplication.setAdditionalProfiles(args[0]); 33 | switch (args[0]) { 34 | case MODE_WEB: 35 | springApplication.setWebApplicationType(WebApplicationType.SERVLET); 36 | springApplication.run(args); 37 | break; 38 | case MODE_BATCH: 39 | springApplication.setWebApplicationType(WebApplicationType.NONE); 40 | springApplication.setLogStartupInfo(false); 41 | try (ConfigurableApplicationContext ctx = springApplication.run(args)) { 42 | try { 43 | AbstractBatch batchService = ctx.getBean(args[1], AbstractBatch.class); 44 | batchService.run(ArrayUtils.subarray(args, 2, args.length)); 45 | } catch (NoSuchBeanDefinitionException e) { 46 | log.error("{}というバッチは存在しません", args[1]); 47 | } 48 | } catch (Throwable t) { 49 | log.error("バッチ実行中に予期せぬエラーが発生しました", t); 50 | System.exit(-1); 51 | } 52 | System.exit(0); 53 | break; 54 | default: 55 | log.error("第一引数ERROR {}", args[0]); 56 | System.exit(-1); 57 | } 58 | } 59 | 60 | @Bean 61 | public ThreadPoolTaskScheduler taskScheduler() { 62 | ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler(); 63 | taskScheduler.setPoolSize(2); 64 | return taskScheduler; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/batch/AddImageBatch.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.batch; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jp.go.ndl.lab.annotation.Application; 5 | import jp.go.ndl.lab.annotation.domain.*; 6 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 7 | import jp.go.ndl.lab.annotation.service.ThumbService; 8 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 9 | import jp.go.ndl.lab.annotation.utils.PascalVoxToAnnotationObject; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.io.FileUtils; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.awt.*; 17 | import java.io.File; 18 | 19 | @Component("add-image") 20 | @Slf4j 21 | public class AddImageBatch extends AbstractBatch { 22 | 23 | public static void main(String[] args) { 24 | Application.main(new String[]{"batch", "add-image", "kotenseki", "ignore\\input", "true"}); 25 | } 26 | 27 | 28 | @Autowired 29 | private ThumbService ts; 30 | 31 | @Autowired 32 | private EsDataStore binderEsDataStore; 33 | @Autowired 34 | private EsDataStore imageEsDataStore; 35 | @Autowired 36 | private EsDataStore typeEsDataStore; 37 | 38 | @Autowired 39 | private ThumbService thumbService; 40 | 41 | @Override 42 | public void run(String[] params) { 43 | ImageType type = typeEsDataStore.get(params[0]); 44 | if (type == null) { 45 | log.error("タイプ:{}は存在しません", params[1]); 46 | return; 47 | } 48 | File baseDir = new File(params[1]); 49 | 50 | boolean forceUpdate = params.length >= 3 && Boolean.parseBoolean(params[2]); 51 | ObjectMapper om = new ObjectMapper(); 52 | 53 | 54 | for (File dir : baseDir.listFiles()) { 55 | log.info("start {}", dir); 56 | try { 57 | ImageBinder binder = om.readValue(FileUtils.getFile(dir, "binder.json"), ImageBinder.class); 58 | if (StringUtils.isBlank(binder.id)) { 59 | log.error("{}のbinder.jsonにIDがありません", dir); 60 | continue; 61 | } 62 | ImageBinder current = binderEsDataStore.get(binder.id); 63 | if (current != null && !forceUpdate) { 64 | log.info("ID:{}は登録済みです", current.id); 65 | } 66 | log.info("{}",binder); 67 | 68 | binder.imageType = type.id; 69 | binder.status = Status.WORKING; 70 | 71 | int count = 0; 72 | for (File image : binder.toPath().toFile().listFiles()) { 73 | count++; 74 | TargetImage img = new TargetImage(); 75 | img.id = img.name = image.getName(); 76 | img.binder = binder.id; 77 | img.imageType = type.id; 78 | img.imagePath = image.getPath(); 79 | 80 | File xml = FileUtils.getFile(dir, "xml", image.getName().replace(".jpg", ".xml")); 81 | if (xml.exists()) { 82 | //Annotationの追加 83 | img.annotations = PascalVoxToAnnotationObject.convert(xml); 84 | img.status = Status.ESTIMATED; 85 | } else { 86 | log.info("{}についてXMLが見つかりません", image); 87 | img.status = Status.CREATED; 88 | } 89 | Dimension d = thumbService.createThumbnail(type, img); 90 | img.size = new ImageSize(d.width, d.height, 3); 91 | imageEsDataStore.create(img, false); 92 | } 93 | binder.images = count; 94 | 95 | binderEsDataStore.create(binder, false); 96 | } catch (Exception e) { 97 | log.error("JSONファイルが不正です@" + dir, e); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/batch/AddType.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.batch; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import jp.go.ndl.lab.annotation.Application; 5 | import jp.go.ndl.lab.annotation.domain.ImageType; 6 | import jp.go.ndl.lab.annotation.domain.TargetImage; 7 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 8 | import jp.go.ndl.lab.annotation.service.ThumbService; 9 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.io.File; 15 | import java.io.IOException; 16 | 17 | @Component("add-type") 18 | @Slf4j 19 | public class AddType extends AbstractBatch { 20 | 21 | public static void main(String[] args) { 22 | Application.main(new String[]{"batch", "add-type","ignore\\type.json"}); 23 | } 24 | 25 | @Autowired 26 | private EsDataStore typeEsDataStore; 27 | 28 | @Override 29 | public void run(String[] params) { 30 | try { 31 | ImageType type = new ObjectMapper().readValue(new File(params[0]), ImageType.class); 32 | type = typeEsDataStore.create(type,true); 33 | log.info("タイプを更新しました:{}",type); 34 | } catch (IOException e) { 35 | log.error("多分JSONファイルに間違いがあります。"); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/batch/BackupBatch.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package jp.go.ndl.lab.annotation.batch; 7 | 8 | import com.fasterxml.jackson.databind.ObjectMapper; 9 | import jp.go.ndl.lab.annotation.Application; 10 | import jp.go.ndl.lab.annotation.domain.ImageBinder; 11 | import jp.go.ndl.lab.annotation.domain.TargetImage; 12 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 13 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 14 | import jp.go.ndl.lab.annotation.utils.RWUtils; 15 | import lombok.extern.slf4j.Slf4j; 16 | import org.elasticsearch.index.query.QueryBuilders; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Component; 19 | 20 | import java.nio.file.Paths; 21 | import java.time.ZonedDateTime; 22 | import java.time.format.DateTimeFormatter; 23 | 24 | @Slf4j 25 | @Component("backup") 26 | public class BackupBatch extends AbstractBatch { 27 | 28 | public static void main(String[] args) { 29 | Application.main(Application.MODE_BATCH, "backup"); 30 | } 31 | 32 | @Autowired 33 | private EsDataStore imageEsDataStore; 34 | @Autowired 35 | private EsDataStore binderEsDataStore; 36 | 37 | @Override 38 | public void run(String[] params) { 39 | String today = DateTimeFormatter.ofPattern("yyyyMMdd").format(ZonedDateTime.now()); 40 | 41 | ObjectMapper om = new ObjectMapper(); 42 | try (RWUtils.Writer writer = RWUtils.writer(Paths.get("backup", today, "image.jsonl"))) { 43 | imageEsDataStore.scroll(QueryBuilders.matchAllQuery(), b -> { 44 | try { 45 | writer.writeLine(om.writeValueAsString(b)); 46 | } catch (Exception ex) { 47 | ex.printStackTrace(); 48 | } 49 | }); 50 | } catch (Exception e) { 51 | log.error("", e); 52 | } 53 | try (RWUtils.Writer writer = RWUtils.writer(Paths.get("backup", today,"binder.jsonl"))) { 54 | binderEsDataStore.scroll(QueryBuilders.matchAllQuery(), b -> { 55 | try { 56 | writer.writeLine(om.writeValueAsString(b)); 57 | } catch (Exception ex) { 58 | ex.printStackTrace(); 59 | } 60 | }); 61 | } catch (Exception e) { 62 | log.error("", e); 63 | } 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/batch/ExportAnnotBatch.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.batch; 2 | 3 | import jp.go.ndl.lab.annotation.Application; 4 | import jp.go.ndl.lab.annotation.domain.ImageBinder; 5 | import jp.go.ndl.lab.annotation.domain.Status; 6 | import jp.go.ndl.lab.annotation.domain.TargetImage; 7 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 8 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.elasticsearch.index.query.QueryBuilders; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | import org.thymeleaf.TemplateEngine; 14 | import org.thymeleaf.context.Context; 15 | import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; 16 | 17 | import java.io.FileWriter; 18 | import java.io.Writer; 19 | import java.nio.file.Files; 20 | import java.nio.file.Path; 21 | import java.nio.file.Paths; 22 | 23 | @Slf4j 24 | @Component("export") 25 | public class ExportAnnotBatch extends AbstractBatch { 26 | public static void main(String[] args) { 27 | Application.main("batch", "export", "all", "ignore\\output"); 28 | } 29 | 30 | @Autowired 31 | private EsDataStore imageEsDataStore; 32 | @Autowired 33 | private EsDataStore binderEsDataStore; 34 | 35 | @Override 36 | public void run(String[] params) { 37 | 38 | String type = params[0]; 39 | Path output = Paths.get(params[1]); 40 | 41 | try { 42 | Files.createDirectories(output); 43 | 44 | final TemplateEngine templateEngine = new TemplateEngine(); 45 | ClassLoaderTemplateResolver resolver = new ClassLoaderTemplateResolver(); 46 | resolver.setTemplateMode("XML"); 47 | resolver.setPrefix("templates/"); 48 | resolver.setSuffix(".xml"); 49 | templateEngine.setTemplateResolver(resolver); 50 | 51 | 52 | imageEsDataStore.scroll(QueryBuilders.matchAllQuery(), b -> { 53 | if ((b.imageType.equals(type) || type.equals("all")) && b.status == Status.ANNOTATED) { 54 | log.info("export {}", b.id); 55 | try { 56 | Path file = output.resolve(b.id.replace(".jpg", ".xml")); 57 | final Context ctx = new Context(); 58 | ctx.setVariable("i", b); 59 | final Writer writer = new FileWriter(file.toString()); 60 | templateEngine.process("voc", ctx, writer); 61 | writer.close(); 62 | } catch (Exception ex) { 63 | ex.printStackTrace(); 64 | } 65 | 66 | } 67 | }); 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/batch/ListUnannotatedBatch.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.batch; 2 | 3 | import jp.go.ndl.lab.annotation.Application; 4 | import jp.go.ndl.lab.annotation.domain.ImageBinder; 5 | import jp.go.ndl.lab.annotation.domain.Status; 6 | import jp.go.ndl.lab.annotation.domain.TargetImage; 7 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 8 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 9 | import jp.go.ndl.lab.annotation.utils.RWUtils; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.lang3.StringUtils; 12 | import org.elasticsearch.index.query.QueryBuilders; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Component; 15 | 16 | import java.io.IOException; 17 | import java.nio.file.Paths; 18 | 19 | 20 | @Slf4j 21 | @Component("list-unfinished") 22 | public class ListUnannotatedBatch extends AbstractBatch { 23 | 24 | public static void main(String[] args) { 25 | Application.main("batch", "list-unfinished", "out.txt"); 26 | } 27 | 28 | 29 | @Autowired 30 | private EsDataStore imageEsDataStore; 31 | @Autowired 32 | private EsDataStore binderEsDataStore; 33 | 34 | @Override 35 | public void run(String[] params) { 36 | log.info("write to {}", params[0]); 37 | try (RWUtils.Writer writer = RWUtils.writer(Paths.get(params[0]))) { 38 | binderEsDataStore.scroll(QueryBuilders.matchAllQuery(), b -> { 39 | if (b.status != Status.CHECKED && StringUtils.isEmpty(b.holder)) { 40 | try { 41 | writer.writeLine(b.id); 42 | } catch (IOException e) { 43 | log.error("", e); 44 | } 45 | } 46 | }); 47 | } catch (Exception e) { 48 | log.error("", e); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/batch/UpdateLayoutBatch.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.batch; 2 | 3 | 4 | import jp.go.ndl.lab.annotation.Application; 5 | import jp.go.ndl.lab.annotation.domain.*; 6 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 7 | import jp.go.ndl.lab.annotation.service.ThumbService; 8 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 9 | import jp.go.ndl.lab.annotation.utils.PascalVoxToAnnotationObject; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.io.File; 15 | 16 | @Slf4j 17 | @Component("update-layout") 18 | public class UpdateLayoutBatch extends AbstractBatch { 19 | 20 | public static void main(String[] args) { 21 | Application.main("batch", "update-layout", "ignore\\input\\2544473\\xml"); 22 | } 23 | 24 | 25 | @Autowired 26 | private EsDataStore imageEsDataStore; 27 | @Autowired 28 | private EsDataStore annotationTypeEsDataStore; 29 | @Autowired 30 | private ThumbService thumbService; 31 | 32 | @Override 33 | public void run(String[] params) { 34 | File xmls = new File(params[0]); 35 | for (File xml : xmls.listFiles()) { 36 | TargetImage image = imageEsDataStore.get(xml.getName().replace(".xml", ".jpg")); 37 | if (image != null) { 38 | if (image.status != Status.ANNOTATED) { 39 | image.annotations = PascalVoxToAnnotationObject.convert(xml); 40 | imageEsDataStore.update(image,false); 41 | ImageType type = annotationTypeEsDataStore.get(image.imageType); 42 | try { 43 | thumbService.createThumbnail(type, image); 44 | } catch (Exception e) { 45 | log.error("", e); 46 | } 47 | log.info("{}は更新されました。", image.id); 48 | } else { 49 | log.info("{}は人手で修正済みです。", image.id); 50 | } 51 | }else{ 52 | log.info("{}は未登録です。", xml); 53 | } 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/controller/BinderController.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.controller; 2 | 3 | import jp.go.ndl.lab.annotation.Application; 4 | import jp.go.ndl.lab.annotation.domain.ImageBinder; 5 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 6 | import jp.go.ndl.lab.annotation.infra.EsFacet; 7 | import jp.go.ndl.lab.annotation.infra.EsSearchQuery; 8 | import jp.go.ndl.lab.annotation.infra.EsSearchResult; 9 | import jp.go.ndl.lab.annotation.service.PathService; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.elasticsearch.search.aggregations.AggregationBuilders; 12 | import org.elasticsearch.search.aggregations.bucket.terms.Terms; 13 | import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder; 14 | import org.elasticsearch.search.builder.SearchSourceBuilder; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.context.annotation.Profile; 17 | import org.springframework.util.MultiValueMap; 18 | import org.springframework.web.bind.annotation.*; 19 | 20 | import java.io.IOException; 21 | 22 | @RestController 23 | @RequestMapping("/api/binder") 24 | @Slf4j 25 | @Profile(Application.MODE_WEB) 26 | public class BinderController { 27 | 28 | @Autowired 29 | private EsDataStore dataStore; 30 | 31 | @PostMapping(path = "/create") 32 | public ImageBinder create(@RequestBody ImageBinder image) throws Exception { 33 | log.info("{}", image); 34 | return dataStore.create(image, true); 35 | } 36 | 37 | @PostMapping(path = "/update") 38 | public ImageBinder update(@RequestBody ImageBinder book) throws Exception { 39 | log.info("{}", book); 40 | return dataStore.update(book, true); 41 | } 42 | 43 | @GetMapping(path = "/{id}") 44 | public ImageBinder get(@PathVariable("id") String id) { 45 | log.info("{}", id); 46 | return dataStore.get(id); 47 | } 48 | 49 | @Autowired 50 | private PathService ps; 51 | 52 | @GetMapping("search") 53 | public EsSearchResult search(@RequestParam MultiValueMap query) throws IOException { 54 | log.info("{}", query); 55 | SearchSourceBuilder ssb = EsSearchQuery.readQuery(query).createSearchSource(); 56 | 57 | TermsAggregationBuilder termFacetAgg = AggregationBuilders.terms("status"); 58 | termFacetAgg.field("status"); 59 | termFacetAgg.size(200); 60 | ssb.aggregation(termFacetAgg); 61 | 62 | termFacetAgg = AggregationBuilders.terms("tags"); 63 | termFacetAgg.field("tags"); 64 | termFacetAgg.size(200); 65 | ssb.aggregation(termFacetAgg); 66 | 67 | termFacetAgg = AggregationBuilders.terms("imageType"); 68 | termFacetAgg.field("imageType"); 69 | termFacetAgg.size(200); 70 | ssb.aggregation(termFacetAgg); 71 | 72 | termFacetAgg = AggregationBuilders.terms("holder"); 73 | termFacetAgg.field("holder"); 74 | termFacetAgg.size(200); 75 | ssb.aggregation(termFacetAgg); 76 | 77 | EsSearchResult r = dataStore.search(ssb); 78 | 79 | r.searchResponse.getAggregations().forEach(agg -> { 80 | Terms terms = (Terms) agg; 81 | String name = agg.getName(); 82 | EsFacet facet = new EsFacet(name); 83 | for (Terms.Bucket b : terms.getBuckets()) { 84 | String key = b.getKeyAsString(); 85 | long count = b.getDocCount(); 86 | facet.addCount(key, count); 87 | } 88 | r.facets.put(name, facet); 89 | }); 90 | 91 | return r; 92 | } 93 | 94 | @PostMapping("search") 95 | public EsSearchResult search(@RequestBody EsSearchQuery query) throws IOException { 96 | log.info("{}", query); 97 | return dataStore.search(query.createSearchSource()); 98 | } 99 | 100 | @Autowired 101 | PathService pathService; 102 | 103 | } 104 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/controller/ImageController.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.controller; 2 | 3 | import java.io.IOException; 4 | import java.io.InputStream; 5 | import java.nio.file.Files; 6 | import java.nio.file.Path; 7 | import jp.go.ndl.lab.annotation.Application; 8 | import jp.go.ndl.lab.annotation.domain.ImageType; 9 | import jp.go.ndl.lab.annotation.domain.TargetImage; 10 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 11 | import jp.go.ndl.lab.annotation.infra.EsSearchQuery; 12 | import jp.go.ndl.lab.annotation.infra.EsSearchResult; 13 | import jp.go.ndl.lab.annotation.service.PathService; 14 | import jp.go.ndl.lab.annotation.service.ThumbService; 15 | import lombok.extern.slf4j.Slf4j; 16 | import org.apache.commons.io.IOUtils; 17 | import org.elasticsearch.search.builder.SearchSourceBuilder; 18 | import org.elasticsearch.search.sort.SortOrder; 19 | import org.springframework.beans.factory.annotation.Autowired; 20 | import org.springframework.context.annotation.Profile; 21 | import org.springframework.http.HttpEntity; 22 | import org.springframework.http.HttpHeaders; 23 | import org.springframework.http.MediaType; 24 | import org.springframework.util.MultiValueMap; 25 | import org.springframework.web.bind.annotation.GetMapping; 26 | import org.springframework.web.bind.annotation.PathVariable; 27 | import org.springframework.web.bind.annotation.PostMapping; 28 | import org.springframework.web.bind.annotation.RequestBody; 29 | import org.springframework.web.bind.annotation.RequestMapping; 30 | import org.springframework.web.bind.annotation.RequestParam; 31 | import org.springframework.web.bind.annotation.RestController; 32 | 33 | @RestController 34 | @RequestMapping("/api/image") 35 | @Slf4j 36 | @Profile(Application.MODE_WEB) 37 | public class ImageController { 38 | 39 | @Autowired 40 | private EsDataStore dataStore; 41 | 42 | @PostMapping(path = "/create") 43 | public TargetImage create(@RequestBody TargetImage image) throws Exception { 44 | log.info("{}", image); 45 | return dataStore.create(image, true); 46 | } 47 | 48 | @Autowired 49 | private EsDataStore typeStore; 50 | 51 | @Autowired 52 | private ThumbService thumbService; 53 | 54 | @PostMapping(path = "/update") 55 | public TargetImage update(@RequestBody TargetImage book) throws Exception { 56 | log.info("{}", book); 57 | ImageType type = typeStore.get(book.imageType); 58 | TargetImage img = dataStore.update(book, true); 59 | thumbService.asyncCreateThumbnail(type, img); 60 | return img; 61 | } 62 | 63 | @GetMapping(path = "/{id}") 64 | public TargetImage get(@PathVariable("id") String id) { 65 | log.info("{}", id); 66 | return dataStore.get(id); 67 | } 68 | 69 | @GetMapping(path = "/file/{name}") 70 | public HttpEntity getImage(@PathVariable("name") String name) throws IOException { 71 | TargetImage tg = dataStore.get(name); 72 | return returnImage(tg.toPath()); 73 | } 74 | 75 | @GetMapping(path = "/thumb/{name}") 76 | public HttpEntity getThumb(@PathVariable("name") String name) throws IOException { 77 | TargetImage tg = dataStore.get(name); 78 | return returnImage(ps.thumbFile(tg)); 79 | } 80 | 81 | private HttpEntity returnImage(Path p) throws IOException { 82 | HttpHeaders headers = new HttpHeaders(); 83 | headers.setContentType(MediaType.IMAGE_JPEG); 84 | if (!Files.exists(p)) { 85 | headers.setContentLength(0); 86 | log.error("file not found {}", p); 87 | return new HttpEntity<>(new byte[0], headers); 88 | } 89 | byte[] bt = null; 90 | try (InputStream is = Files.newInputStream(p)) { 91 | bt = IOUtils.toByteArray(is); 92 | } 93 | headers.setContentLength(bt.length); 94 | return new HttpEntity<>(bt, headers); 95 | } 96 | 97 | @Autowired 98 | private PathService ps; 99 | 100 | @GetMapping("search") 101 | public EsSearchResult search(@RequestParam MultiValueMap query) throws IOException { 102 | log.info("{}", query); 103 | SearchSourceBuilder ssb = EsSearchQuery.readQuery(query).createSearchSource(); 104 | ssb.sort("name", SortOrder.ASC); 105 | 106 | EsSearchResult r = dataStore.search(ssb); 107 | 108 | return r; 109 | } 110 | 111 | @PostMapping("search") 112 | public EsSearchResult search(@RequestBody EsSearchQuery query) throws IOException { 113 | log.info("{}", query); 114 | return dataStore.search(query.createSearchSource()); 115 | } 116 | 117 | @Autowired 118 | PathService pathService; 119 | 120 | } 121 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/controller/ServletController.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.controller; 2 | 3 | import jp.go.ndl.lab.annotation.Application; 4 | import lombok.extern.slf4j.Slf4j; 5 | import org.springframework.context.annotation.Profile; 6 | import org.springframework.stereotype.Controller; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | 10 | @Controller 11 | @RequestMapping("/app") 12 | @Slf4j 13 | @Profile({Application.MODE_WEB}) 14 | public class ServletController { 15 | 16 | @GetMapping(path = "/**") 17 | public String staticPageRootLevel() { 18 | return "index"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/controller/TypeController.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.controller; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | import jp.go.ndl.lab.annotation.Application; 7 | import jp.go.ndl.lab.annotation.domain.ImageType; 8 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 9 | import jp.go.ndl.lab.annotation.infra.EsSearchQuery; 10 | import jp.go.ndl.lab.annotation.infra.EsSearchResult; 11 | import lombok.extern.slf4j.Slf4j; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.context.annotation.Profile; 14 | import org.springframework.util.MultiValueMap; 15 | import org.springframework.web.bind.annotation.GetMapping; 16 | import org.springframework.web.bind.annotation.PathVariable; 17 | import org.springframework.web.bind.annotation.PostMapping; 18 | import org.springframework.web.bind.annotation.RequestBody; 19 | import org.springframework.web.bind.annotation.RequestMapping; 20 | import org.springframework.web.bind.annotation.RequestParam; 21 | import org.springframework.web.bind.annotation.RestController; 22 | 23 | @RestController 24 | @RequestMapping("/api/type") 25 | @Slf4j 26 | @Profile(Application.MODE_WEB) 27 | public class TypeController { 28 | 29 | @Autowired 30 | private EsDataStore dataStore; 31 | 32 | @PostMapping(path = "/update") 33 | public ImageType update(@RequestBody ImageType project) throws Exception { 34 | log.info("{}", project); 35 | return dataStore.update(project, true); 36 | } 37 | 38 | @GetMapping(path = "/{id}") 39 | public ImageType get(@PathVariable("id") String id) { 40 | log.info("{}", id); 41 | return dataStore.get(id); 42 | } 43 | 44 | @PostMapping(path = "/delete") 45 | public void delete(@RequestParam("id") String id) { 46 | log.info("{}", id); 47 | dataStore.delete(id); 48 | } 49 | 50 | @GetMapping(path = "/all") 51 | public List all() { 52 | log.info(""); 53 | return new ArrayList<>(dataStore.getAll().values()); 54 | } 55 | 56 | @GetMapping("search") 57 | public EsSearchResult search(@RequestParam MultiValueMap query) throws IOException { 58 | log.info("{}", query); 59 | return dataStore.search(EsSearchQuery.readQuery(query).createSearchSource()); 60 | } 61 | 62 | @PostMapping("search") 63 | public EsSearchResult search(@RequestBody EsSearchQuery query) throws IOException { 64 | log.info("{}", query); 65 | return dataStore.search(query.createSearchSource()); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/Action.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import java.util.Date; 5 | import lombok.Data; 6 | 7 | @Data 8 | @JsonInclude(JsonInclude.Include.NON_NULL) 9 | public class Action { 10 | 11 | private Status type; 12 | private String note; 13 | private String user; 14 | private Date start; 15 | private Date end; 16 | 17 | public Action() { 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/AnnotationObject.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | @Data 7 | @JsonInclude(JsonInclude.Include.NON_NULL) 8 | public class AnnotationObject { 9 | 10 | public String id; 11 | public String name; 12 | public Bndbox bndbox; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/AnnotationType.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | @Data 7 | @JsonInclude(JsonInclude.Include.NON_NULL) 8 | public class AnnotationType { 9 | 10 | public String id; 11 | public String name; 12 | public String description; 13 | public Boolean large; 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/Bndbox.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | @Data 7 | @JsonInclude(JsonInclude.Include.NON_NULL) 8 | public class Bndbox { 9 | 10 | public Bndbox(int xmin, int ymin, int xmax, int ymax) { 11 | this.xmin = xmin; 12 | this.ymin = ymin; 13 | this.xmax = xmax; 14 | this.ymax = ymax; 15 | } 16 | 17 | public Bndbox() { 18 | } 19 | 20 | public int xmin; 21 | public int ymin; 22 | public int xmax; 23 | public int ymax; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/ImageBinder.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | import jp.go.ndl.lab.annotation.infra.ESData; 10 | import lombok.Data; 11 | 12 | @Data 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | public class ImageBinder implements ESData { 15 | 16 | public String id; 17 | public Long version; 18 | public String name; 19 | public String imageType; 20 | public List tags; 21 | public Status status; 22 | public String seeAlso; 23 | public int images; 24 | public String holder; 25 | public String imagePath; 26 | 27 | public List actions = new ArrayList<>(); 28 | 29 | public Path toPath(){ 30 | return Paths.get(this.imagePath); 31 | } 32 | 33 | public void addTag(String tag) { 34 | if (tags == null) tags = new ArrayList<>(); 35 | if (!tags.contains(tag)) tags.add(tag); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/ImageSize.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.Data; 5 | 6 | @Data 7 | @JsonInclude(JsonInclude.Include.NON_NULL) 8 | public class ImageSize { 9 | 10 | public ImageSize() { 11 | } 12 | 13 | public ImageSize(int width, int height, int depth) { 14 | this.width = width; 15 | this.height = height; 16 | this.depth = depth; 17 | } 18 | 19 | public int width; 20 | public int height; 21 | public int depth; 22 | } 23 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/ImageType.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | 5 | import java.awt.*; 6 | import java.util.ArrayList; 7 | import java.util.HashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import java.util.stream.Collectors; 11 | 12 | import jp.go.ndl.lab.annotation.infra.ESData; 13 | import jp.go.ndl.lab.annotation.utils.ColorSchema; 14 | import lombok.Data; 15 | 16 | @Data 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | public class ImageType implements ESData { 19 | 20 | public String id; 21 | public Long version; 22 | public String name; 23 | public List types = new ArrayList<>(); 24 | 25 | public Map colorMap() { 26 | List typess = types.stream().filter(ts -> !ts.id.contains("overall")).map(ts -> ts.id).collect(Collectors.toList()); 27 | Map colors = new HashMap<>(); 28 | for (int i = 0; i < typess.size(); i++) { 29 | colors.put(typess.get(i), ColorSchema.getColor(i)); 30 | } 31 | return colors; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/Status.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | public enum Status { 4 | WORKING,CREATED, ESTIMATED, ANNOTATED, CHECKED 5 | } 6 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/domain/TargetImage.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.domain; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import jp.go.ndl.lab.annotation.infra.ESData; 5 | import lombok.Data; 6 | 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | @Data 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | public class TargetImage implements ESData { 15 | 16 | public Long version; 17 | public String id; 18 | public String binder; 19 | public String name; 20 | public String imageType; 21 | public List tags; 22 | public ImageSize size = new ImageSize(); 23 | public String annotator; 24 | 25 | public String imagePath; 26 | 27 | public Path toPath(){ 28 | return Paths.get(imagePath); 29 | } 30 | 31 | public String note; 32 | 33 | public double bwThreshold = 0; 34 | 35 | public Status status; 36 | 37 | public List annotations = new ArrayList<>(); 38 | 39 | public void addTag(String tag) { 40 | if (tags == null) tags = new ArrayList<>(); 41 | if (!tags.contains(tag)) tags.add(tag); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/BatchRunner.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | import jp.go.ndl.lab.annotation.Application; 8 | import lombok.extern.slf4j.Slf4j; 9 | import org.apache.commons.io.FileUtils; 10 | import org.apache.commons.lang3.ArrayUtils; 11 | import org.springframework.beans.factory.annotation.Value; 12 | import org.springframework.stereotype.Component; 13 | 14 | @Component 15 | @Slf4j 16 | public class BatchRunner { 17 | 18 | private String[] batchCommand; 19 | 20 | public BatchRunner(@Value("${command}") String convertCommand) { 21 | this.batchCommand = convertCommand.split("\\s"); 22 | } 23 | 24 | public void run(String batchName, String... parameters) { 25 | try { 26 | log.info("run {} {}", batchName, parameters); 27 | File convertLog = FileUtils.getFile("log", "batch", batchName + "-" + new SimpleDateFormat("yyyyMMdd-HHmmss").format(new Date()) + ".log"); 28 | String[] command = ArrayUtils.addAll(batchCommand, Application.MODE_BATCH, batchName); 29 | command = ArrayUtils.addAll(command, parameters); 30 | new ProcessBuilder(command) 31 | .redirectError(ProcessBuilder.Redirect.to(convertLog)) 32 | .redirectOutput(ProcessBuilder.Redirect.to(convertLog)) 33 | .start(); 34 | } catch (IOException ex) { 35 | log.error("予期せぬIOエラー", ex); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/ESData.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | public interface ESData { 4 | 5 | String getId(); 6 | 7 | void setId(String id); 8 | 9 | Long getVersion(); 10 | 11 | void setVersion(Long l); 12 | 13 | } 14 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/EsBulkIndexer.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import java.io.Closeable; 6 | import java.io.IOException; 7 | import java.util.HashMap; 8 | import java.util.Map; 9 | import lombok.extern.slf4j.Slf4j; 10 | import org.apache.commons.lang3.StringUtils; 11 | 12 | @Slf4j 13 | public class EsBulkIndexer implements Closeable { 14 | 15 | public enum Type { 16 | INDEX, DELETE; 17 | } 18 | 19 | private EsDataStore dataStore; 20 | private int bulkSize; 21 | private Type type; 22 | 23 | public EsBulkIndexer(EsDataStore dataStore, int bulkSize) { 24 | this(dataStore, bulkSize, Type.INDEX); 25 | } 26 | 27 | public EsBulkIndexer(EsDataStore dataStore, int bulkSize, Type type) { 28 | this.dataStore = dataStore; 29 | this.bulkSize = bulkSize; 30 | this.type = type; 31 | } 32 | 33 | private Map map = new HashMap<>(); 34 | 35 | public void addJson(String id, String json) { 36 | if (StringUtils.isBlank(id) || StringUtils.isBlank(json)) { 37 | log.warn("invalid input {} {}", id, json); 38 | } else { 39 | map.put(id, json); 40 | if (map.size() >= bulkSize) flush(); 41 | } 42 | } 43 | ObjectMapper om = new ObjectMapper(); 44 | 45 | public void add(String id, Object value) { 46 | try { 47 | map.put(id, om.writeValueAsString(value)); 48 | if (map.size() >= bulkSize) flush(); 49 | } catch (JsonProcessingException ex) { 50 | log.error("", ex); 51 | } 52 | } 53 | 54 | public void delete(String id) { 55 | map.put(id, null); 56 | } 57 | 58 | public void flush() { 59 | if (!map.isEmpty()) { 60 | if (type == Type.DELETE) { 61 | dataStore.bulkDelete(map.keySet()); 62 | } else { 63 | dataStore.bulkIndex(map); 64 | } 65 | map.clear(); 66 | } 67 | } 68 | 69 | @Override 70 | public void close() throws IOException { 71 | flush(); 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/EsDataStoreFactory.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | import org.springframework.beans.factory.annotation.Value; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | public class EsDataStoreFactory { 8 | 9 | private String host; 10 | private int port; 11 | private String path; 12 | 13 | public EsDataStoreFactory(@Value("${es.host}") String host, @Value("${es.port}") int port, @Value("${es.path}") String path) { 14 | this.host = host; 15 | this.port = port; 16 | this.path = path; 17 | } 18 | 19 | public EsDataStore build(Class clazz) { 20 | return new EsDataStore(host, port, path, "ja_" + clazz.getSimpleName().toLowerCase(), clazz); 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/EsFacet.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public class EsFacet { 7 | 8 | public EsFacet() { 9 | } 10 | 11 | public EsFacet(String field) { 12 | this.field = field; 13 | } 14 | 15 | public String field; 16 | public Map counts = new LinkedHashMap<>(); 17 | 18 | public void addCount(String key, long count) { 19 | Long current = counts.get(key); 20 | if (current == null) { 21 | current = 0L; 22 | } 23 | counts.put(key, (current + count)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/EsSearchResult.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnore; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import java.util.ArrayList; 6 | import java.util.Iterator; 7 | import java.util.LinkedHashMap; 8 | import java.util.List; 9 | import java.util.Map; 10 | import org.elasticsearch.action.search.SearchResponse; 11 | 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | public class EsSearchResult implements Iterable { 14 | 15 | public EsSearchResult() { 16 | } 17 | 18 | @Override 19 | public Iterator iterator() { 20 | return list.iterator(); 21 | } 22 | 23 | public List list = new ArrayList<>(); 24 | public long hit; 25 | public int from; 26 | 27 | public Map facets = new LinkedHashMap<>(); 28 | 29 | @JsonIgnore 30 | public SearchResponse searchResponse; 31 | 32 | @Override 33 | public String toString() { 34 | return "SearchResult{" + "list=" + list + ", hit=" + hit + ", from=" + from + '}'; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/infra/GenericEsClient.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.infra; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Files; 8 | import java.nio.file.Path; 9 | import java.util.List; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.apache.commons.io.IOUtils; 12 | import org.apache.commons.lang3.StringUtils; 13 | import org.apache.http.HttpEntity; 14 | import org.apache.http.HttpHost; 15 | import org.apache.http.entity.ContentType; 16 | import org.apache.http.nio.entity.NStringEntity; 17 | import org.elasticsearch.action.delete.DeleteRequest; 18 | import org.elasticsearch.client.Request; 19 | import org.elasticsearch.client.RequestOptions; 20 | import org.elasticsearch.client.Response; 21 | import org.elasticsearch.client.RestClient; 22 | import org.elasticsearch.client.RestClientBuilder; 23 | import org.elasticsearch.client.RestHighLevelClient; 24 | import org.elasticsearch.index.query.QueryBuilder; 25 | import org.springframework.beans.factory.annotation.Value; 26 | import org.springframework.stereotype.Component; 27 | 28 | @Component 29 | @Slf4j 30 | public class GenericEsClient { 31 | 32 | public RestClient restClient; 33 | public RestHighLevelClient highClient; 34 | 35 | public GenericEsClient(@Value("${es.host}") String host, @Value("${es.port}") int port, @Value("${es.path}") String path) { 36 | RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, "http")).setRequestConfigCallback(c -> { 37 | return c.setConnectTimeout(5000).setSocketTimeout(60000); 38 | }); 39 | if (StringUtils.isNotBlank(path)) { 40 | builder.setPathPrefix(path); 41 | } 42 | restClient = builder.build(); 43 | highClient = new RestHighLevelClient(builder); 44 | } 45 | 46 | public String issueGet(String command) throws Exception { 47 | log.info("GET {}", command); 48 | Response r = restClient.performRequest(new Request("GET", "/" + command)); 49 | String content = null; 50 | try (InputStream is = r.getEntity().getContent()) { 51 | content = IOUtils.toString(is, StandardCharsets.UTF_8); 52 | } 53 | return content; 54 | } 55 | 56 | public String issuePut(String command) throws Exception { 57 | log.info("PUT {}", command); 58 | Response r = restClient.performRequest(new Request( 59 | "PUT", 60 | "/" + command)); 61 | String content = null; 62 | try (InputStream is = r.getEntity().getContent()) { 63 | content = IOUtils.toString(is, StandardCharsets.UTF_8); 64 | } 65 | return content; 66 | } 67 | 68 | public String issueDelete(String command) throws Exception { 69 | log.info("DELETE {}", command); 70 | Response r = restClient.performRequest(new Request( 71 | "DELETE", 72 | "/" + command)); 73 | String content = null; 74 | try (InputStream is = r.getEntity().getContent()) { 75 | content = IOUtils.toString(is, StandardCharsets.UTF_8); 76 | } 77 | return content; 78 | } 79 | 80 | /* 81 | 1行目にパスとメソッド、二行目以降にJSONを記入した命令ファイルでコマンドを実行する 82 | */ 83 | public String issue(String method, String path, String json) throws Exception { 84 | HttpEntity entity = new NStringEntity(json, ContentType.APPLICATION_JSON); 85 | Request rq = new Request(method, "/" + path); 86 | rq.setEntity(entity); 87 | Response r = restClient.performRequest(rq); 88 | String content = null; 89 | try (InputStream is = r.getEntity().getContent()) { 90 | content = IOUtils.toString(is, StandardCharsets.UTF_8); 91 | } 92 | return content; 93 | } 94 | 95 | /* 96 | 1行目にパスとメソッド、二行目以降にJSONを記入した命令ファイルでコマンドを実行する 97 | */ 98 | public String issue(String method, String path, Path commandFile) throws Exception { 99 | List file = Files.readAllLines(commandFile, StandardCharsets.UTF_8); 100 | HttpEntity entity = new NStringEntity(String.join("", file), ContentType.APPLICATION_JSON); 101 | Request rq = new Request(method, "/" + path); 102 | rq.setEntity(entity); 103 | Response r = restClient.performRequest(rq); 104 | String content = null; 105 | try (InputStream is = r.getEntity().getContent()) { 106 | content = IOUtils.toString(is, StandardCharsets.UTF_8); 107 | } 108 | return content; 109 | } 110 | 111 | /* 112 | 1行目にパスとメソッド、二行目以降にJSONを記入した命令ファイルでコマンドを実行する 113 | */ 114 | public String issue(Path commandFile) throws Exception { 115 | List file = Files.readAllLines(commandFile, StandardCharsets.UTF_8); 116 | String[] order = file.get(0).split(" "); 117 | Response r = null; 118 | if (file.size() > 1) { 119 | file.remove(0); 120 | HttpEntity entity = new NStringEntity(String.join("", file), ContentType.APPLICATION_JSON); 121 | Request rq = new Request(order[0], "/" + order[1]); 122 | rq.setEntity(entity); 123 | r = restClient.performRequest(rq); 124 | } else { 125 | r = restClient.performRequest(new Request(order[0], "/" + order[1])); 126 | } 127 | String content = null; 128 | try (InputStream is = r.getEntity().getContent()) { 129 | content = IOUtils.toString(is, StandardCharsets.UTF_8); 130 | } 131 | return content; 132 | } 133 | 134 | public void deleteByQuery(String index, QueryBuilder queryBuilder) { 135 | try { 136 | String query = "{\"query\":" + org.elasticsearch.common.Strings.toString(queryBuilder, false, false) + "}"; 137 | log.info("delete query: {}", query); 138 | HttpEntity entity = new NStringEntity(query, ContentType.APPLICATION_JSON); 139 | Request r = new Request("POST", "/" + index + "/_delete_by_query"); 140 | r.setEntity(entity); 141 | restClient.performRequest(r); 142 | } catch (JsonProcessingException ex) { 143 | log.error("", ex); 144 | } catch (IOException ex) { 145 | log.error("", ex); 146 | } 147 | } 148 | 149 | public void deleteIndex(String index) throws IOException { 150 | DeleteRequest req = new DeleteRequest(index); 151 | highClient.delete(req, RequestOptions.DEFAULT); 152 | } 153 | 154 | public void close() throws IOException { 155 | restClient.close(); 156 | } 157 | 158 | } 159 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/service/JobScheduler.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.service; 2 | 3 | import jp.go.ndl.lab.annotation.Application; 4 | import jp.go.ndl.lab.annotation.batch.BackupBatch; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Profile; 8 | import org.springframework.scheduling.annotation.Scheduled; 9 | import org.springframework.stereotype.Component; 10 | 11 | @Slf4j 12 | @Profile({Application.MODE_WEB}) 13 | @Component 14 | public class JobScheduler { 15 | 16 | public JobScheduler() { 17 | log.info("init job scheduler"); 18 | } 19 | 20 | @Autowired 21 | private BackupBatch backupBatch; 22 | 23 | @Scheduled(cron = "0 0 1 * * *") 24 | public void run() { 25 | log.info("start backup"); 26 | backupBatch.run(new String[0]); 27 | log.info("done"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/service/PathService.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.service; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Path; 5 | import jp.go.ndl.lab.annotation.domain.TargetImage; 6 | import lombok.extern.slf4j.Slf4j; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.core.io.Resource; 9 | import org.springframework.stereotype.Service; 10 | 11 | @Service 12 | @Slf4j 13 | public class PathService { 14 | 15 | public Path thumbPath; 16 | 17 | public PathService( 18 | @Value("${thumbPath}") Resource thumbPath 19 | ) { 20 | try { 21 | this.thumbPath = thumbPath.getFile().toPath(); 22 | } catch (IOException ex) { 23 | log.error("予期せぬエラー", ex); 24 | } 25 | } 26 | 27 | public Path thumbFile(TargetImage image) { 28 | return this.thumbPath.resolve(image.binder).resolve(image.id); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/service/StoreBeans.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.service; 2 | 3 | import jp.go.ndl.lab.annotation.domain.ImageBinder; 4 | import jp.go.ndl.lab.annotation.domain.ImageType; 5 | import jp.go.ndl.lab.annotation.domain.TargetImage; 6 | import jp.go.ndl.lab.annotation.infra.EsDataStore; 7 | import jp.go.ndl.lab.annotation.infra.EsDataStoreFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | public class StoreBeans { 14 | 15 | @Autowired 16 | EsDataStoreFactory dsFactory; 17 | 18 | @Bean 19 | public EsDataStore imageStore() { 20 | EsDataStore video = dsFactory.build(TargetImage.class); 21 | return video; 22 | } 23 | 24 | @Bean 25 | public EsDataStore binderStore() { 26 | return dsFactory.build(ImageBinder.class); 27 | } 28 | 29 | @Bean 30 | public EsDataStore typeStore() { 31 | return dsFactory.build(ImageType.class); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/service/ThumbService.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.service; 2 | 3 | import java.awt.*; 4 | import java.awt.image.BufferedImage; 5 | import java.util.Map; 6 | import javax.imageio.ImageIO; 7 | import jp.go.ndl.lab.annotation.domain.ImageType; 8 | import jp.go.ndl.lab.annotation.domain.TargetImage; 9 | import jp.go.ndl.lab.annotation.utils.ImageUtil; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.scheduling.annotation.Async; 13 | import org.springframework.stereotype.Service; 14 | 15 | @Service 16 | @Slf4j 17 | public class ThumbService { 18 | 19 | @Autowired 20 | private PathService ps; 21 | 22 | @Async 23 | public void asyncCreateThumbnail(ImageType type, TargetImage image) { 24 | try { 25 | this.createThumbnail(type, image); 26 | } catch (Exception e) { 27 | log.info("", e); 28 | } 29 | } 30 | 31 | public Dimension createThumbnail(ImageType type, TargetImage image) throws Exception { 32 | BufferedImage img = ImageIO.read(image.toPath().toFile()); 33 | Graphics2D g2 = img.createGraphics(); 34 | final Map colorMap = type.colorMap(); 35 | 36 | image.annotations.forEach(a -> { 37 | if (a.name.equals("1_overall")) { 38 | g2.setColor(Color.BLUE); 39 | g2.setStroke(new BasicStroke(4f)); 40 | g2.setComposite(AlphaComposite.Src); 41 | g2.drawRect(a.bndbox.xmin, a.bndbox.ymin, a.bndbox.xmax - a.bndbox.xmin, a.bndbox.ymax - a.bndbox.ymin); 42 | } else { 43 | g2.setColor(colorMap.getOrDefault(a.name, Color.BLACK)); 44 | g2.setComposite(AlphaComposite.SrcOver.derive(0.8f)); 45 | g2.fillRect(a.bndbox.xmin, a.bndbox.ymin, a.bndbox.xmax - a.bndbox.xmin, a.bndbox.ymax - a.bndbox.ymin); 46 | g2.setStroke(new BasicStroke(1f)); 47 | g2.setComposite(AlphaComposite.Src); 48 | g2.setColor(Color.BLACK); 49 | g2.drawRect(a.bndbox.xmin, a.bndbox.ymin, a.bndbox.xmax - a.bndbox.xmin, a.bndbox.ymax - a.bndbox.ymin); 50 | } 51 | }); 52 | ps.thumbFile(image).toFile().getParentFile().mkdirs(); 53 | BufferedImage scaled = ImageUtil.getFasterScaledInstance(img, 300, -1, true); 54 | ImageIO.write(scaled, "jpg", ps.thumbFile(image).toFile()); 55 | img.flush(); 56 | scaled.flush(); 57 | return new Dimension(img.getWidth(), img.getHeight()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/tools/CreateIndexBatch.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.tools; 2 | 3 | import java.io.IOException; 4 | import java.nio.file.Files; 5 | import java.nio.file.Path; 6 | import java.nio.file.Paths; 7 | import jp.go.ndl.lab.annotation.Application; 8 | import jp.go.ndl.lab.annotation.infra.GenericEsClient; 9 | import jp.go.ndl.lab.annotation.utils.AbstractBatch; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.context.annotation.Lazy; 13 | import org.springframework.context.annotation.Profile; 14 | import org.springframework.stereotype.Component; 15 | 16 | /** 17 | * ElasticSearchのIndexを初期化するバッチ 18 | * 19 | */ 20 | @Slf4j 21 | @Component("create-index") 22 | @Profile({Application.MODE_BATCH}) 23 | @Lazy 24 | public class CreateIndexBatch extends AbstractBatch { 25 | 26 | public static void main(String[] args) throws Throwable { 27 | Application.main(Application.MODE_BATCH, "create-index", "all"); 28 | } 29 | 30 | @Autowired 31 | private GenericEsClient esClient; 32 | private Path base = Paths.get("config", "index"); 33 | 34 | @Override 35 | public void run(String[] params) { 36 | try { 37 | if (params[0].equals("all")) { 38 | createAllIndex(); 39 | } else { 40 | createIndex(params[0], base.resolve(params[0] + ".json"), true); 41 | } 42 | } catch (Exception e) { 43 | log.error("{}", e); 44 | } 45 | 46 | } 47 | 48 | private void createAllIndex() throws IOException { 49 | try { 50 | log.info("try delete all"); 51 | esClient.issueDelete("ja_*"); 52 | } catch (Exception e) { 53 | log.error("{}", e); 54 | } 55 | Files.list(base).forEach(path -> { 56 | try { 57 | createIndex(path.getFileName().toString().replace(".json", ""), path, false); 58 | } catch (Exception ex) { 59 | log.error("error {} {}", path); 60 | log.error("", ex); 61 | } 62 | }); 63 | } 64 | 65 | private void createIndex(String indexName, Path file, boolean delete) { 66 | try { 67 | if (delete) { 68 | log.info("try delete {}", indexName); 69 | esClient.issueDelete(indexName); 70 | } 71 | } catch (Exception e) { 72 | } 73 | try { 74 | log.info("try create {}", indexName); 75 | String result = esClient.issue("PUT", indexName, file); 76 | log.info("{}->{}", file, result); 77 | } catch (Exception e) { 78 | log.error("{}", e); 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/AbstractBatch.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | 4 | public abstract class AbstractBatch { 5 | 6 | public abstract void run(String[] params); 7 | 8 | } 9 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/ColorSchema.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import java.awt.*; 4 | 5 | public class ColorSchema { 6 | 7 | public static Color RED = new Color(255, 75, 0); 8 | public static Color YELLOW = new Color(255, 241, 0); 9 | public static Color GREEN = new Color(3, 175, 122); 10 | public static Color BLUE = new Color(0, 90, 255); 11 | public static Color CYAN = new Color(77, 196, 255); 12 | public static Color PINK = new Color(255, 128, 130); 13 | public static Color ORANGE = new Color(246, 170, 0); 14 | public static Color PURPLE = new Color(153, 0, 153); 15 | public static Color BROWN = new Color(128, 64, 0); 16 | 17 | public static Color[] COLORS = {RED, YELLOW, GREEN, BLUE, CYAN, PINK, ORANGE, PURPLE, BROWN}; 18 | 19 | public static Color getColor(int index) { 20 | return COLORS[index % COLORS.length]; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/HashCounter.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collections; 5 | import java.util.HashMap; 6 | import java.util.LinkedHashMap; 7 | import java.util.List; 8 | import java.util.Map; 9 | import java.util.Set; 10 | 11 | public class HashCounter { 12 | 13 | 14 | 15 | public HashCounter() { 16 | } 17 | 18 | public Set getKeySet() { 19 | return countMap.keySet(); 20 | } 21 | 22 | private class Count { 23 | 24 | @Override 25 | public String toString() { 26 | return "" + count; 27 | } 28 | 29 | /** 30 | * 総称型インスタンス 31 | */ 32 | private K key; 33 | 34 | /** 35 | * カウント値 36 | */ 37 | private int count = 0; 38 | 39 | /** 40 | * コンストラクタ 41 | * 42 | * @param key 総称型 43 | */ 44 | Count(K key) { 45 | this.key = key; 46 | } 47 | } 48 | /** 49 | * カウンタ格納マップ 50 | */ 51 | private final HashMap countMap = new LinkedHashMap<>(); 52 | 53 | /** 54 | * カウンタ格納マップの指定したキーに対応するカウントを+1する。 55 | * @param key キー 56 | * @return カウンタ格納マップの指定したキーに対応するカウントを+1 57 | */ 58 | public int countPlus(K key) { 59 | if (key == null) return 0; 60 | Count c = countMap.get(key); 61 | if (c == null) { 62 | c = new Count(key); 63 | countMap.put(key, c); 64 | } 65 | return ++c.count; 66 | } 67 | 68 | /** 69 | * カウンタ格納マップの指定したキーに対応するカウントを-1する。 70 | * @param key キー 71 | */ 72 | public void countMinus(K key) { 73 | if (key == null) return; 74 | Count c = countMap.get(key); 75 | if (c == null) { 76 | c = new Count(key); 77 | countMap.put(key, c); 78 | } 79 | c.count--; 80 | } 81 | 82 | /** 83 | * カウンタ格納マップの指定したキーに対応するカウントに指定したカウントを加算する。 84 | * @param key キー 85 | * @param count カウント 86 | */ 87 | public void countValue(K key, int count) { 88 | if (key == null) return; 89 | Count c = countMap.get(key); 90 | if (c == null) { 91 | c = new Count(key); 92 | countMap.put(key, c); 93 | } 94 | c.count += count; 95 | } 96 | 97 | /** 98 | * カウンタ格納マップの指定したキーに対応するカウントを取得する。 99 | * @param key countMapキー 100 | * @return キーに対応する要素 101 | */ 102 | public int getCount(K key) { 103 | if (key == null) return 0; 104 | Count c = countMap.get(key); 105 | if (c == null) { 106 | return 0; 107 | } else { 108 | return c.count; 109 | } 110 | } 111 | 112 | /** 113 | * カウンタ格納マップからカウントの昇順で取得件数分のカウンタ格納マップを取得する。 114 | * 115 | * @param x 取得件数 116 | * @return 取得件数分のカウンタ格納マップ 117 | */ 118 | public Map topX(int x) { 119 | List countList = new ArrayList<>(countMap.values()); 120 | Collections.sort(countList, (Count o1, Count o2) -> o2.count - o1.count); 121 | 122 | Map map = new LinkedHashMap<>(); 123 | int limit = Math.min(x, countList.size()); 124 | for (int i = 0; i < limit; i++) { 125 | Count count = countList.get(i); 126 | map.put(count.key, count.count); 127 | } 128 | return map; 129 | } 130 | 131 | /** 132 | * 標準出力にカウンタ格納マップに格納されているキーとカウントをカウントの昇順で出力する。 133 | */ 134 | public void printCount() { 135 | List countList = new ArrayList<>(countMap.values()); 136 | Collections.sort(countList, (Count o1, Count o2) -> o1.count - o2.count); 137 | for (Count c : countList) { 138 | System.out.println(c.key + "\t" + c.count); 139 | } 140 | } 141 | 142 | /** 143 | * 標準出力にカウンタ格納マップに格納されているキーとカウント、カウントの合計をカウントの昇順で出力する。 144 | */ 145 | public void printCount2() { 146 | List countList = new ArrayList<>(countMap.values()); 147 | Collections.sort(countList, (Count o1, Count o2) -> o1.count - o2.count); 148 | int i1 = 0; 149 | int i2 = 0; 150 | for (Count c : countList) { 151 | i1 += (Integer) c.count; 152 | int sum = ((Integer) c.key) * c.count; 153 | i2 += sum; 154 | System.out.println(c.key + "\t" + c.count + "\t" + sum); 155 | } 156 | System.out.println("\t" + i1 + "\t" + i2); 157 | } 158 | 159 | /** 160 | * 標準出力にカウンタ格納マップに格納されているキーとカウント、カウントの合計をカウントの降順で出力する。 161 | */ 162 | public void printCount3() { 163 | List countList = new ArrayList<>(countMap.values()); 164 | Collections.sort(countList, (Count o1, Count o2) -> (Integer) o2.key - (Integer) o1.key); 165 | int i1 = 0; 166 | int i2 = 0; 167 | for (Count c : countList) { 168 | i1 += (Integer) c.count; 169 | int sum = ((Integer) c.key) * c.count; 170 | i2 += sum; 171 | System.out.println(c.key + "\t" + c.count + "\t" + sum); 172 | } 173 | System.out.println("\t" + i1 + "\t" + i2); 174 | } 175 | 176 | /** 177 | * カウンタ格納マップに格納されている値の合計値を返却する。 178 | * @return カウント合計値 179 | */ 180 | public int getTotal() { 181 | int total = 0; 182 | for (Count c : countMap.values()) { 183 | total += c.count; 184 | } 185 | return total; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/ImageUtil.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import java.awt.Graphics; 4 | import java.awt.Graphics2D; 5 | import java.awt.GraphicsConfiguration; 6 | import java.awt.RenderingHints; 7 | import java.awt.Transparency; 8 | import java.awt.image.BufferedImage; 9 | import java.util.Arrays; 10 | 11 | public class ImageUtil { 12 | 13 | public static double min(double... values) { 14 | if (values.length == 0) return 0; 15 | Arrays.sort(values); 16 | return values[0]; 17 | } 18 | 19 | public static double max(double... values) { 20 | if (values.length == 0) return 0; 21 | Arrays.sort(values); 22 | return values[values.length - 1]; 23 | } 24 | 25 | public static BufferedImage toCompatibleImage(GraphicsConfiguration gc, BufferedImage img) { 26 | if (img.getColorModel().equals(gc.getColorModel())) { 27 | return img; 28 | } 29 | BufferedImage compatibleImage = gc.createCompatibleImage(img.getWidth(), img.getHeight(), img.getTransparency()); 30 | Graphics g = compatibleImage.getGraphics(); 31 | g.drawImage(img, 0, 0, null); 32 | g.dispose(); 33 | img.flush(); 34 | return compatibleImage; 35 | } 36 | 37 | /** 38 | * TargetWidthやTargetHeightが-1の時には、もう片方の値を基準に元の画像と同じ比率を保って拡大縮小 39 | */ 40 | public static BufferedImage getFasterScaledInstance(BufferedImage img, int targetWidth, int targetHeight, boolean progressiveBilinear) { 41 | int type = (img.getTransparency() == Transparency.OPAQUE) ? BufferedImage.TYPE_INT_RGB : BufferedImage.TYPE_INT_ARGB; 42 | BufferedImage ret = img; 43 | BufferedImage scratchImage = null; 44 | Graphics2D g2 = null; 45 | 46 | if (targetWidth == -1) { 47 | targetWidth = (int) (1.0d * targetHeight / img.getHeight() * img.getWidth()); 48 | } 49 | 50 | if (targetHeight == -1) { 51 | targetHeight = (int) (1.0d * targetWidth / img.getWidth() * img.getHeight()); 52 | } 53 | 54 | int w, h; 55 | int prevW = ret.getWidth(); 56 | int prevH = ret.getHeight(); 57 | 58 | if (progressiveBilinear) { 59 | w = img.getWidth(); 60 | h = img.getHeight(); 61 | } else { 62 | w = targetWidth; 63 | h = targetHeight; 64 | } 65 | 66 | do { 67 | if (progressiveBilinear && w > targetWidth) { 68 | w /= 2; 69 | if (w < targetWidth) { 70 | w = targetWidth; 71 | } 72 | } 73 | if (progressiveBilinear && h > targetHeight) { 74 | h /= 2; 75 | if (h < targetHeight) { 76 | h = targetHeight; 77 | } 78 | } 79 | if (scratchImage == null) { 80 | scratchImage = new BufferedImage(w, h, type); 81 | g2 = scratchImage.createGraphics(); 82 | } 83 | // System.out.println(g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING)); 84 | // System.out.println(g2.getRenderingHint(RenderingHints.KEY_INTERPOLATION)); 85 | // System.out.println(g2.getRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION)); 86 | // System.out.println(g2.getRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING)); 87 | g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); 88 | g2.drawImage(ret, 0, 0, w, h, 0, 0, prevW, prevH, null); 89 | prevW = w; 90 | prevH = h; 91 | ret = scratchImage; 92 | // System.out.println(w + " " + h); 93 | } while (w != targetWidth || h != targetHeight); 94 | 95 | if (g2 != null) { 96 | g2.dispose(); 97 | } 98 | 99 | if (targetWidth != ret.getWidth() || targetHeight != ret.getHeight()) { 100 | scratchImage = new BufferedImage(targetWidth, targetHeight, type); 101 | g2 = scratchImage.createGraphics(); 102 | g2.drawImage(ret, 0, 0, null); 103 | ret = scratchImage; 104 | } 105 | return ret; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/PascalVoxToAnnotationObject.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import jp.go.ndl.lab.annotation.domain.AnnotationObject; 4 | import jp.go.ndl.lab.annotation.domain.Bndbox; 5 | import lombok.extern.slf4j.Slf4j; 6 | import org.apache.commons.lang3.math.NumberUtils; 7 | import org.jsoup.Jsoup; 8 | import org.jsoup.nodes.Document; 9 | import org.jsoup.nodes.Element; 10 | 11 | import java.io.File; 12 | import java.io.IOException; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.UUID; 16 | 17 | @Slf4j 18 | public class PascalVoxToAnnotationObject { 19 | 20 | public static List convert(File xml){ 21 | //Annotationの追加 22 | List annotations = new ArrayList<>(); 23 | try { 24 | Document d = Jsoup.parse(xml, "UTF-8"); 25 | for (Element o : d.getElementsByTag("object")) { 26 | AnnotationObject ao = new AnnotationObject(); 27 | ao.id = UUID.randomUUID().toString(); 28 | ao.name = o.getElementsByTag("name").first().text(); 29 | Element bnd = o.getElementsByTag("bndbox").first(); 30 | ao.bndbox = new Bndbox(NumberUtils.toInt(bnd.getElementsByTag("xmin").first().text()), NumberUtils.toInt(bnd.getElementsByTag("ymin").first().text()), NumberUtils.toInt(bnd.getElementsByTag("xmax").first().text()), NumberUtils.toInt(bnd.getElementsByTag("ymax").first().text())); 31 | annotations.add(ao); 32 | } 33 | } catch (IOException ex) { 34 | log.error("XML parse error", ex); 35 | } 36 | return annotations; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/RESTUtils.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import java.io.UnsupportedEncodingException; 4 | import java.lang.reflect.Field; 5 | import java.net.URLEncoder; 6 | import java.nio.charset.StandardCharsets; 7 | import java.nio.file.Path; 8 | import java.util.Collection; 9 | import java.util.Objects; 10 | import lombok.extern.slf4j.Slf4j; 11 | 12 | 13 | import org.apache.commons.lang3.StringUtils; 14 | import org.springframework.core.io.FileSystemResource; 15 | import org.springframework.core.io.Resource; 16 | import org.springframework.http.HttpEntity; 17 | import org.springframework.http.HttpHeaders; 18 | import org.springframework.http.HttpStatus; 19 | import org.springframework.http.MediaType; 20 | import org.springframework.http.ResponseEntity; 21 | import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; 22 | 23 | @Slf4j 24 | public class RESTUtils { 25 | 26 | 27 | /** 28 | * nullまたは空文字が含まれているオブジェクトが存在するか判定する。 29 | * 30 | * @param inputs オブジェクト 31 | * @return 判定結果 32 | */ 33 | public static boolean blankCheck(Object... inputs) { 34 | for (Object input : inputs) { 35 | if (input == null || StringUtils.isBlank(input.toString())) 36 | return true; 37 | } 38 | return false; 39 | } 40 | 41 | /** 42 | * nullまたは空文字以外が含まれているオブジェクトが存在するかを判定する。 43 | * @param inputs オブジェクト 44 | * @return 判定結果 45 | */ 46 | public static boolean isAllBlank(Object... inputs) { 47 | for (Object input : inputs) { 48 | if (input != null && !StringUtils.isBlank(input.toString())) 49 | return false; 50 | } 51 | return true; 52 | } 53 | 54 | /** 55 | * コレクションがnullまたは空文字でないかを判定する。 56 | * 57 | * @param c コレクション 58 | * @return 判定結果 59 | */ 60 | public static boolean isEmpty(Collection c) { 61 | return c == null || c.isEmpty(); 62 | } 63 | 64 | /** 65 | * 指定されたファイルからHTTP接続情報のheader情報を格納する。 66 | * 67 | * @param fileName ファイル名 68 | * @param path パス 69 | * @return HTTP接続情報 70 | */ 71 | public static HttpEntity fileDownloadResponse(String fileName, Path path) { 72 | HttpHeaders header = new HttpHeaders(); 73 | header.add("Content-Transfer-Encoding", "binary"); 74 | try { 75 | // ファイル名をエンコード(半角スペースはエンコードしない) 76 | String[] split = fileName.split(""); 77 | for (int i = 0; i < split.length; i++) { 78 | String str = split[i]; 79 | if (!str.matches(" ")) { 80 | split[i] = URLEncoder.encode(str, StandardCharsets.UTF_8.name()); 81 | } 82 | } 83 | String encodeFileName = String.join("", split); 84 | header.add("Content-disposition", "attachment;filename*=UTF-8''" + encodeFileName); 85 | } catch (UnsupportedEncodingException ex) { 86 | } 87 | return new HttpEntity<>(new FileSystemResource(path.toFile()), header); 88 | } 89 | 90 | /** 91 | * 指定されたファイルを基にheader情報を追加し、streamingResponseBody部を返却する。
92 | * 大量のダウンロードを取得する際にRequest毎にレンダリングするクラス 93 | * 94 | * @param fileName ファイル名 95 | * @param streamingResponseBody アウトプットストリーム 96 | * @return ResponseEntity<> レスポンス情報 97 | */ 98 | public static ResponseEntity streamDownloadResponse(String fileName, StreamingResponseBody streamingResponseBody) { 99 | HttpHeaders headers = new HttpHeaders(); 100 | headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); 101 | headers.add("Content-Disposition", "attachment; filename=" + fileName); 102 | headers.add("Content-Transfer-Encoding", "binary"); 103 | return new ResponseEntity<>(streamingResponseBody, headers, HttpStatus.OK); 104 | } 105 | 106 | /** 107 | * メソッド名を取得する。 108 | * 109 | * @return メソッド名 110 | */ 111 | public static String getMethodName() { 112 | return Thread.currentThread().getStackTrace()[2].getMethodName(); 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/RWUtils.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import org.apache.commons.io.IOUtils; 4 | import org.apache.commons.io.LineIterator; 5 | 6 | import java.io.*; 7 | import java.nio.charset.StandardCharsets; 8 | import java.nio.file.Files; 9 | import java.nio.file.Path; 10 | import java.util.Arrays; 11 | import java.util.Iterator; 12 | import java.util.List; 13 | import java.util.Objects; 14 | import java.util.stream.Collectors; 15 | import java.util.zip.GZIPInputStream; 16 | import java.util.zip.GZIPOutputStream; 17 | 18 | public class RWUtils { 19 | 20 | public static InputStream pathToGZIPStream(Path path) throws IOException { 21 | return new GZIPInputStream(Files.newInputStream(path)); 22 | } 23 | 24 | public static int writeStreamToGZipFile(InputStream input, Path output) throws IOException { 25 | GZIPOutputStream out = new GZIPOutputStream(Files.newOutputStream(output)); 26 | int size = IOUtils.copy(input, out); 27 | out.close(); 28 | input.close(); 29 | return size; 30 | } 31 | 32 | public static DataReader gDataReader(Path gzip) throws IOException { 33 | return new DataReader(gzip, true); 34 | } 35 | 36 | public static Reader gReader(Path gzip) throws IOException { 37 | return new Reader(gzip, true); 38 | } 39 | 40 | public static Writer gWriter(Path gzip) throws IOException { 41 | return new Writer(gzip, true); 42 | } 43 | 44 | public static DataReader dataReader(Path txtFile) throws IOException { 45 | return new DataReader(txtFile, false); 46 | } 47 | 48 | public static Reader reader(Path txtFile) throws IOException { 49 | return new Reader(txtFile, false); 50 | } 51 | 52 | public static Writer writer(Path txtFile) throws IOException { 53 | return new Writer(txtFile, false); 54 | } 55 | 56 | public static class Writer implements AutoCloseable { 57 | 58 | private BufferedWriter writer; 59 | 60 | public void close() throws IOException { 61 | writer.close(); 62 | } 63 | 64 | private Writer(Path gzip, boolean isGZipped) throws IOException { 65 | Path paren = gzip.getParent(); 66 | if(paren!=null && !Files.exists(paren))Files.createDirectories(paren); 67 | if (isGZipped) { 68 | writer = new BufferedWriter(new OutputStreamWriter(new GZIPOutputStream(Files.newOutputStream(gzip)), StandardCharsets.UTF_8)); 69 | } else { 70 | writer = new BufferedWriter(new OutputStreamWriter(Files.newOutputStream(gzip), StandardCharsets.UTF_8)); 71 | } 72 | } 73 | 74 | public void writeData(Object[] data) throws IOException { 75 | writeLine(Arrays.stream(data).map(o -> Objects.toString(o).replaceAll("\t", " ")).collect(Collectors.joining("\t"))); 76 | } 77 | 78 | public void writeDataV(Object... data) throws IOException { 79 | writeLine(Arrays.stream(data).map(o -> Objects.toString(o).replaceAll("\t", " ")).collect(Collectors.joining("\t"))); 80 | } 81 | 82 | public void writeLine(String line) throws IOException { 83 | if(line!=null) writer.append(line.replaceAll("\n|\r","")).append("\n"); 84 | } 85 | 86 | public void writeLines(List lines) throws IOException { 87 | for (String line : lines) { 88 | writeLine(line); 89 | } 90 | } 91 | 92 | } 93 | 94 | public static class DataReader implements Iterator, Iterable, AutoCloseable { 95 | 96 | private Reader reader; 97 | 98 | private DataReader(Path gz, boolean isGzipped) throws IOException { 99 | reader = new Reader(gz, isGzipped); 100 | } 101 | 102 | public boolean hasNext() { 103 | return reader.hasNext(); 104 | } 105 | 106 | public String[] nextLine() { 107 | return reader.nextLine().split("\t"); 108 | } 109 | 110 | public void close() throws IOException { 111 | reader.close(); 112 | } 113 | 114 | @Override 115 | public Iterator iterator() { 116 | return this; 117 | } 118 | 119 | @Override 120 | public String[] next() { 121 | return nextLine(); 122 | } 123 | 124 | public int getLineCount() { 125 | return reader.getLineCount(); 126 | } 127 | 128 | public boolean isFirstLine() { 129 | return reader.isFirstLine(); 130 | } 131 | 132 | } 133 | 134 | public static class Reader implements Iterator, Iterable, AutoCloseable { 135 | 136 | private LineIterator it; 137 | private int lineCount = 0; 138 | 139 | private Reader(Path path, boolean gzipped) throws IOException { 140 | if (gzipped) { 141 | it = new LineIterator(new BufferedReader(new InputStreamReader(new GZIPInputStream(Files.newInputStream(path)), StandardCharsets.UTF_8))); 142 | } else { 143 | it = new LineIterator(new BufferedReader(new InputStreamReader(Files.newInputStream(path), StandardCharsets.UTF_8))); 144 | } 145 | 146 | } 147 | 148 | public boolean hasNext() { 149 | return it.hasNext(); 150 | } 151 | 152 | public String nextLine() { 153 | lineCount++; 154 | return it.nextLine(); 155 | } 156 | 157 | public void close() throws IOException { 158 | it.close(); 159 | } 160 | 161 | @Override 162 | public Iterator iterator() { 163 | return this; 164 | } 165 | 166 | @Override 167 | public String next() { 168 | return nextLine(); 169 | } 170 | 171 | public int getLineCount() { 172 | return lineCount; 173 | } 174 | 175 | public boolean isFirstLine() { 176 | return lineCount <= 1; 177 | } 178 | 179 | } 180 | 181 | } 182 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/java/jp/go/ndl/lab/annotation/utils/TempUtils.java: -------------------------------------------------------------------------------- 1 | package jp.go.ndl.lab.annotation.utils; 2 | 3 | import java.io.File; 4 | import java.util.UUID; 5 | import org.apache.commons.io.FileUtils; 6 | 7 | public class TempUtils { 8 | 9 | public static File createTempFile() { 10 | File tempDir = FileUtils.getFile("temp"); 11 | tempDir.mkdirs(); 12 | return FileUtils.getFile(tempDir, UUID.randomUUID().toString()); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/resources/config/application.yml: -------------------------------------------------------------------------------- 1 | server: 2 | max-http-header-size : 80000 3 | compression: 4 | enabled: true 5 | mime-types: application/json 6 | spring: 7 | http: 8 | encoding: 9 | enable: false 10 | servlet: 11 | main: 12 | banner-mode: "off" 13 | management: 14 | add-application-context-header: false -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | System.out 6 | 7 | %d{yyyy/MM/dd HH:mm:ss} [%thread] %-5level %logger{36} %M %msg%n 8 | 9 | 10 | 11 | logs/app.log 12 | 13 | logs/app.%d{yyyy-MM-dd}.log.tar.gz 14 | 999 15 | 16 | 17 | %d{yyyy/MM/dd HH:mm:ss} [%thread] %-5level %logger{36} %M %msg%n 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | System.out 29 | 30 | %d{yyyy/MM/dd HH:mm:ss} %-5level %logger{36} %M %msg%n 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 次デジアノテーター 5 | 6 | 7 | 8 | 12 | 83 | 84 | 85 |
86 |
87 |
88 |
89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /tugidigi-annotation/src/main/resources/templates/voc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 2544473_1.jpg 4 | 5 | 6 | Unknown 7 | 8 | 9 | 1600 10 | 1200 11 | 3 12 | 13 | 0 14 | 15 | 1_overall 16 | Unspecified 17 | 0 18 | 0 19 | 20 | 76 21 | 77 22 | 798 23 | 1006 24 | 25 | 26 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/dst/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 次デジアノテーター 5 | 6 | 7 | 8 | 12 | 83 | 84 | 85 |
86 |
87 |
88 |
89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dms-ld-front", 3 | "version": "0.1.0", 4 | "scripts": { 5 | "start": "webpack-dev-server --inline --hot --config webpack.dev.js", 6 | "build": "webpack-cli --config webpack.prod.js" 7 | }, 8 | "author": "ndl", 9 | "dependencies": { 10 | "axios": "*", 11 | "buefy": "*", 12 | "konva": "^4.0.4", 13 | "throttle-debounce": "*", 14 | "tslib": "*", 15 | "vue": "*", 16 | "vue-class-component": "*", 17 | "vue-context-menu": "^2.0.6", 18 | "vue-form-wizard": "*", 19 | "vue-property-decorator": "*", 20 | "vue-router": "*", 21 | "vue-scrollto": "*", 22 | "vue-smooth-dnd": "*" 23 | }, 24 | "devDependencies": { 25 | "@types/node": "*", 26 | "clean-webpack-plugin": "*", 27 | "css-loader": "*", 28 | "file-loader": "*", 29 | "html-loader": "*", 30 | "node-sass": "^4.13.1", 31 | "sass-loader": "*", 32 | "style-loader": "*", 33 | "ts-loader": "*", 34 | "typescript": "*", 35 | "url-loader": "*", 36 | "vue-loader": "*", 37 | "webpack": "*", 38 | "vue-template-compiler": "*", 39 | "webpack-bundle-analyzer": "*", 40 | "webpack-cli": "*", 41 | "webpack-dev-server": "*", 42 | "webpack-merge": "*" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/components/auto-saver.vue: -------------------------------------------------------------------------------- 1 | > 15 | 16 | 29 | 30 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/components/search/search-detail.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 55 | 56 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/components/search/search-keyword.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 13 | 14 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/components/search/search-pagesize.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 26 | 27 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/components/search/search-pagination.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 60 | 61 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/components/search/search-sort.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 35 | 36 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/config.ts: -------------------------------------------------------------------------------- 1 | declare var DEFINE_BASE_FULL_PATH; 2 | 3 | export var SERVICE_NAME: string = "次デジアノテーター"; 4 | export const BASE_PATH = DEFINE_BASE_FULL_PATH; 5 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/domain/action.ts: -------------------------------------------------------------------------------- 1 | export interface Action{ 2 | type?:Status; 3 | note?:string; 4 | user?:string; 5 | start?:number; 6 | end?:number; 7 | } 8 | export type Status = "ESTIMATED"|"WORKING"|"CREATED"|"ANNOTATED"|"CHECKED"; 9 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/domain/imagebinder.ts: -------------------------------------------------------------------------------- 1 | export interface ImageBinder{ 2 | id?:string; 3 | version?:number; 4 | name?:string; 5 | imageType?:string; 6 | tags?:string[]; 7 | status?:Status; 8 | seeAlso?:string; 9 | images?:number; 10 | holder?:string; 11 | imagePath?:string; 12 | actions?:Action[]; 13 | } 14 | export interface Action{ 15 | type?:Status; 16 | note?:string; 17 | user?:string; 18 | start?:number; 19 | end?:number; 20 | } 21 | export type Status = "ESTIMATED"|"WORKING"|"CREATED"|"ANNOTATED"|"CHECKED"; 22 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/domain/imagetype.ts: -------------------------------------------------------------------------------- 1 | export interface ImageType{ 2 | id?:string; 3 | version?:number; 4 | name?:string; 5 | types?:AnnotationType[]; 6 | } 7 | export interface AnnotationType{ 8 | id?:string; 9 | name?:string; 10 | description?:string; 11 | large?:boolean; 12 | } 13 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/domain/targetimage.ts: -------------------------------------------------------------------------------- 1 | export interface TargetImage{ 2 | version?:number; 3 | id?:string; 4 | binder?:string; 5 | name?:string; 6 | imageType?:string; 7 | tags?:string[]; 8 | size?:ImageSize; 9 | imagePath?:string; 10 | note?:string; 11 | bwThreshold?:number; 12 | status?:Status; 13 | annotations?:AnnotationObject[]; 14 | } 15 | export interface ImageSize{ 16 | width?:number; 17 | height?:number; 18 | depth?:number; 19 | } 20 | export interface Bndbox{ 21 | xmin?:number; 22 | ymin?:number; 23 | xmax?:number; 24 | ymax?:number; 25 | } 26 | export interface AnnotationObject{ 27 | id?:string; 28 | name?:string; 29 | bndbox?:Bndbox; 30 | } 31 | export type Status = "ESTIMATED"|"WORKING"|"CREATED"|"ANNOTATED"|"CHECKED"; 32 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/main.ts: -------------------------------------------------------------------------------- 1 | import Buefy from "buefy"; 2 | import Router from "router.vue"; 3 | import "tslib"; 4 | import Vue from "vue"; 5 | import "./styles/main.scss"; 6 | 7 | require("utils/polyfills"); 8 | 9 | if (process.env.NODE_ENV !== "production") { 10 | Vue.config.devtools = true; 11 | Vue.config.performance = true; 12 | } 13 | 14 | Vue.use(Buefy); 15 | 16 | require("types"); 17 | 18 | new Router().$mount("#app"); 19 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/model/state.ts: -------------------------------------------------------------------------------- 1 | export class AppState { 2 | user: string = ""; 3 | } 4 | 5 | export const state: AppState = new AppState(); 6 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/pages/list.vue: -------------------------------------------------------------------------------- 1 | 56 | 57 | 123 | 124 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/router.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/binder-service.ts: -------------------------------------------------------------------------------- 1 | import * as Axios from "axios"; 2 | import * as Config from "config"; 3 | import { ImageBinder } from "domain/imagebinder"; 4 | import { SearchQuery, SearchResult, toSearchQueryString } from "./search-utils"; 5 | 6 | const BASE_URL = Config.BASE_PATH + "api/binder/"; 7 | 8 | export function getBinder(id: string): Axios.AxiosPromise { 9 | return Axios.default.get(BASE_URL + id); 10 | } 11 | 12 | export function searchBinder( 13 | q: SearchQuery 14 | ): Axios.AxiosPromise> { 15 | return Axios.default.get>( 16 | BASE_URL + "search" + toSearchQueryString(q) 17 | ); 18 | } 19 | 20 | export function saveBinder(v: ImageBinder): Axios.AxiosPromise { 21 | return Axios.default.post(BASE_URL + "update", v); 22 | } 23 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/cache-util.ts: -------------------------------------------------------------------------------- 1 | // import * as Axios from "axios"; 2 | // import { deepFind } from "utils/objects"; 3 | 4 | // //TODO local-storageを使う? 5 | // //TODO cache-sizeの制限? 6 | // let cache = {}; 7 | 8 | // export function putCache(type: string, id: string, value: any) { 9 | // if (cache[type] == null) cache[type] = {}; 10 | // cache[type][id] = value; 11 | // } 12 | 13 | // /** 14 | // * 15 | // * 指定されたtype、idのGETが過去に行われている場合、キャッシュ(メモリ)から応答する。 16 | // * 行われていなかった場合、指定されたURLでGETを行い、値をキャッシュに格納する。 17 | // * 18 | // * @param type キャッシュの種類 19 | // * @param id ID 20 | // * @param getUrl HTTP GETを発行するURL 21 | // * 22 | // */ 23 | // export function cacheGet( 24 | // type: string, 25 | // id: string, 26 | // getUrl: string 27 | // ): Promise { 28 | // if (cache[type] == null) cache[type] = {}; 29 | // return new Promise((resolve, reject) => { 30 | // //キャッシュにヒットした場合 31 | // if (cache[type][id]) { 32 | // // console.info("cache-hit", type, id); 33 | // if (cache[type][id] instanceof Promise) { 34 | // let promise = >cache[type][id]; 35 | // promise 36 | // .then(result => { 37 | // // console.info("chained-success"); 38 | // resolve(result.data); 39 | // return result; 40 | // }) 41 | // .catch(result => { 42 | // // console.info("chained-fail"); 43 | // reject(result); 44 | // return Promise.reject(result); 45 | // }); 46 | // } else { 47 | // // console.info("cache-hit-object"); 48 | // resolve(cache[type][id]); 49 | // } 50 | // } else { 51 | // // console.info("cache-nonhit", type, id); 52 | // let promise = Axios.default 53 | // .get(getUrl) 54 | // .then(result => { 55 | // // console.info("first-success"); 56 | // cache[type][id] = result.data; 57 | // resolve(result.data); 58 | // return result; 59 | // }) 60 | // .catch(result => { 61 | // // console.info("first-fail"); 62 | // cache[type][id] = null; 63 | // reject(result); 64 | // return Promise.reject(result); 65 | // }); 66 | // if (cache[type][id] == null) { 67 | // cache[type][id] = promise; 68 | // } 69 | // } 70 | // }); 71 | // } 72 | 73 | // /** 74 | // * 75 | // * 指定されたtype、idのキャッシュを削除する(データの更新があった時など)。 76 | // * 77 | // * @param type キャッシュの種類 78 | // * @param id ID 79 | // * 80 | // */ 81 | // export function clearCache(type: string, id: string) { 82 | // if (cache[type]) cache[type][id] = null; 83 | // } 84 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/image-service.ts: -------------------------------------------------------------------------------- 1 | import * as Axios from "axios"; 2 | import * as Config from "config"; 3 | import { SearchQuery, SearchResult, toSearchQueryString } from "./search-utils"; 4 | import { TargetImage } from "domain/targetimage"; 5 | 6 | const BASE_URL = Config.BASE_PATH + "api/image/"; 7 | 8 | export function getImage(id: string): Axios.AxiosPromise { 9 | return Axios.default.get(BASE_URL + id); 10 | } 11 | 12 | export function imageUrl(t: TargetImage): string { 13 | return BASE_URL + "file/" + t.id; 14 | } 15 | export function thumbUrl(t: TargetImage): string { 16 | return BASE_URL + "thumb/" + t.id; 17 | } 18 | export function searchImage( 19 | q: SearchQuery 20 | ): Axios.AxiosPromise> { 21 | return Axios.default.get>( 22 | BASE_URL + "search" + toSearchQueryString(q) 23 | ); 24 | } 25 | 26 | export function saveImage(v: TargetImage): Axios.AxiosPromise { 27 | return Axios.default.post(BASE_URL + "update", v); 28 | } 29 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/local-storage-service.ts: -------------------------------------------------------------------------------- 1 | export async function lsSet(key: string, obj: Object) { 2 | if (!key || obj === undefined) return; 3 | // console.info("save local storage", key, obj); 4 | try { 5 | localStorage.setItem(key, JSON.stringify(obj)); 6 | } catch (ex) { 7 | console.error(ex); 8 | } 9 | } 10 | 11 | export function lsGet(key: string, defaultValue?: T): T { 12 | let str: string = localStorage.getItem(key); 13 | try { 14 | if (str) return JSON.parse(str); 15 | } catch (ex) { 16 | console.error(ex); 17 | } 18 | return defaultValue; 19 | } 20 | 21 | export function lsDelete(key: string) { 22 | localStorage.removeItem(key); 23 | } 24 | 25 | export async function ssSet(key: string, obj: Object) { 26 | if (!key || obj === undefined) return; 27 | // console.info("save session storage", key, obj); 28 | try { 29 | sessionStorage.setItem(key, JSON.stringify(obj)); 30 | } catch (ex) { 31 | console.error(ex); 32 | } 33 | } 34 | 35 | export function ssGet(key: string, defaultValue?: T): T { 36 | let str: string = sessionStorage.getItem(key); 37 | try { 38 | if (str) return JSON.parse(str); 39 | } catch (ex) { 40 | console.error(ex); 41 | } 42 | return defaultValue; 43 | } 44 | 45 | export function ssDelete(key: string) { 46 | sessionStorage.removeItem(key); 47 | } 48 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/search-utils.ts: -------------------------------------------------------------------------------- 1 | import * as URL from "utils/url"; 2 | 3 | export interface Range { 4 | from: string; 5 | to: string; 6 | } 7 | 8 | export interface GeoDistance { 9 | lat: number; 10 | lon: number; 11 | distance: string; 12 | } 13 | 14 | export class SearchQuery { 15 | from?: number; 16 | size?: number; 17 | keyword?: string[]; 18 | keywordOR?: boolean; 19 | query?: { [key: string]: string[] }; 20 | range?: { [key: string]: Range[] }; 21 | image?: string[]; 22 | geoDistance?: { [key: string]: GeoDistance[] }; 23 | exists?: string[]; 24 | filter?: { [key: string]: string[] }; 25 | facet?: { [key: string]: string[] }; 26 | sort?: string[]; 27 | } 28 | 29 | function parseBool(values: string[]): boolean { 30 | if (values && values.length > 0) { 31 | return values[0] === "true"; 32 | } 33 | return false; 34 | } 35 | 36 | export function fromQueryString(query: string): SearchQuery { 37 | let q = new SearchQuery(); 38 | let map = URL.getQueryMap(query); 39 | 40 | if (map["from"]) q.from = Number(map["from"]); 41 | if (!Number.isInteger(q.from) || q.from < 0) q.from = 0; 42 | 43 | if (map["size"]) q.size = Number(map["size"]); 44 | if (!Number.isInteger(q.size) || q.size < 0) q.size = 10; 45 | 46 | if (map["sort"]) { 47 | q.sort = map["sort"]; 48 | } 49 | 50 | if (map["keyword"]) { 51 | q.keyword = map["keyword"]; 52 | } 53 | if (map["image"]) { 54 | q.image = map["image"]; 55 | } 56 | 57 | if (map["keywordOr"]) { 58 | q.keywordOR = parseBool(map["keywordOr"]); 59 | } 60 | 61 | q.exists = map["exists"]; 62 | 63 | q.facet = {}; 64 | q.query = {}; 65 | q.filter = {}; 66 | q.range = {}; 67 | Object.keys(map).forEach(key => { 68 | if (key.startsWith("q-")) { 69 | q.query[key.substring(2)] = map[key]; 70 | } 71 | if (key.startsWith("f-")) { 72 | q.filter[key.substring(2)] = map[key]; 73 | } 74 | if (key.startsWith("r-")) { 75 | q.range[key.substring(2)] = map[key].map(v => { 76 | let spl = v.split(/,/, 2); 77 | if (spl.length >= 2) { 78 | return { from: spl[0], to: spl[1] }; 79 | } 80 | }); 81 | } 82 | if (key.startsWith("fc-")) { 83 | q.facet[key.substring(3)] = map[key]; 84 | } 85 | }); 86 | return q; 87 | } 88 | 89 | export function toSearchQueryString(q: SearchQuery) { 90 | return URL.toQueryString(toSeqrchQueryUrlMap(q)); 91 | } 92 | 93 | function filter(input: string[]): string[] { 94 | return input.filter(s => s != null && s !== ""); 95 | } 96 | 97 | export function toSeqrchQueryUrlMap(sq: SearchQuery) { 98 | let q: { [key: string]: any[] } = {}; 99 | if (sq.from != null) q["from"] = [sq.from]; 100 | if (sq.size && sq.size !== 20) q["size"] = [sq.size]; 101 | if (sq.sort) q["sort"] = filter(sq.sort); 102 | if (sq.keyword) q["keyword"] = filter(sq.keyword); 103 | if (sq.image) q["image"] = filter(sq.image); 104 | if (sq.keywordOR) q["keywordOr"] = [true]; 105 | if (sq.exists) q["exists"] = filter(sq.exists); 106 | if (sq.query) { 107 | Object.keys(sq.query).forEach(key => { 108 | let val: string[] = sq.query[key]; 109 | if (val) q["q-" + encodeURIComponent(key)] = filter(val); 110 | }); 111 | } 112 | if (sq.range) { 113 | Object.keys(sq.range).forEach(key => { 114 | let val = sq.range[key]; 115 | if (val) 116 | q["r-" + encodeURIComponent(key)] = val.map( 117 | s => 118 | encodeURIComponent(s.from || "") + 119 | "," + 120 | encodeURIComponent(s.to || "") 121 | ); 122 | }); 123 | } 124 | if (sq.geoDistance) { 125 | Object.keys(sq.geoDistance).forEach(key => { 126 | let val = sq.geoDistance[key]; 127 | if (val) 128 | q["g-" + encodeURIComponent(key)] = val.map( 129 | s => s.lat + "," + s.lon + "," + s.distance 130 | ); 131 | }); 132 | } 133 | if (sq.filter) { 134 | Object.keys(sq.filter).forEach(key => { 135 | let val: string[] = sq.filter[key]; 136 | if (val) q["f-" + encodeURIComponent(key)] = filter(val); 137 | }); 138 | } 139 | 140 | if (sq.facet) { 141 | Object.keys(sq.facet).forEach(key => { 142 | let val: string[] = sq.facet[key]; 143 | if (val) q["fc-" + encodeURIComponent(key)] = filter(val); 144 | }); 145 | } 146 | return q; 147 | } 148 | 149 | export interface SearchResult { 150 | list: T[]; 151 | hit: number; 152 | from: number; 153 | facets: EsFacet[]; 154 | } 155 | 156 | export interface EsFacet { 157 | field: string; 158 | counts: { [key: string]: number }; 159 | } 160 | 161 | export type APICallType = "P" | "M" | "O"; 162 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/service-utils.ts: -------------------------------------------------------------------------------- 1 | import * as Axios from "axios"; 2 | 3 | export function toPromise(ap: Axios.AxiosPromise): Promise { 4 | return new Promise((resolve, reject) => { 5 | ap.then(result => { 6 | resolve(result.data); 7 | return result.data; 8 | }).catch(result => { 9 | reject(result); 10 | Promise.reject(result); 11 | }); 12 | }); 13 | } 14 | 15 | export function getAll( 16 | ids: string[], 17 | getter: (id: string) => Promise 18 | ): Promise { 19 | return Promise.all(ids.map(id => getter(id))); 20 | } 21 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/service/type-service.ts: -------------------------------------------------------------------------------- 1 | import * as Axios from "axios"; 2 | import * as Config from "config"; 3 | import { ImageType } from "domain/imagetype"; 4 | 5 | const BASE_URL = Config.BASE_PATH + "api/type/"; 6 | 7 | export function getType(id: string): Axios.AxiosPromise { 8 | return Axios.default.get(BASE_URL + id); 9 | } 10 | 11 | export function listType(): Axios.AxiosPromise { 12 | return Axios.default.get(BASE_URL + "all"); 13 | } 14 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/sfc.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.vue" { 2 | import Vue from "vue"; 3 | export default Vue; 4 | } 5 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/styles/_palette.scss: -------------------------------------------------------------------------------- 1 | //////////////////////////////////////////////// 2 | // LITERA 3 | //////////////////////////////////////////////// 4 | $grey-darker: #363636; 5 | $grey-dark: #373a3c; 6 | $grey: #55595c; 7 | $grey-light: #818a91; 8 | $grey-lighter: #d9dee2; 9 | 10 | $orange: #f0ad4e; 11 | $yellow: #ffd500; 12 | $green: #02b875; 13 | $blue: #343d46; 14 | $red: #d9534f; 15 | 16 | $link: #4582ec !default; 17 | $primary: $blue !default; 18 | $danger: $orange; 19 | 20 | $subtitle-color: $grey; 21 | 22 | $family-heading: "Raleway", "Lucida Grande", "Lucida Sans Unicode", 23 | "Lucida Sans", Geneva, Arial, sans-serif; 24 | 25 | $body-size: 15px; 26 | 27 | $family-serif: Georgia, Cambria, "Times New Roman", Times, serif; 28 | 29 | $navbar-height: 3.25rem; 30 | $navbar-item-hover-color: $link; 31 | $navbar-item-hover-background-color: transparent; 32 | $navbar-item-active-color: $link; 33 | $navbar-item-active-background-color: transparent; 34 | 35 | $navbar-dropdown-item-hover-color: $link; 36 | $navbar-dropdown-item-hover-background-color: transparent; 37 | $navbar-dropdown-item-active-color: $link; 38 | $navbar-dropdown-item-active-background-color: transparent; 39 | 40 | $bulmaswatch-import-font: true !default; 41 | 42 | $box-shadow: 0 0 1px $grey-light; 43 | 44 | $text: black; 45 | $black: black; 46 | $background-gray: #f5f5f5; 47 | $border-gray: #dbdbdb; 48 | 49 | $basic-border: 1px solid $border-gray; 50 | 51 | @mixin icon($name, $size: 24px, $x: 0px, $y: 0px) { 52 | background: { 53 | /*image:url("../images/ico-#{$name}.png");*/ 54 | repeat: no-repeat; 55 | size: $size $size; 56 | position: $x $y; 57 | } 58 | } 59 | 60 | @mixin shadow() { 61 | box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); 62 | } 63 | 64 | @mixin padding-side($width) { 65 | padding-left: $width; 66 | padding-right: $width; 67 | } 68 | 69 | @mixin md-transition($dur: 0.4s) { 70 | -webkit-transition: all $dur cubic-bezier(0.165, 0.84, 0.44, 1); 71 | -moz-transition: all $dur cubic-bezier(0.165, 0.84, 0.44, 1); 72 | transition: all $dur cubic-bezier(0.165, 0.84, 0.44, 1); 73 | } 74 | 75 | @mixin transform($args) { 76 | -webkit-transform: $args; 77 | transform: $args; 78 | } 79 | 80 | @mixin badge() { 81 | padding: 0px 3px 0px 3px; 82 | border: $lineGray solid 1px; 83 | background: $headGray; 84 | border-radius: 8px; 85 | } 86 | 87 | @mixin dialog-background-black { 88 | position: fixed; 89 | width: 100%; 90 | height: 100%; 91 | top: 0; 92 | right: 0; 93 | bottom: 0; 94 | left: 0; 95 | background: rgba(0, 0, 0, 0.1); 96 | z-index: 10001; 97 | } 98 | 99 | @mixin card { 100 | background: white; 101 | @include shadow2dp; 102 | } 103 | 104 | @mixin is-mobile() { 105 | @media screen and (max-width: 768px) { 106 | @content; 107 | } 108 | } 109 | 110 | @mixin default-margin() { 111 | margin: 40px; 112 | @include is-mobile() { 113 | margin: 5px; 114 | } 115 | } 116 | 117 | @mixin leftPane { 118 | padding: 10px; 119 | border-left: $borderGray solid 1px; 120 | width: 208px; 121 | display: table-cell; 122 | height: 100%; 123 | background-color: $headGray; 124 | button { 125 | width: 100%; 126 | } 127 | box-sizing: border-box; 128 | 129 | @media #{$small} { 130 | display: block; 131 | width: 100%; 132 | border: none; 133 | } 134 | } 135 | 136 | /* utilities */ 137 | @mixin no-user-select() { 138 | -webkit-user-select: none; 139 | -webkit-touch-callout: none; 140 | -moz-user-select: none; 141 | -ms-user-select: none; 142 | user-select: none; 143 | } 144 | 145 | @mixin padding-side($width) { 146 | padding-left: $width; 147 | padding-right: $width; 148 | } 149 | 150 | /* animagtion */ 151 | 152 | @mixin ease($duration: 300ms) { 153 | -webkit-transition: all $duration 0s ease; 154 | transition: all $duration 0s ease; 155 | } 156 | 157 | /* shadow */ 158 | 159 | @mixin box-shadow($color: rgba(0, 0, 0, 0.1)) { 160 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12); 161 | } 162 | 163 | @mixin popup-shadow($color: rgba(0, 0, 0, 0.25)) { 164 | box-shadow: 0 1px 24px $color; 165 | } 166 | 167 | /* gradation */ 168 | @mixin key-gradation-red { 169 | background-image: linear-gradient(90deg, #d30d7d 30%, #de1049 70%); 170 | } 171 | @mixin key-gradation-blue { 172 | background-image: linear-gradient(90deg, #0aba90 30%, #109cde 70%); 173 | } 174 | 175 | /* breakpoint */ 176 | 177 | $mobile-width: 768px; 178 | $tablet-width: 769px; 179 | $desktop-width: 1024px; 180 | $widescreen-width: 1216px; 181 | 182 | @mixin is-mobile() { 183 | @media screen and (max-width: $mobile-width) { 184 | @content; 185 | } 186 | } 187 | 188 | @mixin is-tablet() { 189 | @media screen and (min-width: $tablet-width) { 190 | @content; 191 | } 192 | } 193 | 194 | @mixin is-desktop() { 195 | @media screen and (min-width: $desktop-width) { 196 | @content; 197 | } 198 | } 199 | 200 | @mixin is-widescreen() { 201 | @media screen and (min-width: $widescreen-width) { 202 | @content; 203 | } 204 | } 205 | 206 | @mixin is-IE() { 207 | @media screen and (-ms-high-contrast: active), 208 | screen and (-ms-high-contrast: none) { 209 | @content; 210 | } 211 | } 212 | 213 | /* typography */ 214 | @import url("https://fonts.googleapis.com/css?family=Roboto"); 215 | @import url("https://cdn.jsdelivr.net/npm/yakuhanjp@3.0.0/dist/css/yakuhanjp.min.css"); 216 | 217 | @mixin typo-header($weight: bold, $color: $black) { 218 | font-family: YakuHanJP, "Roboto", sans-serif; 219 | font-weight: $weight; 220 | color: $color; 221 | text-indent: 0; 222 | } 223 | @mixin typo-list($weight: normal, $color: $black) { 224 | font-family: YakuHanJP, "Roboto", sans-serif; 225 | font-weight: $weight; 226 | color: $color; 227 | text-indent: 0; 228 | } 229 | @mixin typo-paragraph($weight: normal, $color: $black) { 230 | font-family: YakuHanJP, "Roboto", sans-serif; 231 | font-weight: $weight; 232 | line-height: 175%; 233 | color: $color; 234 | text-indent: 0; 235 | // margin-bottom: 1rem; 236 | } 237 | 238 | @mixin typo-figcaption($weight: normal, $color: $grey-darker) { 239 | font-family: YakuHanJP, "Roboto", sans-serif; 240 | font-weight: $weight; 241 | line-height: 150%; 242 | color: $color; 243 | text-indent: 0; 244 | margin-bottom: 0.5rem; 245 | } 246 | 247 | @mixin link-marker($color: rgba(21, 209, 151, 0.6), $weight: 85%) { 248 | background: linear-gradient(transparent $weight, $color 20%); 249 | } 250 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/styles/main.scss: -------------------------------------------------------------------------------- 1 | @import "_palette.scss"; 2 | @import "~bulma/sass/utilities/_all"; 3 | 4 | // Setup $colors to use as bulma classes 5 | $colors: ( 6 | "white": ( 7 | $white, 8 | $black 9 | ), 10 | "black": ( 11 | $black, 12 | $white 13 | ), 14 | "light": ( 15 | $light, 16 | $light-invert 17 | ), 18 | "dark": ( 19 | $dark, 20 | $dark-invert 21 | ), 22 | "primary": ( 23 | $primary, 24 | $primary-invert 25 | ), 26 | "info": ( 27 | $info, 28 | $info-invert 29 | ), 30 | "success": ( 31 | $success, 32 | $success-invert 33 | ), 34 | "warning": ( 35 | $warning, 36 | $warning-invert 37 | ), 38 | "danger": ( 39 | $danger, 40 | $danger-invert 41 | ) 42 | ); 43 | 44 | @import "~bulma"; 45 | @import "~buefy/src/scss/buefy"; 46 | 47 | body { 48 | background: $background-gray; 49 | } 50 | 51 | .is-clipped { 52 | overflow-x: hidden !important; 53 | overflow-y: hidden !important; 54 | } 55 | 56 | .is-width-2 { 57 | width: 2rem; 58 | } 59 | .is-width-3 { 60 | width: 3rem; 61 | } 62 | .is-width-4 { 63 | width: 4rem; 64 | } 65 | .is-width-5 { 66 | width: 5rem; 67 | } 68 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/utils/element-util.scss: -------------------------------------------------------------------------------- 1 | @import "_palette.scss"; 2 | 3 | .accordion-mixin-icon { 4 | cursor: pointer; 5 | /*width: 4em;*/ 6 | // margin-left: auto; 7 | padding-left: 0.5em; 8 | padding-right: 0.5em; 9 | transition: $transition; 10 | display: flex; 11 | justify-content: center; 12 | align-items: center; 13 | &.is-open { 14 | /*background: red;*/ 15 | transform: rotate(-180deg); 16 | } 17 | .control-icon { 18 | /*width: 100%;*/ 19 | line-height: 100%; 20 | } 21 | } 22 | 23 | .accordion-mixin-wrapper { 24 | overflow: hidden; 25 | transition: $transition; 26 | } 27 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/utils/element-util.ts: -------------------------------------------------------------------------------- 1 | import Component from "vue-class-component"; 2 | import Vue from "vue"; 3 | import { Watch } from "vue-property-decorator"; 4 | 5 | let originalStyle: { [id: string]: string } = {}; 6 | 7 | export function show(id: string) { 8 | let e: HTMLElement = document.getElementById(id); 9 | if (e) { 10 | if (originalStyle[id]) { 11 | originalStyle[id] = undefined; 12 | e.style.display = originalStyle[id]; 13 | } else e.style.display = "default"; 14 | } 15 | } 16 | 17 | export function hide(id: string) { 18 | let e: HTMLElement = document.getElementById(id); 19 | if (e) { 20 | originalStyle[id] = e.style.display; 21 | e.style.display = "none"; 22 | } 23 | } 24 | 25 | import "./element-util.scss"; 26 | 27 | @Component({}) 28 | export class AccordionMixin extends Vue { 29 | show: boolean = true; 30 | 31 | mounted() { 32 | if (!this.show) { 33 | const wrapper = this.$refs["wrapper"]; 34 | wrapper.style.height = "0"; 35 | wrapper.style.opacity = "0"; 36 | } 37 | } 38 | 39 | @Watch("show") 40 | toggle(n, o) { 41 | const body = this.$refs["body"]; 42 | const wrapper = this.$refs["wrapper"]; 43 | if (this.show) { 44 | //こちらはDelayは不要だが、閉じるときとタイミングを合わせるため一応Delay付与 45 | // body.style.display = "block"; 46 | window.setTimeout(() => { 47 | wrapper.style.height = `${body.offsetHeight}px`; 48 | wrapper.style.opacity = "1"; 49 | //アニメーション終了後に、heightを外さないと、Body内部で要素が追加されたときに切れる 50 | //transition完了後にheightを消去 51 | window.setTimeout(() => { 52 | wrapper.style.height = ""; 53 | }, 100); 54 | }, 50); 55 | } else { 56 | wrapper.style.height = `${body.offsetHeight}px`; 57 | wrapper.style.opacity = "1"; 58 | // body.style.display = "none"; 59 | //delayを入れないと、高さ設定が効かないためアニメーションしない 60 | window.setTimeout(() => { 61 | wrapper.style.height = "0"; 62 | wrapper.style.opacity = "0"; 63 | }, 50); 64 | } 65 | } 66 | } 67 | 68 | export interface IAccordionMixin { 69 | show: boolean; 70 | } 71 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/utils/misc.ts: -------------------------------------------------------------------------------- 1 | let S4 = function() { 2 | return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 3 | }; 4 | 5 | export function guid(): string { 6 | return S4() + S4(); 7 | } 8 | 9 | export function split(str: string): string[] { 10 | const result = []; 11 | let semiFlag = false; 12 | let substr = ""; 13 | for (var i = 0; i < str.length; i++) { 14 | if (str[i] === '"') { 15 | if (semiFlag) { 16 | result.push('"' + substr + '"'); 17 | substr = ""; 18 | semiFlag = false; 19 | } else { 20 | semiFlag = true; 21 | } 22 | } else if (!semiFlag && /[\s ]/.exec(str.substr(i, 1))) { 23 | if (substr.length > 0) { 24 | result.push(substr); 25 | substr = ""; 26 | } 27 | } else { 28 | substr += str[i]; 29 | } 30 | } 31 | if (substr.length > 0) { 32 | result.push(substr); 33 | } 34 | return result; 35 | } 36 | 37 | export function validateEmail(mail: string) { 38 | return /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(mail.trim()); 39 | } 40 | 41 | export function containUrl(content: string): boolean { 42 | if (!content) return false; 43 | return /.+h*ttps*:\/\//.test(content); 44 | } 45 | 46 | export function abbText(str: string, len: number) { 47 | if (!str) return ""; 48 | if (str.length < len) return str; 49 | return str.substring(0, len) + "..."; 50 | } 51 | 52 | export function hashCode(str: string): number { 53 | let hash = 0; 54 | for (var i = 0; i < str.length; i++) { 55 | hash = hash * 31 + str.charCodeAt(i); 56 | hash = hash | 0; //符号付き32bit整数にする。 57 | } 58 | return hash; 59 | } 60 | 61 | export type DeviceType = "mobile" | "tablet" | "desktop"; 62 | 63 | export function getDevice(): DeviceType { 64 | let ua = navigator.userAgent; 65 | if ( 66 | ua.indexOf("iPhone") > 0 || 67 | ua.indexOf("iPod") > 0 || 68 | (ua.indexOf("Android") > 0 && ua.indexOf("Mobile") > 0) 69 | ) { 70 | return "mobile"; 71 | } else if (ua.indexOf("iPad") > 0 || ua.indexOf("Android") > 0) { 72 | return "tablet"; 73 | } else { 74 | return "desktop"; 75 | } 76 | } 77 | 78 | export function findMatch(s: string, regxp: RegExp, group = 0): string { 79 | return regxp.exec(s)[group]; 80 | } 81 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/utils/objects.ts: -------------------------------------------------------------------------------- 1 | export function equals(o1: any, o2: any) { 2 | return JSON.stringify(o1) === JSON.stringify(o2); 3 | } 4 | 5 | // export function equalsIgnoreNull(o1: any, o2: any) { 6 | // const e1 = normalizeEmpty(o1), e2 = normalizeEmpty(o2); 7 | // return equals(e1, e2); 8 | // } 9 | 10 | export function equalsIgnoreNull(o1: any, o2: any): boolean { 11 | if (o1 == null || o2 == null) return false; 12 | if (o1 instanceof Date && o2 instanceof Date) { 13 | return o1.toDateString() === o2.toDateString(); 14 | } else if (isEmpty(o1) && isEmpty(o2)) return true; 15 | else if (typeof o1 === "object" && typeof o2 === "object") { 16 | let o1keys = Object.keys(o1), 17 | o2keys = Object.keys(o2); 18 | // TODO join &unique 19 | return (o1keys.length > o2keys.length ? o1keys : o2keys).every(k => 20 | equalsIgnoreNull(o1[k], o2[k]) 21 | ); 22 | } else if (Array.isArray(o1) && Array.isArray(o2)) { 23 | let len = Math.max(o1.length, o2.length); 24 | for (let i = 0; i < len; i++) { 25 | if (!equalsIgnoreNull(o1[i], o2[i])) return false; 26 | } 27 | return true; 28 | } else { 29 | return o1 === o2; 30 | } 31 | } 32 | 33 | export function isEmpty(v: any): boolean { 34 | if (v == null) return true; 35 | if (v === "") return true; 36 | if (v instanceof Date) return false; 37 | if (typeof v === "object") { 38 | let keys = Object.keys(v); 39 | if (keys.length == 0) return true; 40 | if (keys.map(k => v[k]).every(ov => isEmpty(ov))) return true; 41 | } else if (Array.isArray(v)) { 42 | if (v.length === 0) return true; 43 | if (v.every(av => isEmpty(av))) return true; 44 | } 45 | return false; 46 | } 47 | 48 | export function isEmptyString(v: string): boolean { 49 | return !v || /^[\s\0]+$/.test(v); 50 | } 51 | 52 | export function normalizeEmpty(v: any) { 53 | if (isEmpty(v)) return null; 54 | if (typeof v === "object") { 55 | let o = {}; 56 | Object.keys(v).forEach(k => { 57 | o[k] = normalizeEmpty(v[k]); 58 | }); 59 | return o; 60 | } else if (Array.isArray(v)) { 61 | let a = []; 62 | v.forEach(av => a.push(normalizeEmpty(av))); 63 | return a; 64 | } 65 | return v; 66 | } 67 | 68 | export function deepFind(obj: any, path: string, def?: any): any { 69 | for (let p of path.split(".")) { 70 | if (obj[p]) { 71 | obj = obj[p]; 72 | } else { 73 | return def; 74 | } 75 | } 76 | return obj; 77 | } 78 | export function deepFind2(obj: any, path: string, def: any = []): any[] { 79 | let values = []; 80 | _deepFind2(path.split("."), obj, values); 81 | if (values.length == 0) return def; 82 | return values; 83 | } 84 | 85 | export function deepFind3(obj: any, paths: string[]): any[] { 86 | let values = []; 87 | paths.forEach(path => { 88 | _deepFind2(path.split("."), obj, values); 89 | }); 90 | return values; 91 | } 92 | 93 | function _deepFind2(paths: string[], obj: any, values: any[]) { 94 | if (obj == null) return; 95 | let sub = obj[paths[0]]; 96 | if (sub) { 97 | if (paths.length == 1) { 98 | if (Array.isArray(sub)) { 99 | (sub).forEach(v => { 100 | values.push(v); 101 | }); 102 | } else { 103 | values.push(sub); 104 | } 105 | } else { 106 | if (Array.isArray(sub)) { 107 | (sub).forEach(node => 108 | _deepFind2(paths.slice(1, paths.length), node, values) 109 | ); 110 | } else { 111 | _deepFind2(paths.slice(1, paths.length), sub, values); 112 | } 113 | } 114 | } 115 | } 116 | 117 | export function clone(tgt: T): T { 118 | let cp: any; 119 | if (tgt === null) { 120 | cp = null; 121 | } else if (tgt instanceof Date) { 122 | cp = new Date(tgt.getTime()); 123 | } else if (Array.isArray(tgt)) { 124 | cp = []; 125 | tgt.forEach((v, i, arr) => { 126 | cp.push(v); 127 | }); 128 | cp = cp.map((n: any) => clone(n)); 129 | } else if (typeof tgt === "object" && tgt !== {}) { 130 | cp = { ...(tgt as Object) }; 131 | Object.keys(cp).forEach(k => { 132 | cp[k] = clone(cp[k]); 133 | }); 134 | } else { 135 | cp = tgt; 136 | } 137 | return cp; 138 | } 139 | 140 | export function merge(from: any, to: any) { 141 | if (typeof from === "object" && typeof to === "object") { 142 | Object.keys(from).forEach(k => { 143 | let fromval = from[k]; 144 | // console.info("merge", k, fromval); 145 | if (fromval === null) { 146 | //do nothing 147 | } else if (fromval instanceof Date) { 148 | to[k] = clone(fromval); 149 | } else if (Array.isArray(fromval)) { 150 | to[k] = clone(fromval); 151 | } else if (typeof fromval === "object") { 152 | let toval = to[k]; 153 | if (toval != null && typeof toval === "object") { 154 | //toもfromもobjectの場合、マージ 155 | toval = clone(toval); 156 | merge(fromval, toval); 157 | to[k] = toval; 158 | } else { 159 | //toがobjectでない場合はcloneで上書き 160 | to[k] = clone(fromval); 161 | } 162 | } else { 163 | to[k] = fromval; 164 | } 165 | }); 166 | } 167 | } 168 | 169 | export function deletePropertiesExcept( 170 | object: Object, 171 | ...exceptFor: string[] 172 | ): void { 173 | Object.keys(object).forEach(k => { 174 | if (!exceptFor.includes(k)) { 175 | delete object[k]; 176 | } 177 | }); 178 | } 179 | 180 | export function removeFromArray(array: any[], toRemove: any) { 181 | let index = array.indexOf(toRemove); 182 | if (index >= 0) array.splice(index, 1); 183 | } 184 | 185 | export function addOrMoveToTop(array: any[], toAdd: any) { 186 | removeFromArray(array, toAdd); 187 | array.unshift(toAdd); 188 | } 189 | 190 | export function flatArray(array: any) { 191 | return array.reduce((acc, val) => acc.concat(val), []); 192 | } 193 | 194 | export function uniqueArray(array: any) { 195 | return array.filter((v, i) => array.indexOf(v) === i); 196 | } 197 | 198 | export function findOrDefault(array: any[], cond: (v) => boolean, def: any) { 199 | let val = array.find(cond); 200 | if (val) return val; 201 | else return def; 202 | } 203 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/utils/polyfills.ts: -------------------------------------------------------------------------------- 1 | if (!String.prototype.includes) { 2 | String.prototype.includes = function(search, start) { 3 | if (typeof start !== "number") { 4 | start = 0; 5 | } 6 | 7 | if (start + search.length > this.length) { 8 | return false; 9 | } else { 10 | return this.indexOf(search, start) !== -1; 11 | } 12 | }; 13 | } 14 | 15 | if (!String.prototype.endsWith) { 16 | String.prototype.endsWith = function(search, this_len) { 17 | if (this_len === undefined || this_len > this.length) { 18 | this_len = this.length; 19 | } 20 | return this.substring(this_len - search.length, this_len) === search; 21 | }; 22 | } 23 | 24 | if (!String.prototype.startsWith) { 25 | String.prototype.startsWith = function(search, pos) { 26 | return this.substr(!pos || pos < 0 ? 0 : +pos, search.length) === search; 27 | }; 28 | } 29 | 30 | if (typeof Object.assign != "function") { 31 | // Must be writable: true, enumerable: false, configurable: true 32 | Object.defineProperty(Object, "assign", { 33 | value: function assign(target, varArgs) { 34 | // .length of function is 2 35 | "use strict"; 36 | if (target == null) { 37 | // TypeError if undefined or null 38 | throw new TypeError("Cannot convert undefined or null to object"); 39 | } 40 | 41 | var to = Object(target); 42 | 43 | for (var index = 1; index < arguments.length; index++) { 44 | var nextSource = arguments[index]; 45 | 46 | if (nextSource != null) { 47 | // Skip over if undefined or null 48 | for (var nextKey in nextSource) { 49 | // Avoid bugs when hasOwnProperty is shadowed 50 | if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { 51 | to[nextKey] = nextSource[nextKey]; 52 | } 53 | } 54 | } 55 | } 56 | return to; 57 | }, 58 | writable: true, 59 | configurable: true 60 | }); 61 | } 62 | 63 | //developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Number/isInteger 64 | https: if (!Number.isInteger) { 65 | Number.isInteger = 66 | Number.isInteger || 67 | function(value) { 68 | return ( 69 | typeof value === "number" && 70 | isFinite(value) && 71 | Math.floor(value) === value 72 | ); 73 | }; 74 | } 75 | 76 | //https://tc39.github.io/ecma262/#sec-array.prototype.includes 77 | if (!Array.prototype.includes) { 78 | Object.defineProperty(Array.prototype, "includes", { 79 | value: function(searchElement, fromIndex) { 80 | if (this == null) { 81 | throw new TypeError('"this" is null or not defined'); 82 | } 83 | 84 | // 1. Let O be ? ToObject(this value). 85 | var o = Object(this); 86 | 87 | // 2. Let len be ? ToLength(? Get(O, "length")). 88 | var len = o.length >>> 0; 89 | 90 | // 3. If len is 0, return false. 91 | if (len === 0) { 92 | return false; 93 | } 94 | 95 | // 4. Let n be ? ToInteger(fromIndex). 96 | // (If fromIndex is undefined, this step produces the value 0.) 97 | var n = fromIndex | 0; 98 | 99 | // 5. If n ≥ 0, then 100 | // a. Let k be n. 101 | // 6. Else n < 0, 102 | // a. Let k be len + n. 103 | // b. If k < 0, let k be 0. 104 | var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0); 105 | 106 | function sameValueZero(x, y) { 107 | return ( 108 | x === y || 109 | (typeof x === "number" && 110 | typeof y === "number" && 111 | isNaN(x) && 112 | isNaN(y)) 113 | ); 114 | } 115 | 116 | // 7. Repeat, while k < len 117 | while (k < len) { 118 | // a. Let elementK be the result of ? Get(O, ! ToString(k)). 119 | // b. If SameValueZero(searchElement, elementK) is true, return true. 120 | if (sameValueZero(o[k], searchElement)) { 121 | return true; 122 | } 123 | // c. Increase k by 1. 124 | k++; 125 | } 126 | 127 | // 8. Return false 128 | return false; 129 | } 130 | }); 131 | } 132 | 133 | // https://tc39.github.io/ecma262/#sec-array.prototype.find 134 | if (!Array.prototype.find) { 135 | Object.defineProperty(Array.prototype, "find", { 136 | value: function(predicate) { 137 | // 1. Let O be ? ToObject(this value). 138 | if (this == null) { 139 | throw new TypeError('"this" is null or not defined'); 140 | } 141 | 142 | var o = Object(this); 143 | 144 | // 2. Let len be ? ToLength(? Get(O, "length")). 145 | var len = o.length >>> 0; 146 | 147 | // 3. If IsCallable(predicate) is false, throw a TypeError exception. 148 | if (typeof predicate !== "function") { 149 | throw new TypeError("predicate must be a function"); 150 | } 151 | 152 | // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. 153 | var thisArg = arguments[1]; 154 | 155 | // 5. Let k be 0. 156 | var k = 0; 157 | 158 | // 6. Repeat, while k < len 159 | while (k < len) { 160 | // a. Let Pk be ! ToString(k). 161 | // b. Let kValue be ? Get(O, Pk). 162 | // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). 163 | // d. If testResult is true, return kValue. 164 | var kValue = o[k]; 165 | if (predicate.call(thisArg, kValue, k, o)) { 166 | return kValue; 167 | } 168 | // e. Increase k by 1. 169 | k++; 170 | } 171 | 172 | // 7. Return undefined. 173 | return undefined; 174 | }, 175 | configurable: true, 176 | writable: true 177 | }); 178 | } 179 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/src/utils/url.ts: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Component from "vue-class-component"; 3 | import { removeFromArray } from "./objects"; 4 | 5 | export function isValidUrl(url: string): boolean { 6 | return url.match(/^https*:.+/) != null; 7 | } 8 | 9 | export function getQueryMap(url?: string): { [key: string]: string[] } { 10 | var vars: { [key: string]: string[] } = {}; 11 | if (!url) url = window.location.search; 12 | let query: string[] = url.slice(1).split("&"); 13 | for (var i = 0; i < query.length; i++) { 14 | let array: string[] = query[i].split("="); 15 | if (array[0]) { 16 | if (!vars[array[0]]) vars[array[0]] = []; 17 | vars[decodeURIComponent(array[0])].push(decodeURIComponent(array[1])); 18 | } 19 | } 20 | return vars; 21 | } 22 | 23 | export function toQueryString(map: { [key: string]: string[] }): string { 24 | let q = ""; 25 | Object.keys(map).forEach(key => { 26 | let val = map[key]; 27 | if (Array.isArray(val)) { 28 | (val).forEach(a => { 29 | if (q.length > 0) q += "&"; 30 | q += encodeURIComponent(key) + "=" + encodeURIComponent(a); 31 | }); 32 | } else { 33 | if (q.length > 0) q += "&"; 34 | q += encodeURIComponent(key) + "=" + encodeURIComponent(val); 35 | } 36 | }); 37 | return "?" + q; 38 | } 39 | 40 | export function getPath(): string { 41 | return window.location.pathname; 42 | } 43 | 44 | //popstateで動かすための仕組み 45 | const popStateFunctions: ((ev: PopStateEvent) => void)[] = []; 46 | window.onpopstate = (ev: PopStateEvent) => { 47 | popStateFunctions.forEach(f => f(ev)); 48 | }; 49 | 50 | export function addPopStateListener(f: (ev: PopStateEvent) => void) { 51 | popStateFunctions.push(f); 52 | } 53 | 54 | export function pushState() {} 55 | 56 | const HASH_LISTENERS: HashRouterMixin[] = []; 57 | 58 | window.onhashchange = ev => { 59 | HASH_LISTENERS.forEach(hl => { 60 | hl.setState(); 61 | }); 62 | }; 63 | 64 | @Component({}) 65 | export class HashRouterMixin extends Vue { 66 | created() { 67 | HASH_LISTENERS.push(this); 68 | } 69 | 70 | beforeMount() { 71 | this.setState(); 72 | } 73 | 74 | destoryed() { 75 | removeFromArray(HASH_LISTENERS, this); 76 | } 77 | 78 | public setHash(hash: string) { 79 | window.history.pushState(null, null, "#" + hash); 80 | this.onHashChange(hash); 81 | } 82 | 83 | public setState() { 84 | let hash = window.location.hash; 85 | hash = hash.substring(1); 86 | this.onHashChange(hash); 87 | } 88 | 89 | onHashChange(hash: string) {} 90 | } 91 | 92 | export interface IHashRouterMixin { 93 | setHash(hash: string); 94 | setState(); 95 | onHashChange(hash: string); 96 | } 97 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "es2015", 5 | "moduleResolution": "node", 6 | "lib": ["dom", "es6", "es2016", "scripthost"], 7 | "sourceMap": true, 8 | "declaration": false, 9 | "noImplicitAny": false, 10 | "preserveConstEnums": false, 11 | "emitDecoratorMetadata": true, 12 | "experimentalDecorators": true, 13 | "removeComments": true, 14 | "allowSyntheticDefaultImports": true, 15 | "suppressImplicitAnyIndexErrors": true, 16 | "strictPropertyInitialization": false, 17 | "noImplicitThis": true, 18 | "baseUrl": "./src", 19 | "typeRoots": ["./node_modules/@types/"], 20 | "importHelpers": true, 21 | "noEmitHelpers": true 22 | }, 23 | "include": ["./src/**/*"] 24 | } 25 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/webpack.common.js: -------------------------------------------------------------------------------- 1 | const webpack = require("webpack"); 2 | const VueLoaderPlugin = require("vue-loader/lib/plugin"); 3 | 4 | module.exports = { 5 | context: __dirname + "/src", 6 | entry: { 7 | app: "./main.ts" 8 | }, 9 | optimization: { 10 | moduleIds: "named" 11 | }, 12 | resolve: { 13 | extensions: [".ts", ".js", ".vue"], 14 | alias: { 15 | vue: "vue/dist/vue.common.js", 16 | "vue-class-component": 17 | "vue-class-component/dist/vue-class-component.common.js" 18 | }, 19 | modules: ["./src", "node_modules"] 20 | }, 21 | module: { 22 | rules: [ 23 | { 24 | test: /\.vue$/, 25 | exclude: /node_modules/, 26 | loader: "vue-loader" 27 | }, 28 | { 29 | test: /\.html$/, 30 | use: [ 31 | { 32 | loader: "html-loader", 33 | options: { 34 | minimize: true 35 | } 36 | } 37 | ] 38 | }, 39 | { 40 | test: /\.(sass|scss)$/, 41 | use: [ 42 | "vue-style-loader", 43 | "css-loader", 44 | { 45 | loader: "sass-loader", 46 | options: { 47 | includePaths: ["src", "src/styles", "node_modules"] 48 | } 49 | } 50 | ] 51 | }, 52 | { 53 | test: /\.css$/, //Check for sass or scss file names 54 | use: ["style-loader", "css-loader"] 55 | }, 56 | { 57 | test: /\.ts$/, 58 | loader: "ts-loader", 59 | options: { appendTsSuffixTo: [/\.vue$/] } 60 | }, 61 | { 62 | test: /\.(jpg|png|svg)$/, 63 | loaders: "file-loader?name=[name].[ext]" 64 | } 65 | ] 66 | }, 67 | plugins: [new VueLoaderPlugin(), new webpack.ProvidePlugin({})] 68 | }; 69 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/webpack.dev.js: -------------------------------------------------------------------------------- 1 | var merge = require("webpack-merge"); // webpack-merge 2 | var common = require("./webpack.common.js"); // 汎用設定をインポート 3 | var webpack = require("webpack"); 4 | 5 | module.exports = merge(common, { 6 | mode: "development", 7 | output: { 8 | path: __dirname + "/dst/assets/js/", 9 | publicPath: "/assets/js/", 10 | filename: "[name].bundle.js" 11 | }, 12 | devServer: { 13 | contentBase: __dirname + "/dst/", 14 | host: "0.0.0.0", 15 | disableHostCheck: true, 16 | proxy: [ 17 | { 18 | context: ["/api/**"], 19 | target: "http://localhost:9985/" 20 | } 21 | ], 22 | historyApiFallback: { 23 | index: "/index.html" 24 | } 25 | }, 26 | plugins: [ 27 | new webpack.DefinePlugin({ 28 | DEFINE_BASE_FULL_PATH: "'/'" 29 | }) 30 | ], 31 | devtool: "inline-source-map" 32 | }); 33 | -------------------------------------------------------------------------------- /tugidigi-annotation/web/webpack.prod.js: -------------------------------------------------------------------------------- 1 | var merge = require("webpack-merge"); // webpack-merge 2 | var common = require("./webpack.common.js"); // 汎用設定をインポート 3 | var webpack = require("webpack"); 4 | 5 | module.exports = merge(common, { 6 | mode: "production", 7 | output: { 8 | path: __dirname + "/../src/main/resources/static/assets/", 9 | publicPath: "/annot/assets/", 10 | filename: "[name].bundle.js" 11 | }, 12 | plugins: [ 13 | new webpack.DefinePlugin({ 14 | DEFINE_BASE_FULL_PATH: "'/annot/'" 15 | }) 16 | ] 17 | }); 18 | --------------------------------------------------------------------------------