netrender: refactor thumbnail generation code. New option to generate the thumbnail on the slaves (per slave option, default off). Missing thumbnails are still generated on demand by the master.

This commit is contained in:
2010-01-09 18:21:27 +00:00
parent e3641796c2
commit 670d55f097
5 changed files with 131 additions and 35 deletions

View File

@@ -174,7 +174,8 @@ 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|jpg)")
render_pattern = re.compile("/render_([a-zA-Z0-9]+)_([0-9]+).exr")
thumb_pattern = re.compile("/thumb_([a-zA-Z0-9]+)_([0-9]+).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]+)")
@@ -231,8 +232,6 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
job_id = match.groups()[0]
frame_number = int(match.groups()[1])
exr = match.groups()[2] == "exr"
job = self.server.getJobID(job_id)
if job:
@@ -244,33 +243,11 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
elif frame.status == DONE:
self.server.stats("", "Sending result to client")
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, scene = sce)
try:
process = subprocess.Popen(["convert", filename, "-resize", "300x300", filename])
process.wait()
except:
pass
f = open(filename, 'rb')
self.send_head(content = "image/jpeg")
filename = job.save_path + "%04d" % frame_number + ".exr"
f = open(filename, 'rb')
self.send_head(content = "image/x-exr")
shutil.copyfileobj(f, self.wfile)
f.close()
elif frame.status == ERROR:
self.send_head(http.client.PARTIAL_CONTENT)
@@ -284,6 +261,46 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
# invalid url
self.send_head(http.client.NO_CONTENT)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path.startswith("/thumb"):
match = thumb_pattern.match(self.path)
if match:
job_id = match.groups()[0]
frame_number = int(match.groups()[1])
job = self.server.getJobID(job_id)
if job:
frame = job[frame_number]
if frame:
if frame.status in (QUEUED, DISPATCHED):
self.send_head(http.client.ACCEPTED)
elif frame.status == DONE:
filename = job.save_path + "%04d" % frame_number + ".exr"
thumbname = thumbnail(filename)
if thumbname:
f = open(thumbname, 'rb')
self.send_head(content = "image/jpeg")
shutil.copyfileobj(f, self.wfile)
f.close()
else: # thumbnail couldn't be generated
self.send_head(http.client.PARTIAL_CONTENT)
return
elif frame.status == ERROR:
self.send_head(http.client.PARTIAL_CONTENT)
else:
# no such frame
self.send_head(http.client.NO_CONTENT)
else:
# no such job id
self.send_head(http.client.NO_CONTENT)
else:
# invalid url
self.send_head(http.client.NO_CONTENT)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path.startswith("/log"):
match = log_pattern.match(self.path)
@@ -749,8 +766,6 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
if not slave.id in job.blacklist:
job.blacklist.append(slave.id)
self.server.stats("", "Receiving result")
slave.finishedFrame(job_frame)
frame.status = job_result
@@ -766,6 +781,44 @@ class RenderHandler(http.server.BaseHTTPRequestHandler):
else: # invalid slave id
self.send_head(http.client.NO_CONTENT)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path == "/thumb":
self.server.stats("", "Receiving thumbnail result")
# need some message content here or the slave doesn't like it
self.wfile.write(bytes("foo", encoding='utf8'))
slave_id = self.headers['slave-id']
slave = self.server.getSeenSlave(slave_id)
if slave: # only if slave id is valid
job_id = self.headers['job-id']
job = self.server.getJobID(job_id)
if job:
job_frame = int(self.headers['job-frame'])
frame = job[job_frame]
if frame:
if job.type == netrender.model.JOB_BLENDER:
length = int(self.headers['content-length'])
buf = self.rfile.read(length)
f = open(job.save_path + "%04d" % job_frame + ".jpg", 'wb')
f.write(buf)
f.close()
del buf
self.send_head()
else: # frame not found
self.send_head(http.client.NO_CONTENT)
else: # job not found
self.send_head(http.client.NO_CONTENT)
else: # invalid slave id
self.send_head(http.client.NO_CONTENT)
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
elif self.path.startswith("/log"):
self.server.stats("", "Receiving log file")

View File

@@ -101,7 +101,7 @@ function showThumb(job, frame)
function toggleThumb(job, frame)
{
img = document.images["thumb" + frame];
url = "/render_" + job + "_" + frame + ".jpg"
url = "/thumb_" + job + "_" + frame + ".jpg"
if (img.style.display == "block") {
img.style.display = "none";

View File

@@ -213,14 +213,26 @@ def render_slave(engine, netsettings, threads):
headers["job-result"] = str(DONE)
for frame in job.frames:
headers["job-frame"] = str(frame.number)
if job.type == netrender.model.JOB_BLENDER:
# send image back to server
f = open(JOB_PREFIX + "%06d" % frame.number + ".exr", 'rb')
filename = JOB_PREFIX + "%06d" % frame.number + ".exr"
# thumbnail first
if netsettings.slave_thumb:
thumbname = thumbnail(filename)
f = open(thumbname, 'rb')
conn.request("PUT", "/thumb", f, headers=headers)
f.close()
conn.getresponse()
f = open(filename, 'rb')
conn.request("PUT", "/render", f, headers=headers)
f.close()
if conn.getresponse().status == http.client.NO_CONTENT:
continue
elif job.type == netrender.model.JOB_PROCESS:
conn.request("PUT", "/render", headers=headers)
if conn.getresponse().status == http.client.NO_CONTENT:

View File

@@ -140,6 +140,7 @@ class RENDER_PT_network_slave_settings(RenderButtonsPanel):
netsettings = scene.network_render
layout.prop(netsettings, "slave_clear")
layout.prop(netsettings, "slave_thumb")
layout.label(text="Threads:")
layout.prop(rd, "threads_mode", expand=True)
sub = layout.column()
@@ -356,6 +357,11 @@ NetRenderSettings.BoolProperty( attr="slave_clear",
description="delete downloaded files on exit",
default = True)
NetRenderSettings.BoolProperty( attr="slave_thumb",
name="Generate thumbnails",
description="Generate thumbnails on slaves instead of master",
default = False)
NetRenderSettings.BoolProperty( attr="master_clear",
name="Clear on exit",
description="delete saved files on exit",

View File

@@ -28,7 +28,7 @@ try:
except:
bpy = None
VERSION = bytes("0.7", encoding='utf8')
VERSION = bytes("0.8", encoding='utf8')
# Jobs status
JOB_WAITING = 0 # before all data has been entered
@@ -172,3 +172,28 @@ def prefixPath(prefix_directory, file_path, prefix_path):
full_path = prefix_directory + file_path
return full_path
def thumbnail(filename):
root = os.path.splitext(filename)[0]
imagename = os.path.split(filename)[1]
thumbname = root + ".jpg"
if os.path.exists(thumbname):
return thumbname
if bpy:
sce = bpy.data.scenes[0]
sce.render_data.file_format = "JPEG"
sce.render_data.quality = 90
bpy.ops.image.open(path = filename)
img = bpy.data.images[imagename]
img.save(thumbname, scene = sce)
try:
process = subprocess.Popen(["convert", thumbname, "-resize", "300x300", thumbname])
process.wait()
return thumbname
except:
pass
return None