ikeda commited on
Commit
6810bed
1 Parent(s): bc6513e

translate Japanese comments to English

Browse files
app.py CHANGED
@@ -1,5 +1,5 @@
1
  import gradio as gr
2
- from PIL import Image as PIL_Image # gltflibのImageと被るので別名にする。
3
  from io import BytesIO
4
  import json
5
  import os
@@ -24,7 +24,7 @@ def create_3dmodel(model_no, title, color, mark, historic_site_type, difficulty,
24
 
25
  img_bytearray = BytesIO()
26
  image['background'].save(img_bytearray, "JPEG", quality=95)
27
- img_bytearray.seek(0) # 画像の先頭にシークしないと空データになってしまう。
28
 
29
  option_dict = {
30
  'タイトル': title,
@@ -36,32 +36,31 @@ def create_3dmodel(model_no, title, color, mark, historic_site_type, difficulty,
36
  '厚み': '有' if is_thick else '無',
37
  }
38
 
39
- # model_noのコード値決定ルールを接頭語を付けた場合分けやファクトリ―クラスの作成を検討した方が良い
40
  if model_no not in ['A', 'B']:
41
- # カード画像(表面)の作成
42
  if model_no == '1':
43
  front_img_bytearray = create_historic_site_card_image(img_bytearray, option_dict)
44
  else:
45
  front_img_bytearray = create_card_image(model_no, img_bytearray, option_dict)
46
 
47
- # カード画像(裏面)の取得
48
  back_path = constants.back_card_img_dict[model_no]
49
  back_img = PIL_Image.open(back_path)
50
  back_img_bytearray = BytesIO()
51
  back_img.convert('RGB').save(back_img_bytearray, "JPEG", quality=95)
52
- back_img_bytearray.seek(0) # 画像の先頭にシークしないと空データになってしまう。
53
 
54
- # カードの3Dモデルの作成(戻り値は作成したモデルのパス)
55
  model_path = create_card_model(front_img_bytearray, back_img_bytearray, option_dict)
56
 
57
  else:
58
- # 3Dモデルの作成(戻り値は作成したモデルのパス)
59
  if model_no == 'A':
60
  model_path = create_picture_box_model(img_bytearray)
61
  if model_no == 'B':
62
  model_path = create_extracted_objects_model(img_bytearray)
63
 
64
- # 作成した3Dモデルの送信
65
  return model_path
66
 
67
  with gr.Blocks() as demo:
 
1
  import gradio as gr
2
+ from PIL import Image as PIL_Image # Renaming to avoid conflict with Image from gltflib
3
  from io import BytesIO
4
  import json
5
  import os
 
24
 
25
  img_bytearray = BytesIO()
26
  image['background'].save(img_bytearray, "JPEG", quality=95)
27
+ img_bytearray.seek(0) # Seek to the beginning of the image, otherwise it results in empty data.
28
 
29
  option_dict = {
30
  'タイトル': title,
 
36
  '厚み': '有' if is_thick else '無',
37
  }
38
 
39
+ # Consider implementing model_no categorization rules or creating a factory class
40
  if model_no not in ['A', 'B']:
41
+ # Create the card image (front side)
42
  if model_no == '1':
43
  front_img_bytearray = create_historic_site_card_image(img_bytearray, option_dict)
44
  else:
45
  front_img_bytearray = create_card_image(model_no, img_bytearray, option_dict)
46
 
47
+ # Retrieve the card image (back side)
48
  back_path = constants.back_card_img_dict[model_no]
49
  back_img = PIL_Image.open(back_path)
50
  back_img_bytearray = BytesIO()
51
  back_img.convert('RGB').save(back_img_bytearray, "JPEG", quality=95)
52
+ back_img_bytearray.seek(0) # Seek to the beginning of the image, otherwise it results in empty data
53
 
54
+ # Create a 3D model of the card (return value is the path of the created model)
55
  model_path = create_card_model(front_img_bytearray, back_img_bytearray, option_dict)
56
 
57
  else:
58
+ # Create a 3D model of the card (return value is the path of the created model)
59
  if model_no == 'A':
60
  model_path = create_picture_box_model(img_bytearray)
61
  if model_no == 'B':
62
  model_path = create_extracted_objects_model(img_bytearray)
63
 
 
64
  return model_path
65
 
66
  with gr.Blocks() as demo:
src/card_model.py CHANGED
@@ -1,6 +1,6 @@
1
  import io
2
  import numpy as np
3
- from PIL import Image as PIL_Image # gltflibのImageと被るので別名にする。
4
  import struct
5
  import uuid
6
 
@@ -8,12 +8,13 @@ from gltflib import (
8
  GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
9
  BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness)
10
 
11
- # 共通設定情報
12
- # バイナリ部分のオフセットやサイズに関わらない部分は予め定義できる。
13
- # アセット
 
14
  asset=Asset()
15
 
16
- # イメージ
17
  images=[
18
  Image(mimeType='image/jpeg', bufferView=4),
19
  Image(mimeType='image/jpeg',bufferView=5),
@@ -22,20 +23,20 @@ images=[
22
  Image(mimeType='image/jpeg',bufferView=8),
23
  Image(mimeType='image/jpeg',bufferView=9),
24
  ]
25
- # サンプラー
26
- samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング
27
 
28
- # テクスチャ
29
  textures = [
30
  Texture(name='Front',sampler=0,source=0),
31
  Texture(name='Back',sampler=0,source=1),
32
- Texture(name='Left',sampler=0,source=2), # 左
33
- Texture(name='Right',sampler=0,source=3), # 右
34
- Texture(name='Top',sampler=0,source=4), # 上
35
- Texture(name='Bottom',sampler=0,source=5), # 下
36
  ]
37
 
38
- # マテリアル
39
  materials = [
40
  Material(
41
  pbrMetallicRoughness=PBRMetallicRoughness(
@@ -99,7 +100,7 @@ materials = [
99
  ),
100
  ]
101
 
102
- # メッシュ
103
  meshes = [
104
  Mesh(name='Front', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=0)]),
105
  Mesh(name='Back', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=1)]),
@@ -113,44 +114,44 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
113
 
114
  is_thick = True if option_dict['厚み'] == '有' else False
115
 
116
- # 表面画像
117
  front_img = PIL_Image.open(front_img_bytearray).convert('RGB')
118
  front_bytearray = io.BytesIO()
119
- front_img.save(front_bytearray, format="JPEG", quality=95) # JPEG保存を強制
120
  front_bytearray = front_bytearray.getvalue()
121
  front_bytelen = len(front_bytearray)
122
 
123
- # 裏面画像
124
  back_img = PIL_Image.open(back_img_bytearray).convert('RGB')
125
  back_bytearray = io.BytesIO()
126
- back_img.save(back_bytearray, format="JPEG", quality=95) # JPEG保存を強制
127
  back_bytearray = back_bytearray.getvalue()
128
  back_bytelen = len(back_bytearray)
129
 
130
- # 側面画像の作成(裏面の端の色を延長する)
131
  back_img_array = np.array(back_img)
132
  left_side_img = PIL_Image.fromarray(np.tile(back_img_array[:, [0], :], [1, 32, 1]))
133
  right_side_img = PIL_Image.fromarray(np.tile(back_img_array[:, [back_img_array.shape[1] - 1], :], [1, 32, 1]))
134
  top_side_img = PIL_Image.fromarray(np.tile(back_img_array[[0], :, :], [32, 1, 1]))
135
  bottom_side_img = PIL_Image.fromarray(np.tile(back_img_array[[back_img_array.shape[0] - 1], :, :], [32, 1, 1]))
136
  left_side_bytearray = io.BytesIO()
137
- left_side_img.save(left_side_bytearray, format="JPEG", quality=95) # JPEG保存を強制
138
  left_side_bytearray = left_side_bytearray.getvalue()
139
  left_side_bytelen = len(left_side_bytearray)
140
  right_side_bytearray = io.BytesIO()
141
- right_side_img.save(right_side_bytearray, format="JPEG", quality=95) # JPEG保存を強制
142
  right_side_bytearray = right_side_bytearray.getvalue()
143
  right_side_bytelen = len(right_side_bytearray)
144
  top_side_bytearray = io.BytesIO()
145
- top_side_img.save(top_side_bytearray, format="JPEG", quality=95) # JPEG保存を強制
146
  top_side_bytearray = top_side_bytearray.getvalue()
147
  top_side_bytelen = len(top_side_bytearray)
148
  bottom_side_bytearray = io.BytesIO()
149
- bottom_side_img.save(bottom_side_bytearray, format="JPEG", quality=95) # JPEG保存を強制
150
  bottom_side_bytearray = bottom_side_bytearray.getvalue()
151
  bottom_side_bytelen = len(bottom_side_bytearray)
152
 
153
- # 頂点データ(POSITION)
154
  vertices = [
155
  (-1.0, -1.0, 0.0),
156
  ( 1.0, -1.0, 0.0),
@@ -165,7 +166,7 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
165
  mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
166
  maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
167
 
168
- # 法線データ(NORMAL)
169
  normals = [( 0.0, 0.0, 1.0)] * 4
170
  normal_bytearray = bytearray()
171
  for normal in normals:
@@ -173,7 +174,7 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
173
  normal_bytearray.extend(struct.pack('f', value))
174
  normal_bytelen = len(normal_bytearray)
175
 
176
- # テクスチャ座標(TEXCOORD_0)
177
  texcoord_0s = [
178
  (0.0, 1.0),
179
  (1.0, 1.0),
@@ -186,14 +187,14 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
186
  texcoord_0_bytearray.extend(struct.pack('f', value))
187
  texcoord_0_bytelen = len(texcoord_0_bytearray)
188
 
189
- # 頂点インデックス
190
  vertex_indices = [0, 1, 2, 1, 3, 2]
191
  vertex_index_bytearray = bytearray()
192
  for value in vertex_indices:
193
  vertex_index_bytearray.extend(struct.pack('H', value))
194
  vertex_index_bytelen = len(vertex_index_bytearray)
195
 
196
- # バイナリデータ部分の結合
197
  bytearray_list = [
198
  vertex_bytearray,
199
  normal_bytearray,
@@ -224,17 +225,16 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
224
  all_bytearray = bytearray()
225
  for temp_bytearray in bytearray_list:
226
  all_bytearray.extend(temp_bytearray)
227
- offset_list = [0] + bytelen_cumsum_list # 最初のオフセットは0
228
- offset_list.pop() # 末尾を削除
229
 
230
- # リソースの作成
231
  resources = [GLBResource(data=all_bytearray)]
232
 
233
- # 各種設定
234
- # バッファ
235
  buffers = [Buffer(byteLength=len(all_bytearray))]
236
 
237
- # バッファビュー
238
  bufferViews = [
239
  BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
240
  BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
@@ -248,7 +248,7 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
248
  BufferView(buffer=0, byteOffset=offset_list[9], byteLength=bytelen_list[9], target=None),
249
  ]
250
 
251
- # アクセサー
252
  accessors = [
253
  Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins),
254
  Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None),
@@ -256,7 +256,7 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
256
  Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None),
257
  ]
258
 
259
- # ノード
260
  card_thickness = 0.025
261
  card_ratio_x = 0.8679999709129333
262
  card_ratio_y = 1.2130000591278076
@@ -277,7 +277,7 @@ def create_card_model(front_img_bytearray, back_img_bytearray, option_dict):
277
  Node(mesh=1, scale=[card_ratio_x, card_ratio_y, card_ratio_z], rotation=[0, 1, 0, 0])
278
  ]
279
 
280
- # シーン
281
  scene = 0
282
  if is_thick:
283
  scenes = [Scene(name='Scene', nodes=[0, 1, 2, 3, 4, 5])]
 
1
  import io
2
  import numpy as np
3
+ from PIL import Image as PIL_Image # Renaming to avoid conflict with Image from gltflib
4
  import struct
5
  import uuid
6
 
 
8
  GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
9
  BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness)
