From cfaa1e5db1952f54c5fa3db1ec00282bba5caa4a Mon Sep 17 00:00:00 2001 From: Martin Poirier Date: Thu, 7 Jan 2010 18:54:47 +0000 Subject: [PATCH] netrender jpeg versions of the render results are downloadable from the web interface. Notes: 1) They are generated (and written to disk) on demand on the master in the same Blender instance, so this will increase memory usage (until there's a way to free and image buffer from memory or it's run in a separate process). 2) They are darker then the real results, since multilayer exr contain the result before gamma correction (for the linear workflow) and that's not applied when loading them as an image. 3) They are NOT thumbnails, they are the same size as the results (albeit at 90% quality jpeg compression) --- release/scripts/io/netrender/balancing.py | 2 +- release/scripts/io/netrender/master.py | 24 ++++++++++++++++++--- release/scripts/io/netrender/master_html.py | 15 ++++++++++--- release/scripts/io/netrender/utils.py | 4 ++-- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/release/scripts/io/netrender/balancing.py b/release/scripts/io/netrender/balancing.py index 0a654394103..344bacde90a 100644 --- a/release/scripts/io/netrender/balancing.py +++ b/release/scripts/io/netrender/balancing.py @@ -161,7 +161,7 @@ class MinimumTimeBetweenDispatchPriority(PriorityRule): class ExcludeQueuedEmptyJob(ExclusionRule): def __str__(self): - return "Exclude non queued and empty jobs" + return "Exclude non queued or empty jobs" def test(self, job): return job.status != JOB_QUEUED or job.countFrames(status = QUEUED) == 0 diff --git a/release/scripts/io/netrender/master.py b/release/scripts/io/netrender/master.py index 4b21221d3af..fdc4b5fec31 100644 --- a/release/scripts/io/netrender/master.py +++ b/release/scripts/io/netrender/master.py @@ -174,7 +174,7 @@ class MRenderFrame(netrender.model.RenderFrame): # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- file_pattern = re.compile("/file_([a-zA-Z0-9]+)_([0-9]+)") -render_pattern = re.compile("/render_([a-zA-Z0-9]+)_([0-9]+).exr") +render_pattern = re.compile("/render_([a-zA-Z0-9]+)_([0-9]+).(exr|jpg)") log_pattern = re.compile("/log_([a-zA-Z0-9]+)_([0-9]+).log") reset_pattern = re.compile("/reset(all|)_([a-zA-Z0-9]+)_([0-9]+)") cancel_pattern = re.compile("/cancel_([a-zA-Z0-9]+)") @@ -230,6 +230,8 @@ class RenderHandler(http.server.BaseHTTPRequestHandler): if match: job_id = match.groups()[0] frame_number = int(match.groups()[1]) + + exr = match.groups()[2] == "exr" job = self.server.getJobID(job_id) @@ -241,9 +243,25 @@ class RenderHandler(http.server.BaseHTTPRequestHandler): self.send_head(http.client.ACCEPTED) elif frame.status == DONE: self.server.stats("", "Sending result to client") - f = open(job.save_path + "%04d" % frame_number + ".exr", 'rb') + + if exr: + f = open(job.save_path + "%04d" % frame_number + ".exr", 'rb') + self.send_head(content = "image/x-exr") + else: + filename = job.save_path + "%04d" % frame_number + ".jpg" + + if not os.path.exists(filename): + import bpy + sce = bpy.data.scenes[0] + sce.render_data.file_format = "JPEG" + sce.render_data.quality = 90 + bpy.ops.image.open(path = job.save_path + "%04d" % frame_number + ".exr") + img = bpy.data.images["%04d" % frame_number + ".exr"] + img.save(filename) + + f = open(filename, 'rb') + self.send_head(content = "image/jpeg") - self.send_head() shutil.copyfileobj(f, self.wfile) diff --git a/release/scripts/io/netrender/master_html.py b/release/scripts/io/netrender/master_html.py index c76e57991d9..f55712caa0f 100644 --- a/release/scripts/io/netrender/master_html.py +++ b/release/scripts/io/netrender/master_html.py @@ -166,7 +166,7 @@ def get(handler): "done", "dispatched", "error", - "first", + "priority", "exception" ) @@ -195,7 +195,8 @@ def get(handler): results[DISPATCHED], str(results[ERROR]) + """""" % (job.id, "disabled=True" if not results[ERROR] else ""), - handler.server.balancer.applyPriorities(job), handler.server.balancer.applyExceptions(job) + "yes" if handler.server.balancer.applyPriorities(job) else "no", + "yes" if handler.server.balancer.applyExceptions(job) else "no" ) endTable() @@ -261,7 +262,15 @@ def get(handler): headerTable("no", "status", "render time", "slave", "log", "result") for frame in job.frames: - rowTable(frame.number, frame.statusText(), "%.1fs" % frame.time, frame.slave.name if frame.slave else " ", link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", link("view result", renderURL(job_id, frame.number)) if frame.status == DONE else " ") + rowTable( + frame.number, + frame.statusText(), + "%.1fs" % frame.time, + frame.slave.name if frame.slave else " ", + link("view log", logURL(job_id, frame.number)) if frame.log_path else " ", + link("view result", renderURL(job_id, frame.number)) + " [" + + link("jpeg", renderURL(job_id, frame.number, exr = False)) + "]" if frame.status == DONE else " " + ) endTable() else: diff --git a/release/scripts/io/netrender/utils.py b/release/scripts/io/netrender/utils.py index 7e644c83e5f..e9103db8da7 100644 --- a/release/scripts/io/netrender/utils.py +++ b/release/scripts/io/netrender/utils.py @@ -148,8 +148,8 @@ def fileURL(job_id, file_index): def logURL(job_id, frame_number): return "/log_%s_%i.log" % (job_id, frame_number) -def renderURL(job_id, frame_number): - return "/render_%s_%i.exr" % (job_id, frame_number) +def renderURL(job_id, frame_number, exr = True): + return "/render_%s_%i.%s" % (job_id, frame_number, "exr" if exr else "jpg") def cancelURL(job_id): return "/cancel_%s" % (job_id)