Skip to content

Commit c12723d

Browse files
Merge pull request #27 from Geode-solutions/feat/screenshot
feat(screenshot): new rpc
2 parents 8ef5d85 + becbd80 commit c12723d

File tree

8 files changed

+180
-16
lines changed

8 files changed

+180
-16
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"rpc": "take_screenshot",
3+
"type": "object",
4+
"properties": {
5+
"filename": {
6+
"type": "string"
7+
},
8+
"output_extension": {
9+
"type": "string",
10+
"enum": [
11+
"png",
12+
"jpg"
13+
]
14+
},
15+
"include_background": {
16+
"type": "boolean"
17+
}
18+
},
19+
"required": [
20+
"filename",
21+
"output_extension",
22+
"include_background"
23+
],
24+
"additionalProperties": false
25+
}

src/opengeodeweb_viewer/vtk_protocol.py

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1+
# Standard library imports
12
import json
23
import os
4+
5+
# Third party imports
6+
import vtk
37
from vtk.web import protocols as vtk_protocols
8+
from vtkmodules.vtkIOImage import vtkPNGWriter, vtkJPEGWriter
9+
from vtkmodules.vtkRenderingCore import (vtkWindowToImageFilter)
410
from wslink import register as exportRpc
5-
import vtk
11+
12+
# Local application imports
613
from .function import validate_schemas
714

15+
16+
817
schemas = os.path.join(os.path.dirname(__file__), "rpc/schemas")
918

1019
with open(os.path.join(schemas, "create_visualization.json"), "r") as file:
@@ -35,6 +44,8 @@
3544
set_color_json = json.load(file)
3645
with open(os.path.join(schemas, "set_vertex_attribute.json"), "r") as file:
3746
set_vertex_attribute_json = json.load(file)
47+
with open(os.path.join(schemas, "take_screenshot.json"), "r") as file:
48+
take_screenshot_json = json.load(file)
3849

3950

4051
class VtkView(vtk_protocols.vtkWebProtocol):
@@ -271,6 +282,51 @@ def setVertexAttribute(self, params):
271282
mapper.SetScalarModeToUsePointFieldData()
272283
self.render()
273284

285+
@exportRpc(take_screenshot_json["rpc"])
286+
def takeScreenshot(self, params):
287+
validate_schemas(params, take_screenshot_json)
288+
print(f"{params=}", flush=True)
289+
filename = params["filename"]
290+
output_extension = params["output_extension"]
291+
include_background = params["include_background"]
292+
renderWindow = self.getView("-1")
293+
renderer = self.get_renderer()
294+
295+
w2if = vtkWindowToImageFilter()
296+
297+
if not include_background:
298+
renderWindow.SetAlphaBitPlanes(1)
299+
w2if.SetInputBufferTypeToRGBA()
300+
else:
301+
renderWindow.SetAlphaBitPlanes(0)
302+
w2if.SetInputBufferTypeToRGB()
303+
304+
renderWindow.Render()
305+
306+
w2if.SetInput(renderWindow)
307+
w2if.ReadFrontBufferOff()
308+
w2if.Update()
309+
310+
if output_extension == "png":
311+
writer = vtkPNGWriter()
312+
elif output_extension in ["jpg", "jpeg"]:
313+
if not include_background:
314+
raise Exception("output_extension not supported with background")
315+
writer = vtkJPEGWriter()
316+
else:
317+
raise Exception("output_extension not supported")
318+
319+
new_filename = filename + '.' + output_extension
320+
file_path = os.path.join(self.DATA_FOLDER_PATH, new_filename)
321+
writer.SetFileName(file_path)
322+
writer.SetInputConnection(w2if.GetOutputPort())
323+
writer.Write()
324+
325+
with open(file_path, "rb") as file:
326+
file_content = file.read()
327+
328+
return {"blob": self.addAttachment(file_content)}
329+
274330
def get_data_base(self):
275331
return self.getSharedObject("db")
276332

src/tests/conftest.py

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ def __init__(self, log):
3030
self.call("viewport.image.push.observer.add", [-1])
3131
for i in range(5):
3232
print(f"{i=}", flush=True)
33-
reponse = self.ws.recv()
34-
print(f"{reponse=}", flush=True)
33+
response = self.ws.recv()
34+
print(f"{response=}", flush=True)
3535

3636
def call(self, rpc, params=[{}]):
3737
print(f"{rpc=} {params=}", flush=True)
@@ -57,8 +57,32 @@ def print_log(self):
5757
print(output)
5858