10
 
11
+ # Common configuration information
12
+ # Parts that are independent of the binary section's offset and size can be predefined.
13
+
14
+ # Asset
15
  asset=Asset()
16
 
17
+ # Image
18
  images=[
19
  Image(mimeType='image/jpeg', bufferView=4),
20
  Image(mimeType='image/jpeg',bufferView=5),
 
23
  Image(mimeType='image/jpeg',bufferView=8),
24
  Image(mimeType='image/jpeg',bufferView=9),
25
  ]
26
+ # Sampler
27
+ samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter: Nearest filtering, minFilter: Mipmap + Nearest filtering
28
 
29
+ # Texture
30
  textures = [
31
  Texture(name='Front',sampler=0,source=0),
32
  Texture(name='Back',sampler=0,source=1),
33
+ Texture(name='Left',sampler=0,source=2),
34
+ Texture(name='Right',sampler=0,source=3),
35
+ Texture(name='Top',sampler=0,source=4),
36
+ Texture(name='Bottom',sampler=0,source=5),
37
  ]
38
 
39
+ # Material
40
  materials = [
41
  Material(
42
  pbrMetallicRoughness=PBRMetallicRoughness(
 
100
  ),
101
  ]
102
 
103
+ # Mesh
104
  meshes = [
105
  Mesh(name='Front', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=0)]),
106
  Mesh(name='Back', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2), indices=3, material=1)]),
 
114
 
115
  is_thick = True if option_dict['厚み'] == '有' else False
116
 
117
+ # Front image
118
  front_img = PIL_Image.open(front_img_bytearray).convert('RGB')
119
  front_bytearray = io.BytesIO()
120
+ front_img.save(front_bytearray, format="JPEG", quality=95) # Force JPEG format for saving
121
  front_bytearray = front_bytearray.getvalue()
122
  front_bytelen = len(front_bytearray)
123
 
124
+ # Back image
125
  back_img = PIL_Image.open(back_img_bytearray).convert('RGB')
126
  back_bytearray = io.BytesIO()
127
+ back_img.save(back_bytearray, format="JPEG", quality=95) # Force JPEG format for saving
128
  back_bytearray = back_bytearray.getvalue()
129
  back_bytelen = len(back_bytearray)
130
 
131
+ # Create side image (extend the color of the edges of the back side)
132
  back_img_array = np.array(back_img)
133
  left_side_img = PIL_Image.fromarray(np.tile(back_img_array[:, [0], :], [1, 32, 1]))
134
  right_side_img = PIL_Image.fromarray(np.tile(back_img_array[:, [back_img_array.shape[1] - 1], :], [1, 32, 1]))
135
  top_side_img = PIL_Image.fromarray(np.tile(back_img_array[[0], :, :], [32, 1, 1]))
136
  bottom_side_img = PIL_Image.fromarray(np.tile(back_img_array[[back_img_array.shape[0] - 1], :, :], [32, 1, 1]))
137
  left_side_bytearray = io.BytesIO()
