Spaces:
Running
Running
ikeda
commited on
Commit
·
6810bed
1
Parent(s):
bc6513e
translate Japanese comments to English
Browse files- app.py +8 -9
- src/card_model.py +37 -37
- src/extracted_objects_model.py +40 -40
- src/front_card_image.py +18 -18
- src/front_card_image_historic_site.py +29 -29
- src/picture_box_model.py +24 -25
app.py
CHANGED
@@ -1,5 +1,5 @@
|
|
1 |
import gradio as gr
|
2 |
-
from PIL import Image as PIL_Image # gltflib
|
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 |
-
#
|
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
|
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
|
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 |
-
#
|
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 |
-
#
|
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 |
-
#
|
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 #
|
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
|
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
|
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 |
-
#
|
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 |
-
#
|
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 |
-
#
|
59 |
-
#
|
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())) #
|
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())) #
|
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 |
-
#
|
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 |
-
#
|
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 |
-
#
|
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 #
|
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
|
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 |
-
#
|
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 |
-
#
|
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 |
-
#
|
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 |
-
#
|
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 #
|
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 |
|