5959
def get_response(self):
60-
response = eval(self.ws.recv())
61-
return response
60+
response = self.ws.recv()
61+
if isinstance(response, bytes):
62+
return response
63+
else:
64+
return eval(response)
65+
66+
def images_diff(self, first_image_path, second_image_path):
67+
if ".png" in first_image_path:
68+
first_reader = vtk.vtkPNGReader()
69+
elif (".jpg" in first_image_path) or (".jpeg" in first_image_path):
70+
first_reader = vtk.vtkJPEGReader()
71+
first_reader.SetFileName(first_image_path)
72+
73+
if ".png" in second_image_path:
74+
second_reader = vtk.vtkPNGReader()
75+
elif (".jpg" in second_image_path) or (".jpeg" in second_image_path):
76+
second_reader = vtk.vtkJPEGReader()
77+
second_reader.SetFileName(second_image_path)
78+
79+
images_diff = vtk.vtkImageDifference()
80+
images_diff.SetInputConnection(first_reader.GetOutputPort())
81+
images_diff.SetImageConnection(second_reader.GetOutputPort())
82+
images_diff.Update()
83+
84+
print(f"{images_diff.GetThresholdedError()=}")
85+
return images_diff.GetThresholdedError()
6286

6387
def compare_image(self, nb_messages, filename):
6488
for message in range(nb_messages):
@@ -83,20 +107,10 @@ def compare_image(self, nb_messages, filename):
83107
f.write(image)
84108
f.close()
85109

86-
test_reader = vtk.vtkJPEGReader()
87-
test_reader.SetFileName(test_file_path)
88-
89110
path_image = os.path.join(self.images_dir_path, filename)
90-
answer_reader = vtk.vtkJPEGReader()
91-
answer_reader.SetFileName(path_image)
92111

93-
images_diff = vtk.vtkImageDifference()
94-
images_diff.SetInputConnection(test_reader.GetOutputPort())
95-
images_diff.SetImageConnection(answer_reader.GetOutputPort())
96-
images_diff.Update()
112+
return self.images_diff(test_file_path, path_image)==0.0
97113

98-
print(f"{images_diff.GetThresholdedError()=}")
99-
return images_diff.GetThresholdedError() == 0.0
100114

101115

102116
class FixtureHelper:
Loading
Loading
Loading

src/tests/test_protocol.py

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,3 +103,72 @@ def test_set_color(server):
103103

104104
server.call("set_color", [{"id": "123456789", "red": 50, "green": 2, "blue": 250}])
105105
assert server.compare_image(3, "set_color.jpeg") == True
106+
107+
108+
109+
def test_take_screenshot(server):
110+
# Create an object
111+
server.call(
112+
"create_object_pipeline",
113+
[{"id": "123456789", "file_name": "hat.vtp"}],
114+
)
115+
assert server.compare_image(3, "create_object_pipeline.jpeg") == True
116+
117+
118+
# Take a screenshot with background jpg
119+
server.call(
120+
"take_screenshot",
121+
[{"filename": "take_screenshot_with_background", "output_extension": "jpg", "include_background": True}],
122+
)
123+
124+
response = server.get_response()
125+
blob = server.get_response()
126+
assert type(blob) is bytes
127+
128+
with open(os.path.join(server.test_output_dir, "test.jpg"), "wb") as f:
129+
f.write(blob)
130+
f.close()
131+
first_image_path = os.path.join(server.test_output_dir, "test.jpg")
132+
second_image_path = os.path.join(server.images_dir_path, "take_screenshot_with_background.jpg")
133+
134+
assert server.images_diff(first_image_path, second_image_path) == 0.0
135+
136+
# Take a screenshot without background png
137+
server.call(
138+
"take_screenshot",
139+
[{"filename": "take_screenshot_without_background", "output_extension": "png", "include_background": True}],
140+
)
141+
142+
response = server.get_response()
143+
response = server.get_response()
144+
blob = server.get_response()
145+
print(f"{blob=}", flush=True)
146+
assert type(blob) is bytes
147+
148+
with open(os.path.join(server.test_output_dir, "test.png"), "wb") as f:
149+
f.write(blob)
150+
f.close()
151+
first_image_path = os.path.join(server.test_output_dir, "test.png")
152+
second_image_path = os.path.join(server.images_dir_path, "take_screenshot_without_background.png")
153+
154+
assert server.images_diff(first_image_path, second_image_path) == 0.0
155+
156+
# Take a screenshot with background png
157+
server.call(
158+
"take_screenshot",
159+
[{"filename": "take_screenshot_with_background", "output_extension": "png", "include_background": True}],
160+
)
161+
162+
response = server.get_response()
163+
response = server.get_response()
164+
blob = server.get_response()
165+
print(f"{blob=}", flush=True)
166+
assert type(blob) is bytes
167+
168+
with open(os.path.join(server.test_output_dir, "test.png"), "wb") as f:
169+
f.write(blob)
170+
f.close()
171+
first_image_path = os.path.join(server.test_output_dir, "test.png")
172+
second_image_path = os.path.join(server.images_dir_path, "take_screenshot_with_background.png")
173+
174+
assert server.images_diff(first_image_path, second_image_path) == 0.0

src/tests/tests_output/test.jpeg

-13 Bytes
Loading

0 commit comments

Comments
 (0)