138
+ left_side_img.save(left_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving
139
  left_side_bytearray = left_side_bytearray.getvalue()
140
  left_side_bytelen = len(left_side_bytearray)
141
  right_side_bytearray = io.BytesIO()
142
+ right_side_img.save(right_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving
143
  right_side_bytearray = right_side_bytearray.getvalue()
144
  right_side_bytelen = len(right_side_bytearray)
145
  top_side_bytearray = io.BytesIO()
146
+ top_side_img.save(top_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving
147
  top_side_bytearray = top_side_bytearray.getvalue()
148
  top_side_bytelen = len(top_side_bytearray)
149
  bottom_side_bytearray = io.BytesIO()
150
+ bottom_side_img.save(bottom_side_bytearray, format="JPEG", quality=95) # Force JPEG format for saving
151
  bottom_side_bytearray = bottom_side_bytearray.getvalue()
152
  bottom_side_bytelen = len(bottom_side_bytearray)
153
 
154
+ # Vertex data(POSITION)
155
  vertices = [
156
  (-1.0, -1.0, 0.0),
157
  ( 1.0, -1.0, 0.0),
 
166
  mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
167
  maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
168
 
169
+ # Normal data(NORMAL)
170
  normals = [( 0.0, 0.0, 1.0)] * 4
171
  normal_bytearray = bytearray()
172
  for normal in normals:
 
174
  normal_bytearray.extend(struct.pack('f', value))
175
  normal_bytelen = len(normal_bytearray)
176
 
177
+ # Texture coordinates(TEXCOORD_0)
178
  texcoord_0s = [
179
  (0.0, 1.0),
180
  (1.0, 1.0),
 
187
  texcoord_0_bytearray.extend(struct.pack('f', value))
188
  texcoord_0_bytelen = len(texcoord_0_bytearray)
189
 
190
+ # Vertex indices
191
  vertex_indices = [0, 1, 2, 1, 3, 2]
192
  vertex_index_bytearray = bytearray()
193
  for value in vertex_indices:
194
  vertex_index_bytearray.extend(struct.pack('H', value))
195
  vertex_index_bytelen = len(vertex_index_bytearray)
196
 
197
+ # Concatenation of the binary data section
198
  bytearray_list = [
199
  vertex_bytearray,
200
  normal_bytearray,
 
225
  all_bytearray = bytearray()
226
  for temp_bytearray in bytearray_list:
227
  all_bytearray.extend(temp_bytearray)
228
+ offset_list = [0] + bytelen_cumsum_list # The first offset is 0
229
+ offset_list.pop() # Remove the end
230
 
231
+ # GLBResource
232
  resources = [GLBResource(data=all_bytearray)]
233
 
234
+ # Buffer
 
235
  buffers = [Buffer(byteLength=len(all_bytearray))]
236
 
237
+ # BufferView
238
  bufferViews = [
239
  BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
240
  BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
 
248
  BufferView(buffer=0, byteOffset=offset_list[9], byteLength=bytelen_list[9], target=None),
249
  ]
250
 
251
+ # Accessor
252
  accessors = [
253
  Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins),
254
  Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None),
 
256
  Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None),
257
  ]
258
 
259
+ # Node
260
  card_thickness = 0.025
261
  card_ratio_x = 0.8679999709129333
262
  card_ratio_y = 1.2130000591278076
 
277
  Node(mesh=1, scale=[card_ratio_x, card_ratio_y, card_ratio_z], rotation=[0, 1, 0, 0])
278
  ]
279
 
280
+ # Scene
281
  scene = 0
282
  if is_thick:
283
  scenes = [Scene(name='Scene', nodes=[0, 1, 2, 3, 4, 5])]
src/extracted_objects_model.py CHANGED
@@ -1,6 +1,6 @@
1
  import io
2
  import numpy as np
3
- from PIL import Image as PIL_Image # gltflibのImageと被るので別名にする。
4
  import cv2
5
  import struct
6
  import triangle
@@ -10,17 +10,17 @@ from gltflib import (
10
  GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
11
  BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness)
12
 
13
- # 表面及び裏面の頂点リストの作成
14
  def make_front_and_back_vertex_list(coordinate_list, img):
15
 
16
- # 表面の頂点
17
  front_vertex_list = []
18
- # 裏面の頂点
19
  back_vertex_list = []
20
  for coordinates in coordinate_list:
21
  front_vertices = []
22
  back_vertices = []
23
- # Y軸方向は画像とGLBで上下が逆になるので注意
24
  for coordinate in coordinates:
25
  front_vertices.append((coordinate[0] * 2 / img.size[0] - 1.0, -(coordinate[1] * 2 / img.size[1] - 1.0), 0.2))
26
  back_vertices.append((coordinate[0] * 2 / img.size[0] - 1.0, -(coordinate[1] * 2 / img.size[1] - 1.0), -0.2))
@@ -30,13 +30,13 @@ def make_front_and_back_vertex_list(coordinate_list, img):
30
 
31
  return front_vertex_list, back_vertex_list
32
 
33
- # メッシュの各種情報の作成
34
  def make_mesh_data(coordinate_list, img):
35
  front_vertex_list, back_vertex_list = make_front_and_back_vertex_list(coordinate_list, img)
36
 
37
- # 頂点データ(POSITION)
38
  vertices = []
39
- # 頂点インデックス決定時に使うオフセット値のリスト
40
  front_offset = 0
41
  front_offset_list = []
42
  back_offset_list = []
@@ -49,51 +49,51 @@ def make_mesh_data(coordinate_list, img):
49
  back_offset_list.append(back_offset)
50
  front_offset += len(front_vertices) + len(back_vertices)
51
 
52
- # 法線データ(NORMAL)
53
  normals = []
54
  for front_vertices, back_vertices in zip(front_vertex_list, back_vertex_list):
55
  normals.extend([( 0.0, 0.0, 1.0)] * len(front_vertices))
56
  normals.extend([( 0.0, 0.0, -1.0)] * len(back_vertices))
57
 
58
- # テクスチャ座標(TEXCOORD_0)
59
- # 画像は左上原点になり、Y軸の上下を変える必要がある。
60
  texcoord_0s = [((vertex[0] + 1.0) / 2.0, 1.0 - ((vertex[1] + 1.0) / 2.0) ) for vertex in vertices]
61
 
62
- # 頂点インデックス
63
  vertex_indices = []
64
  for front_vertices, back_vertices, front_offset, back_offset \
65
  in zip(front_vertex_list, back_vertex_list, front_offset_list, back_offset_list):
66
  polygon = {
67
  'vertices': np.array(front_vertices)[:, :2],
68
- 'segments': np.array([( i, (i + 1) % (len(front_vertices)) ) for i in range(len(front_vertices))]) # 各辺を定義
69
  }
70
  triangulate_result = triangle.triangulate(polygon, 'p')
71
- vertex_indices.extend(list(np.array(triangulate_result['triangles']+front_offset).flatten())) # 表面
72
- vertex_indices.extend(list((np.array(triangulate_result['triangles'])+back_offset).flatten())) # 裏面
73
  vertex_indices.extend(list(np.array([[front_offset + i,
74
  front_offset + (i + 1) % len(front_vertices),
75
  back_offset + i]
76
- for i in range(len(front_vertices))]).flatten())) # 側面1
77
  vertex_indices.extend(list(np.array([[back_offset + i,
78
  back_offset + (i + 1) % len(back_vertices),
79
- front_offset+ (i + 1) % len(front_vertices)] for i in range(len(front_vertices))]).flatten())) # 側面2
80
 
81
  return vertices, normals, texcoord_0s, vertex_indices
82
 
83
  def create_extracted_objects_model(img_bytearray):
84
 
85
- # 画像の取得
86
  img = PIL_Image.open(img_bytearray).convert('RGB')
87
  img_bytearray = io.BytesIO()
88
  img.save(img_bytearray, format="JPEG", quality=95)
89
  img_bytearray = img_bytearray.getvalue()
90
  img_bytelen = len(img_bytearray)
91
 
92
- # 3Dモデルのスケールの計算
93
  scale_factor = np.power(img.size[0] * img.size[1], 0.5)
94
  scale = (img.size[0] / scale_factor, img.size[1] / scale_factor, 0.4)
95
 
96
- # 画像主要部分の頂点の取得
97
  base_color = img.getpixel((0, 0))
98
  mask = PIL_Image.new('RGB', img.size)
99
  for i in range(img.size[0]):
@@ -112,10 +112,10 @@ def create_extracted_objects_model(img_bytearray):
112
  coordinates.append((x, y))
113
  coordinate_list.append(coordinates)
114
 
115
- # 各種データの作成
116
  vertices, normals, texcoord_0s, vertex_indices = make_mesh_data(coordinate_list, img)
117
 
118
- # 頂点データ(POSITION)
119
  vertex_bytearray = bytearray()
120
  for vertex in vertices:
121
  for value in vertex:
@@ -124,14 +124,14 @@ def create_extracted_objects_model(img_bytearray):
124
  mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
125
  maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
126
 
127
- # 法線データ(NORMAL)
128
  normal_bytearray = bytearray()
129
  for normal in normals:
130
  for value in normal:
131
  normal_bytearray.extend(struct.pack('f', value))
132
  normal_bytelen = len(normal_bytearray)
133
 
134
- # テクスチャ座標(TEXCOORD_0)
135
  texcoord_0s = [
136
  ((vertex[0] + 1.0) / 2.0, 1.0 - ((vertex[1] + 1.0) / 2.0) ) for vertex in vertices
137
  ]
@@ -141,13 +141,13 @@ def create_extracted_objects_model(img_bytearray):
141
  texcoord_0_bytearray.extend(struct.pack('f', value))
142
  texcoord_0_bytelen = len(texcoord_0_bytearray)
143
 
144
- # 頂点インデックス
145
  vertex_index_bytearray = bytearray()
146
  for value in vertex_indices:
147
  vertex_index_bytearray.extend(struct.pack('H', value))
148
  vertex_index_bytelen = len(vertex_index_bytearray)
149
 
150
- # バイナリデータ部分の結合
151
  bytearray_list = [
152
  vertex_bytearray,
153
  normal_bytearray,
@@ -168,20 +168,19 @@ def create_extracted_objects_model(img_bytearray):
168
  all_bytearray = bytearray()
169
  for temp_bytearray in bytearray_list:
170
  all_bytearray.extend(temp_bytearray)
171
- offset_list = [0] + bytelen_cumsum_list # 最初のオフセットは0
172
  offset_list.pop() # 末尾を削除
173
 
174
- # リソースの作成
175
  resources = [GLBResource(data=all_bytearray)]
176
 
177
- # 各種設定
178
- # アセット
179
  asset=Asset()
180
 
181
- # バッファ
182
  buffers = [Buffer(byteLength=len(all_bytearray))]
183
 
184
- # バッファビュー
185
  bufferViews = [
186
  BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
187
  BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
@@ -190,7 +189,7 @@ def create_extracted_objects_model(img_bytearray):
190
  BufferView(buffer=0, byteOffset=offset_list[4], byteLength=bytelen_list[4], target=None),
191
  ]
192
 
193
- # アクセサー
194
  accessors = [
195
  Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins),
196
  Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None),
@@ -198,20 +197,20 @@ def create_extracted_objects_model(img_bytearray):
198
  Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None)
199
  ]
200
 
201
- # イメージ
202
  images=[
203
  Image(mimeType='image/jpeg', bufferView=4),
204
  ]
205
 
206
- # サンプラー
207
  samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング
208
 
209
- # テクスチャ
210
  textures = [
211
  Texture(name='Main',sampler=0,source=0),
212
  ]
213
 
214
- # マテリアル
215
  materials = [
216
  Material(
217
  pbrMetallicRoughness=PBRMetallicRoughness(
@@ -225,21 +224,22 @@ def create_extracted_objects_model(img_bytearray):
225
  ),
226
  ]
227
 
228
- # メッシュ
229
  meshes = [
230
  Mesh(name='Main', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2),
231
  indices=3, material=0, mode=4)]),
232
  ]
233
 
234
- # ノード
235
  nodes = [
236
  Node(mesh=0,rotation=None, scale=scale),
237
  ]
238
 
239
- # シーン
240
  scene = 0
241
  scenes = [Scene(name='Scene', nodes=[0])]
242
 
 
243
  model = GLTFModel(
244
  asset=asset,
245
  buffers=buffers,
 
1
  import io
2
  import numpy as np
3
+ from PIL import Image as PIL_Image # Renaming to avoid conflict with Image from gltflib
4
  import cv2
5
  import struct
6
  import triangle
 
10
  GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
11
  BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness)
12
 
13
+ # Create vertex lists for both the front and back surfaces
14
  def make_front_and_back_vertex_list(coordinate_list, img):
15
 
16
+ # Front surface vertices
17
  front_vertex_list = []
18
+ # Back surface vertices
19
  back_vertex_list = []
20
  for coordinates in coordinate_list:
21
  front_vertices = []
22
  back_vertices = []
23
+ # Note that the Y-axis direction is inverted between the image and GLB, so be careful
24
  for coordinate in coordinates:
25
  front_vertices.append((coordinate[0] * 2 / img.size[0] - 1.0, -(coordinate[1] * 2 / img.size[1] - 1.0), 0.2))
26
  back_vertices.append((coordinate[0] * 2 / img.size[0] - 1.0, -(coordinate[1] * 2 / img.size[1] - 1.0), -0.2))
 
30
 
31
  return front_vertex_list, back_vertex_list
32
 
33
+ # Creation of various information for the mesh
34
  def make_mesh_data(coordinate_list, img):
35
  front_vertex_list, back_vertex_list = make_front_and_back_vertex_list(coordinate_list, img)
36
 
37
+ # Vertex data(POSITION)
38
  vertices = []
39
+ # List of offset values used when determining vertex indices
40
  front_offset = 0
41
  front_offset_list = []
42
  back_offset_list = []
 
49
  back_offset_list.append(back_offset)
50
  front_offset += len(front_vertices) + len(back_vertices)
51
 
52
+ # Normal data(NORMAL)
53
  normals = []
54
  for front_vertices, back_vertices in zip(front_vertex_list, back_vertex_list):
55
  normals.extend([( 0.0, 0.0, 1.0)] * len(front_vertices))
56
  normals.extend([( 0.0, 0.0, -1.0)] * len(back_vertices))
57
 
58
+ # Texture coordinates (TEXCOORD_0)
59
+ # The image origin is at the top-left, requiring an inversion of the Y-axis.
60
  texcoord_0s = [((vertex[0] + 1.0) / 2.0, 1.0 - ((vertex[1] + 1.0) / 2.0) ) for vertex in vertices]
61
 
62
+ # Vertex indices
63
  vertex_indices = []
64
  for front_vertices, back_vertices, front_offset, back_offset \
65
  in zip(front_vertex_list, back_vertex_list, front_offset_list, back_offset_list):
66
  polygon = {
67
  'vertices': np.array(front_vertices)[:, :2],
68
+ 'segments': np.array([( i, (i + 1) % (len(front_vertices)) ) for i in range(len(front_vertices))]) # Define each edge
69
  }
70
  triangulate_result = triangle.triangulate(polygon, 'p')
71
+ vertex_indices.extend(list(np.array(triangulate_result['triangles']+front_offset).flatten())) # Front surface
72
+ vertex_indices.extend(list((np.array(triangulate_result['triangles'])+back_offset).flatten())) # Back surface
73
  vertex_indices.extend(list(np.array([[front_offset + i,
74
  front_offset + (i + 1) % len(front_vertices),
75
  back_offset + i]
76
+ for i in range(len(front_vertices))]).flatten())) # Side surface 1
77
  vertex_indices.extend(list(np.array([[back_offset + i,
78
  back_offset + (i + 1) % len(back_vertices),
79
+ front_offset+ (i + 1) % len(front_vertices)] for i in range(len(front_vertices))]).flatten())) # Side surface 2
80
 
81
  return vertices, normals, texcoord_0s, vertex_indices
82
 
83
  def create_extracted_objects_model(img_bytearray):
84
 
85
+ # Retrieve the image
86
  img = PIL_Image.open(img_bytearray).convert('RGB')
87
  img_bytearray = io.BytesIO()
88
  img.save(img_bytearray, format="JPEG", quality=95)
89
  img_bytearray = img_bytearray.getvalue()
90
  img_bytelen = len(img_bytearray)
91
 
92
+ # Calculate the scale of the 3D model
93
  scale_factor = np.power(img.size[0] * img.size[1], 0.5)
94
  scale = (img.size[0] / scale_factor, img.size[1] / scale_factor, 0.4)
95
 
96
+ # Retrieve vertices of the main part of the image
97
  base_color = img.getpixel((0, 0))
98
  mask = PIL_Image.new('RGB', img.size)
99
  for i in range(img.size[0]):
 
112
  coordinates.append((x, y))
113
  coordinate_list.append(coordinates)
114
 
115
+ # Creation of associated data for the mesh
116
  vertices, normals, texcoord_0s, vertex_indices = make_mesh_data(coordinate_list, img)
117
 
118
+ # Vertex data(POSITION)
119
  vertex_bytearray = bytearray()
120
  for vertex in vertices:
121
  for value in vertex:
 
124
  mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
125
  maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
126
 
127
+ # Normal data(NORMAL)
128
  normal_bytearray = bytearray()
129
  for normal in normals:
130
  for value in normal:
131
  normal_bytearray.extend(struct.pack('f', value))
132
  normal_bytelen = len(normal_bytearray)
133
 
134
+ # Texture coordinates(TEXCOORD_0)
135
  texcoord_0s = [
136
  ((vertex[0] + 1.0) / 2.0, 1.0 - ((vertex[1] + 1.0) / 2.0) ) for vertex in vertices
137
  ]
 
141
  texcoord_0_bytearray.extend(struct.pack('f', value))
142
  texcoord_0_bytelen = len(texcoord_0_bytearray)
143
 
144
+ # Vertex indices
145
  vertex_index_bytearray = bytearray()
146
  for value in vertex_indices:
147
  vertex_index_bytearray.extend(struct.pack('H', value))
148
  vertex_index_bytelen = len(vertex_index_bytearray)
149
 
150
+ # Concatenation of the binary data section
151
  bytearray_list = [
152
  vertex_bytearray,
153
  normal_bytearray,
 
168
  all_bytearray = bytearray()
169
  for temp_bytearray in bytearray_list:
170
  all_bytearray.extend(temp_bytearray)
171
+ offset_list = [0] + bytelen_cumsum_list # The first offset is 0
172
  offset_list.pop() # 末尾を削除
173
 
174
+ # GLBResource
175
  resources = [GLBResource(data=all_bytearray)]
176
 
177
+ # Asset
 
178
  asset=Asset()
179
 
180
+ # Buffer
181
  buffers = [Buffer(byteLength=len(all_bytearray))]
182
 
183
+ # BufferView
184
  bufferViews = [
185
  BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
186
  BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
 
189
  BufferView(buffer=0, byteOffset=offset_list[4], byteLength=bytelen_list[4], target=None),
190
  ]
191
 
192
+ # Accessor
193
  accessors = [
194
  Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins),
195
  Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None),
 
197
  Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None)
198
  ]
199
 
200
+ # Image
201
  images=[
202
  Image(mimeType='image/jpeg', bufferView=4),
203
  ]
204
 
205
+ # Sampler
206
  samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング
207
 
208
+ # Texture
209
  textures = [
210
  Texture(name='Main',sampler=0,source=0),
211
  ]
212
 
213
+ # Material
214
  materials = [
215
  Material(
216
  pbrMetallicRoughness=PBRMetallicRoughness(
 
224
  ),
225
  ]
226
 
227
+ # Mesh
228
  meshes = [
229
  Mesh(name='Main', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2),
230
  indices=3, material=0, mode=4)]),
231
  ]
232
 
233
+ # Node
234
  nodes = [
235
  Node(mesh=0,rotation=None, scale=scale),
236
  ]
237
 
238
+ # Scene
239
  scene = 0
240
  scenes = [Scene(name='Scene', nodes=[0])]
241
 
242
+ # GLTFModel
243
  model = GLTFModel(
244
  asset=asset,
245
  buffers=buffers,
src/front_card_image.py CHANGED
@@ -3,27 +3,27 @@ from io import BytesIO
3
 
4
  from src.constants import front_card_img_dict
5
 
6
- # カード画像各領域のピクセル位置情報
7
- # 写真
8
  PICTURE_LT_XY = (65, 188)
9
  PICTURE_RB_XY = (802, 925)
10
  PICTURE_SIZE = (PICTURE_RB_XY[0] - PICTURE_LT_XY[0], PICTURE_RB_XY[1] - PICTURE_LT_XY[1])
11
 
12
- # タイトル
13
- # ある程度の余白を作る。
14
  TITLE_LT_XY = (65, 45)
15
- TITLE_RB_XY = (647, 132) # マーク挿入部分と重ならないような位置
16
  TITLE_SIZE = (TITLE_RB_XY[0] - TITLE_LT_XY[0], TITLE_RB_XY[1] - TITLE_LT_XY[1])
17
 
18
- # 説明文本体
19
  DESCRIPTION_LT_XY = (46, 972)
20
  DESCRIPTION_RB_XY = (810, 1174)
21
  DESCRIPTION_SIZE = (DESCRIPTION_RB_XY[0] - DESCRIPTION_LT_XY[0], DESCRIPTION_RB_XY[1] - DESCRIPTION_LT_XY[1])
22
 
23
- # フォント関連
24
- # 明朝体
25
  font_selif_path = 'data/fonts/SourceHanSerif-Bold.otf'
26
- # ゴシック体
27
  font_sanselif_path = 'data/fonts/SourceHanSans-Bold.otf'
28
 
29
  def crop_center(pil_img, crop_width, crop_height):
@@ -38,22 +38,22 @@ def crop_max_square(pil_img):
38
 
39
  def create_card_image(model_no, img_bytearray, option_dict):
40
 
41
- # 画像の読み込みとトリミング
42
  picture_img = Image.open(img_bytearray)
43
  picture_img = crop_max_square(picture_img)
44
  picture_img = picture_img.resize(PICTURE_SIZE)
45
 
46
- # カードの読み込み
47
  card_img = Image.open(front_card_img_dict[model_no])
48
 
49
- # 写真の埋め込み
50
  card_img.paste(picture_img, PICTURE_LT_XY)
51
 
52
- # 各種書き込み準備
53
  card_imgdraw = ImageDraw.Draw(card_img)
54
 
55
- # タイトル埋め込み
56
- # (複数行対応は必要ならば対応)
57
  title_font_size = 100
58
  while True:
59
  title_font = ImageFont.truetype(font_selif_path, title_font_size)
@@ -65,7 +65,7 @@ def create_card_image(model_no, img_bytearray, option_dict):
65
 
66
  card_imgdraw.text((TITLE_LT_XY[0], int((TITLE_LT_XY[1] + TITLE_RB_XY[1]) / 2)), option_dict['タイトル'], fill='black', font=title_font, anchor='lm')
67
 
68
- # 説明文埋め込み
69
  description_font = ImageFont.truetype(font_sanselif_path, 40)
70
 
71
  description_list = []
@@ -83,9 +83,9 @@ def create_card_image(model_no, img_bytearray, option_dict):
83
 
84
  card_imgdraw.text(DESCRIPTION_LT_XY, description_display, fill='black', font=description_font)
85
 
86
- # バイナリデータの出力
87
  output_img_bytearray = BytesIO()
88
  card_img.convert('RGB').save(output_img_bytearray, "JPEG", quality=95)
89
- output_img_bytearray.seek(0) # 画像の先頭にシークしないと空データになってしまう。
90
 
91
  return output_img_bytearray
 
3
 
4
  from src.constants import front_card_img_dict
5
 
6
+ # Pixel position information for each area of the card image
7
+ # Picture
8
  PICTURE_LT_XY = (65, 188)
9
  PICTURE_RB_XY = (802, 925)
10
  PICTURE_SIZE = (PICTURE_RB_XY[0] - PICTURE_LT_XY[0], PICTURE_RB_XY[1] - PICTURE_LT_XY[1])
11
 
12
+ # Title
13
+ # Create some margin
14
  TITLE_LT_XY = (65, 45)
15
+ TITLE_RB_XY = (647, 132)
16
  TITLE_SIZE = (TITLE_RB_XY[0] - TITLE_LT_XY[0], TITLE_RB_XY[1] - TITLE_LT_XY[1])
17
 
18
+ # Main Body of Description
19
  DESCRIPTION_LT_XY = (46, 972)
20
  DESCRIPTION_RB_XY = (810, 1174)
21
  DESCRIPTION_SIZE = (DESCRIPTION_RB_XY[0] - DESCRIPTION_LT_XY[0], DESCRIPTION_RB_XY[1] - DESCRIPTION_LT_XY[1])
22
 
23
+ # Font information
24
+ # Ming Typeface
25
  font_selif_path = 'data/fonts/SourceHanSerif-Bold.otf'
26
+ # Gothic Typeface
27
  font_sanselif_path = 'data/fonts/SourceHanSans-Bold.otf'
28
 
29
  def crop_center(pil_img, crop_width, crop_height):
 
38
 
39
  def create_card_image(model_no, img_bytearray, option_dict):
40
 
41
+ # Loading and Cropping of Picture
42
  picture_img = Image.open(img_bytearray)
43
  picture_img = crop_max_square(picture_img)
44
  picture_img = picture_img.resize(PICTURE_SIZE)
45
 
46
+ # Loading of Card Frame
47
  card_img = Image.open(front_card_img_dict[model_no])
48
 
49
+ # Embedding a Picture in the Card
50
  card_img.paste(picture_img, PICTURE_LT_XY)
51
 
52
+ ## Preparing for editting the card image
53
  card_imgdraw = ImageDraw.Draw(card_img)
54
 
55
+ # Embedding the Title
56
+ # (Support for multiple lines if needed)
57
  title_font_size = 100
58
  while True:
59
  title_font = ImageFont.truetype(font_selif_path, title_font_size)
 
65
 
66
  card_imgdraw.text((TITLE_LT_XY[0], int((TITLE_LT_XY[1] + TITLE_RB_XY[1]) / 2)), option_dict['タイトル'], fill='black', font=title_font, anchor='lm')
67
 
68
+ # Embedding the Description Text
69
  description_font = ImageFont.truetype(font_sanselif_path, 40)
70
 
71
  description_list = []
 
83
 
84
  card_imgdraw.text(DESCRIPTION_LT_XY, description_display, fill='black', font=description_font)
85
 
86
+ # Outputting Binary Data
87
  output_img_bytearray = BytesIO()
88
  card_img.convert('RGB').save(output_img_bytearray, "JPEG", quality=95)
89
+ output_img_bytearray.seek(0) # Seek to the beginning of the image, otherwise it results in empty data
90
 
91
  return output_img_bytearray
src/front_card_image_historic_site.py CHANGED
@@ -1,7 +1,9 @@
1
  from PIL import Image, ImageDraw, ImageFont
2
  from io import BytesIO
3
 
4
- # カード画像関連
 
 
5
  card_frame_path_dict = {
6
  '橙': 'data/cards/史跡カードフレーム(橙).png',
7
  '白': 'data/cards/史跡カードフレーム(白).png',
@@ -25,38 +27,37 @@ card_mark_path_dict = {
25
  '都指定': 'data/cards/史跡カード指定マーク(都指定).png'
26
  }
27
 
28
- # カード画像各領域のピクセル位置情報
29
- # 写真
30
  PICTURE_LT_XY = (65, 188)
31
  PICTURE_RB_XY = (802, 925)
32
  PICTURE_SIZE = (PICTURE_RB_XY[0] - PICTURE_LT_XY[0], PICTURE_RB_XY[1] - PICTURE_LT_XY[1])
33
 
34
- # タイトル
35
- # ある程度の余白を作る。
36
  TITLE_LT_XY = (65, 45)
37
- TITLE_RB_XY = (647, 132) # マーク挿入部分と重ならないような位置
38
  TITLE_SIZE = (TITLE_RB_XY[0] - TITLE_LT_XY[0], TITLE_RB_XY[1] - TITLE_LT_XY[1])
39
 
40
- # 説明欄区切り線
41
  DESCRIPTION_LINE_L_XY = (52, 1024)
42
  DESCRIPTION_LINE_R_XY = (816, 1024)
43
 
44
- # 史跡種類
45
  HS_TYPE_LT_XY = (56, 972)
46
 
47
- # 訪問難度
48
  DIFFICULTY_LT_XY = (444, 972)
49
 
50
- # 説明文本体
51
  DESCRIPTION_LT_XY = (46, 1024)
52
  DESCRIPTION_RB_XY = (810, 1174)
53
  DESCRIPTION_SIZE = (DESCRIPTION_RB_XY[0] - DESCRIPTION_LT_XY[0], DESCRIPTION_RB_XY[1] - DESCRIPTION_LT_XY[1])
54
 
55
-
56
- # フォント関連
57
- # 明朝体
58
  font_selif_path = 'data/fonts/SourceHanSerif-Bold.otf'
59
- # ゴシック体
60
  font_sanselif_path = 'data/fonts/SourceHanSans-Bold.otf'
61
 
62
  def crop_center(pil_img, crop_width, crop_height):
@@ -71,36 +72,35 @@ def crop_max_square(pil_img):
71
 
72
  def create_historic_site_card_image(img_bytearray, option_dict):
73
 
74
- # 画像の読み込みとトリミング
75
  picture_img = Image.open(img_bytearray)
76
  picture_img = crop_max_square(picture_img)
77
  picture_img = picture_img.resize(PICTURE_SIZE)
78
 
79
-
80
- # カードの読み込み
81
  card_img = Image.open(card_frame_path_dict[option_dict['色']])
82
 
83
- # マークの追加
84
  mark_image = Image.open(card_mark_path_dict[option_dict['マーク']])
85
  card_img.paste(mark_image, mask=mark_image)
86
 
87
- # 写真の埋め込み
88
  card_img.paste(picture_img, PICTURE_LT_XY)
89
 
90
- # 各種書き込み準備
91
  card_imgdraw = ImageDraw.Draw(card_img)
92
 
93
- # カード枠線の追加
94
  card_imgdraw.line(( (0, 0), (card_img.size[0], 0) ), fill='black', width=15)
95
  card_imgdraw.line(( (0, 0), (0, card_img.size[1]) ), fill='black', width=15)
96
  card_imgdraw.line(( (card_img.size[0], 0), card_img.size), fill='black', width=15)
97
  card_imgdraw.line(( (0, card_img.size[1]), card_img.size), fill='black', width=15)
98
 
99
- # 説明欄区切り線の追加
100
  card_imgdraw.line((DESCRIPTION_LINE_L_XY, DESCRIPTION_LINE_R_XY), fill='black', width=3)
101
 
102
- # タイトル埋め込み
103
- # (複数行対応は必要ならば対応)
104
  title_font_size = 100
105
  while True:
106
  title_font = ImageFont.truetype(font_selif_path, title_font_size)
@@ -112,19 +112,19 @@ def create_historic_site_card_image(img_bytearray, option_dict):
112
 
113
  card_imgdraw.text((TITLE_LT_XY[0], int((TITLE_LT_XY[1] + TITLE_RB_XY[1]) / 2)), option_dict['タイトル'], fill='black', font=title_font, anchor='lm')
114
 
115
- # 史跡種類埋め込み
116
  hs_type_display = f'種類:{option_dict["史跡種類"]}'
117
  hs_typefont = ImageFont.truetype(font_sanselif_path, 40)
118
  card_imgdraw.text(HS_TYPE_LT_XY, hs_type_display, fill='black', font=hs_typefont, anchor='lt')
119
 
120
- # 訪問難度埋め込み
121
  difficulty = int(option_dict['訪問難度'])
122
  difficulty = 1 if difficulty < 1 else 5 if difficulty > 5 else difficulty
123
  difficulty_display = '訪問難度:' + '☆' * difficulty + '★' * (5 - difficulty)
124
  difficulty_font = ImageFont.truetype(font_sanselif_path, 40)
125
  card_imgdraw.text(DIFFICULTY_LT_XY, difficulty_display, fill='black', font=difficulty_font, anchor='lt')
126
 
127
- # 説明文埋め込み
128
  description_font = ImageFont.truetype(font_sanselif_path, 40)
129
 
130
  description_list = []
@@ -142,9 +142,9 @@ def create_historic_site_card_image(img_bytearray, option_dict):
142
 
143
  card_imgdraw.text(DESCRIPTION_LT_XY, description_display, fill='black', font=description_font)
144
 
145
- # バイナリデータの出力
146
  output_img_bytearray = BytesIO()
147
  card_img.convert('RGB').save(output_img_bytearray, "JPEG", quality=95)
148
- output_img_bytearray.seek(0) # 画像の先頭にシークしないと空データになってしまう。
149
 
150
  return output_img_bytearray
 
1
  from PIL import Image, ImageDraw, ImageFont
2
  from io import BytesIO
3
 
4
+ # Card image information
5
+
6
+ # Historic Site Card files
7
  card_frame_path_dict = {
8
  '橙': 'data/cards/史跡カードフレーム(橙).png',
9
  '白': 'data/cards/史跡カードフレーム(白).png',
 
27
  '都指定': 'data/cards/史跡カード指定マーク(都指定).png'
28
  }
29
 
30
+ # Pixel position information for each area of the card image
31
+ # Picture
32
  PICTURE_LT_XY = (65, 188)
33
  PICTURE_RB_XY = (802, 925)
34
  PICTURE_SIZE = (PICTURE_RB_XY[0] - PICTURE_LT_XY[0], PICTURE_RB_XY[1] - PICTURE_LT_XY[1])
35
 
36
+ # Title
37
+ # Create some margin
38
  TITLE_LT_XY = (65, 45)
39
+ TITLE_RB_XY = (647, 132) # Position to avoid overlapping with the marker insertion point
40
  TITLE_SIZE = (TITLE_RB_XY[0] - TITLE_LT_XY[0], TITLE_RB_XY[1] - TITLE_LT_XY[1])
41
 
42
+ # Explanation Section Divider
43
  DESCRIPTION_LINE_L_XY = (52, 1024)
44
  DESCRIPTION_LINE_R_XY = (816, 1024)
45
 
46
+ # Historic Site Type
47
  HS_TYPE_LT_XY = (56, 972)
48
 
49
+ # Difficulty of Visit
50
  DIFFICULTY_LT_XY = (444, 972)
51
 
52
+ # Main Body of Description
53
  DESCRIPTION_LT_XY = (46, 1024)
54
  DESCRIPTION_RB_XY = (810, 1174)
55
  DESCRIPTION_SIZE = (DESCRIPTION_RB_XY[0] - DESCRIPTION_LT_XY[0], DESCRIPTION_RB_XY[1] - DESCRIPTION_LT_XY[1])
56
 
57
+ # Font information
58
+ # Ming Typeface
 
59
  font_selif_path = 'data/fonts/SourceHanSerif-Bold.otf'
60
+ # Gothic Typeface
61
  font_sanselif_path = 'data/fonts/SourceHanSans-Bold.otf'
62
 
63
  def crop_center(pil_img, crop_width, crop_height):
 
72
 
73
  def create_historic_site_card_image(img_bytearray, option_dict):
74
 
75
+ # Loading and Cropping of Picture
76
  picture_img = Image.open(img_bytearray)
77
  picture_img = crop_max_square(picture_img)
78
  picture_img = picture_img.resize(PICTURE_SIZE)
79
 
80
+ # Loading of Card Frame
 
81
  card_img = Image.open(card_frame_path_dict[option_dict['色']])
82
 
83
+ # Adding a Marker
84
  mark_image = Image.open(card_mark_path_dict[option_dict['マーク']])
85
  card_img.paste(mark_image, mask=mark_image)
86
 
87
+ # Embedding a Picture in the Card
88
  card_img.paste(picture_img, PICTURE_LT_XY)
89
 
90
+ # Preparing for editting the card image
91
  card_imgdraw = ImageDraw.Draw(card_img)
92
 
93
+ # Adding a Border to the Card
94
  card_imgdraw.line(( (0, 0), (card_img.size[0], 0) ), fill='black', width=15)
95
  card_imgdraw.line(( (0, 0), (0, card_img.size[1]) ), fill='black', width=15)
96
  card_imgdraw.line(( (card_img.size[0], 0), card_img.size), fill='black', width=15)
97
  card_imgdraw.line(( (0, card_img.size[1]), card_img.size), fill='black', width=15)
98
 
99
+ # Adding a Divider Line to the Explanation Section
100
  card_imgdraw.line((DESCRIPTION_LINE_L_XY, DESCRIPTION_LINE_R_XY), fill='black', width=3)
101
 
102
+ # Embedding the Title
103
+ # (Support for multiple lines if needed)
104
  title_font_size = 100
105
  while True:
106
  title_font = ImageFont.truetype(font_selif_path, title_font_size)
 
112
 
113
  card_imgdraw.text((TITLE_LT_XY[0], int((TITLE_LT_XY[1] + TITLE_RB_XY[1]) / 2)), option_dict['タイトル'], fill='black', font=title_font, anchor='lm')
114
 
115
+ # Embedding the Historic Site Type
116
  hs_type_display = f'種類:{option_dict["史跡種類"]}'
117
  hs_typefont = ImageFont.truetype(font_sanselif_path, 40)
118
  card_imgdraw.text(HS_TYPE_LT_XY, hs_type_display, fill='black', font=hs_typefont, anchor='lt')
119
 
120
+ # Embedding the Difficulty of Visit
121
  difficulty = int(option_dict['訪問難度'])
122
  difficulty = 1 if difficulty < 1 else 5 if difficulty > 5 else difficulty
123
  difficulty_display = '訪問難度:' + '☆' * difficulty + '★' * (5 - difficulty)
124
  difficulty_font = ImageFont.truetype(font_sanselif_path, 40)
125
  card_imgdraw.text(DIFFICULTY_LT_XY, difficulty_display, fill='black', font=difficulty_font, anchor='lt')
126
 
127
+ # Embedding the Description Text
128
  description_font = ImageFont.truetype(font_sanselif_path, 40)
129
 
130
  description_list = []
 
142
 
143
  card_imgdraw.text(DESCRIPTION_LT_XY, description_display, fill='black', font=description_font)
144
 
145
+ # Outputting Binary Data
146
  output_img_bytearray = BytesIO()
147
  card_img.convert('RGB').save(output_img_bytearray, "JPEG", quality=95)
148
+ output_img_bytearray.seek(0) # Seek to the beginning of the image, otherwise it results in empty data
149
 
150
  return output_img_bytearray
src/picture_box_model.py CHANGED
@@ -1,7 +1,7 @@
1
  import io
2
  import numpy as np
3
 
4
- from PIL import Image as PIL_Image # gltflibのImageと被るので別名にする。
5
  import struct
6
  import uuid
7
 
@@ -9,8 +9,8 @@ from gltflib import (
9
  GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
10
  BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness)
11
 
12
- # 共通バイナリ情報
13
- # 頂点データ(POSITION)
14
  vertices = [
15
  [ 1.0, 1.0, -1.0,],
16
  [ 1.0, 1.0, -1.0,],
@@ -45,7 +45,7 @@ vertex_bytelen = len(vertex_bytearray)
45
  mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
46
  maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
47
 
48
- # 法線データ(NORMAL)
49
  normals = [
50
  [ 0.0, 0.0, -1.0,],
51
  [ 0.0, 1.0, -0.0,],
@@ -78,7 +78,7 @@ for normal in normals:
78
  normal_bytearray.extend(struct.pack('f', value))
79
  normal_bytelen = len(normal_bytearray)
80
 
81
- # テクスチャ座標(TEXCOORD_0)
82
  texcoord_0s = [
83
  [0.9, 0.1],[0.9, 0.0],[1.0, 0.1],
84
  [0.9, 1.0],[0.9, 0.9],[1.0, 0.9],
@@ -95,7 +95,7 @@ for texcoord_0 in texcoord_0s:
95
  texcoord_0_bytearray.extend(struct.pack('f', value))
96
  texcoord_0_bytelen = len(texcoord_0_bytearray)
97
 
98
- # 頂点インデックス
99
  vertex_indices = [
100
  1, 14, 20,
101
  1, 20, 7,
@@ -118,10 +118,10 @@ vertex_index_bytelen = len(vertex_index_bytearray)
118
 
119
  def create_picture_box_model(img_bytearray):
120
 
121
- # 画像の取得
122
  img = PIL_Image.open(img_bytearray).convert('RGB')
123
 
124
- # 辺の長さが8で割れる数値になるように調整
125
  img = img.resize((8 * (img.size[0] // 8), 8 * (img.size[1] // 8)))
126
  temp_img = PIL_Image.new('RGB', (int(img.size[0] * 1.25), int(img.size[1] * 1.25)), 'white')
127
  temp_img.paste(img, (int(temp_img.size[0] / 10), int(temp_img.size[1] / 10)))
@@ -142,11 +142,11 @@ def create_picture_box_model(img_bytearray):
142
  img_bytearray = img_bytearray.getvalue()
143
  img_bytelen = len(img_bytearray)
144
 
145
- # 3Dモデルのスケールの計算
146
  scale_factor = np.power(img.size[0] * img.size[1], 0.5)
147
  scale = (img.size[0] / scale_factor, img.size[1] / scale_factor, 0.1)
148
 
149
- # バイナリデータ部分の結合
150
  bytearray_list = [
151
  vertex_bytearray,
152
  normal_bytearray,
@@ -167,20 +167,19 @@ def create_picture_box_model(img_bytearray):
167
  all_bytearray = bytearray()
168
  for temp_bytearray in bytearray_list:
169
  all_bytearray.extend(temp_bytearray)
170
- offset_list = [0] + bytelen_cumsum_list # 最初のオフセットは0
171
- offset_list.pop() # 末尾を削除
172
 
173
- # リソースの作成
174
  resources = [GLBResource(data=all_bytearray)]
175
 
176
- # 各種設定
177
- # アセット
178
  asset=Asset()
179
 
180
- # バッファ
181
  buffers = [Buffer(byteLength=len(all_bytearray))]
182
 
183
- # バッファビュー
184
  bufferViews = [
185
  BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
186
  BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
@@ -189,7 +188,7 @@ def create_picture_box_model(img_bytearray):
189
  BufferView(buffer=0, byteOffset=offset_list[4], byteLength=bytelen_list[4], target=None),
190
  ]
191
 
192
- # アクセサー
193
  accessors = [
194
  Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins),
195
  Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None),
@@ -197,20 +196,20 @@ def create_picture_box_model(img_bytearray):
197
  Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None)
198
  ]
199
 
200
- # イメージ
201
  images=[
202
  Image(mimeType='image/jpeg', bufferView=4),
203
  ]
204
 
205
- # サンプラー
206
  samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング
207
 
208
- # テクスチャ
209
  textures = [
210
  Texture(name='Image',sampler=0,source=0),
211
  ]
212
 
213
- # マテリアル
214
  materials = [
215
  Material(
216
  pbrMetallicRoughness=PBRMetallicRoughness(
@@ -224,17 +223,17 @@ def create_picture_box_model(img_bytearray):
224
  ),
225
  ]
226
 
227
- # メッシュ
228
  meshes = [
229
  Mesh(name='Image', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2),indices=3, material=0)]),
230
  ]
231
 
232
- # ノード
233
  nodes = [
234
  Node(mesh=0,rotation=None, scale=scale),
235
  ]
236
 
237
- # シーン
238
  scene = 0
239
  scenes = [Scene(name='Scene', nodes=[0])]
240
 
 
1
  import io
2
  import numpy as np
3
 
4
+ from PIL import Image as PIL_Image # Renaming to avoid conflict with Image from gltflib
5
  import struct
6
  import uuid
7
 
 
9
  GLTF, GLTFModel, Asset, Scene, Node, Mesh, Primitive, Attributes, Buffer, BufferView, Image, Texture, TextureInfo, Material, Sampler, Accessor, AccessorType,
10
  BufferTarget, ComponentType, GLBResource, PBRMetallicRoughness)
11
 
12
+ # Common Binary Information
13
+ # Vertex data(POSITION)
14
  vertices = [
15
  [ 1.0, 1.0, -1.0,],
16
  [ 1.0, 1.0, -1.0,],
 
45
  mins = [min([vertex[i] for vertex in vertices]) for i in range(3)]
46
  maxs = [max([vertex[i] for vertex in vertices]) for i in range(3)]
47
 
48
+ # Normal data(NORMAL)
49
  normals = [
50
  [ 0.0, 0.0, -1.0,],
51
  [ 0.0, 1.0, -0.0,],
 
78
  normal_bytearray.extend(struct.pack('f', value))
79
  normal_bytelen = len(normal_bytearray)
80
 
81
+ # Texture coordinates(TEXCOORD_0)
82
  texcoord_0s = [
83
  [0.9, 0.1],[0.9, 0.0],[1.0, 0.1],
84
  [0.9, 1.0],[0.9, 0.9],[1.0, 0.9],
 
95
  texcoord_0_bytearray.extend(struct.pack('f', value))
96
  texcoord_0_bytelen = len(texcoord_0_bytearray)
97
 
98
+ # Vertex indices
99
  vertex_indices = [
100
  1, 14, 20,
101
  1, 20, 7,
 
118
 
119
  def create_picture_box_model(img_bytearray):
120
 
121
+ # Loading Picture
122
  img = PIL_Image.open(img_bytearray).convert('RGB')
123
 
124
+ # Adjusting to a Value That Is Divisible by 8 for Edge Length
125
  img = img.resize((8 * (img.size[0] // 8), 8 * (img.size[1] // 8)))
126
  temp_img = PIL_Image.new('RGB', (int(img.size[0] * 1.25), int(img.size[1] * 1.25)), 'white')
127
  temp_img.paste(img, (int(temp_img.size[0] / 10), int(temp_img.size[1] / 10)))
 
142
  img_bytearray = img_bytearray.getvalue()
143
  img_bytelen = len(img_bytearray)
144
 
145
+ # Calculating the Scale of a 3D Model
146
  scale_factor = np.power(img.size[0] * img.size[1], 0.5)
147
  scale = (img.size[0] / scale_factor, img.size[1] / scale_factor, 0.1)
148
 
149
+ # Concatenation of the binary data section
150
  bytearray_list = [
151
  vertex_bytearray,
152
  normal_bytearray,
 
167
  all_bytearray = bytearray()
168
  for temp_bytearray in bytearray_list:
169
  all_bytearray.extend(temp_bytearray)
170
+ offset_list = [0] + bytelen_cumsum_list # The first offset is 0
171
+ offset_list.pop() # Remove the end
172
 
173
+ # GLBResource
174
  resources = [GLBResource(data=all_bytearray)]
175
 
176
+ # Asset
 
177
  asset=Asset()
178
 
179
+ # Buffer
180
  buffers = [Buffer(byteLength=len(all_bytearray))]
181
 
182
+ # BufferView
183
  bufferViews = [
184
  BufferView(buffer=0, byteOffset=offset_list[0], byteLength=bytelen_list[0], target=BufferTarget.ARRAY_BUFFER.value),
185
  BufferView(buffer=0, byteOffset=offset_list[1], byteLength=bytelen_list[1], target=BufferTarget.ARRAY_BUFFER.value),
 
188
  BufferView(buffer=0, byteOffset=offset_list[4], byteLength=bytelen_list[4], target=None),
189
  ]
190
 
191
+ # Accessor
192
  accessors = [
193
  Accessor(bufferView=0, componentType=ComponentType.FLOAT.value, count=len(vertices), type=AccessorType.VEC3.value, max=maxs, min=mins),
194
  Accessor(bufferView=1, componentType=ComponentType.FLOAT.value, count=len(normals), type=AccessorType.VEC3.value, max=None, min=None),
 
196
  Accessor(bufferView=3, componentType=ComponentType.UNSIGNED_SHORT.value, count=len(vertex_indices), type=AccessorType.SCALAR.value, max=None, min=None)
197
  ]
198
 
199
+ # Image
200
  images=[
201
  Image(mimeType='image/jpeg', bufferView=4),
202
  ]
203
 
204
+ # Sampler
205
  samplers = [Sampler(magFilter=9728, minFilter=9984)] # magFilter:最近傍フィルタリング、minFilter:ミップマップ+最近傍フィルタリング
206
 
207
+ # Texture
208
  textures = [
209
  Texture(name='Image',sampler=0,source=0),
210
  ]
211
 
212
+ # Material
213
  materials = [
214
  Material(
215
  pbrMetallicRoughness=PBRMetallicRoughness(
 
223
  ),
224
  ]
225
 
226
+ # Mesh
227
  meshes = [
228
  Mesh(name='Image', primitives=[Primitive(attributes=Attributes(POSITION=0, NORMAL=1,TEXCOORD_0=2),indices=3, material=0)]),
229
  ]
230
 
231
+ # Node
232
  nodes = [
233
  Node(mesh=0,rotation=None, scale=scale),
234
  ]
235
 
236
+ # Scene
237
  scene = 0
238
  scenes = [Scene(name='Scene', nodes=[0])]
239