Added initial stl processor

This commit is contained in:
Rodney Ewing 2013-08-14 10:38:13 -07:00
parent ab64ca3474
commit 77daec9224

View File

@ -14,6 +14,7 @@
# You should have received a copy of the GNU Affero General Public License # You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
import os import os
import json import json
import logging import logging
@ -21,8 +22,11 @@ import subprocess
import pkg_resources import pkg_resources
from mediagoblin import mg_globals as mgg from mediagoblin import mg_globals as mgg
from mediagoblin.processing import create_pub_filepath, \ from mediagoblin.processing import (
FilenameBuilder FilenameBuilder, MediaProcessor,
ProcessingManager, request_from_args,
get_orig_filename, store_public,
copy_original)
from mediagoblin.media_types.stl import model_loader from mediagoblin.media_types.stl import model_loader
@ -75,49 +79,60 @@ def blender_render(config):
env=env) env=env)
def process_stl(proc_state): class CommonStlProcessor(MediaProcessor):
"""Code to process an stl or obj model. Will be run by celery. """
Provides a common base for various stl processing steps
A Workbench() represents a local tempory dir. It is automatically
cleaned up when this function exits.
""" """
entry = proc_state.entry
workbench = proc_state.workbench
queued_filepath = entry.queued_media_file def common_setup(self):
queued_filename = workbench.localized_file( # Pull down and set up the original file
mgg.queue_store, queued_filepath, 'source') self.orig_filename = get_orig_filename(
name_builder = FilenameBuilder(queued_filename) self.entry, self.workbench)
self.name_builder = FilenameBuilder(self.orig_filename)
ext = queued_filename.lower().strip()[-4:] self._set_ext()
if ext.startswith("."): self._set_model()
ext = ext[1:] self._set_greatest()
else:
def _set_ext(self):
ext = self.name_builder.ext
if not ext:
ext = None ext = None
# Attempt to parse the model file and divine some useful self.ext = ext
# information about it.
with open(queued_filename, 'rb') as model_file:
model = model_loader.auto_detect(model_file, ext)
# generate preview images def _set_model(self):
greatest = [model.width, model.height, model.depth] """
Attempt to parse the model file and divine some useful
information about it.
"""
with open(self.orig_filename, 'rb') as model_file:
self.model = model_loader.auto_detect(model_file, self.ext)
def _set_greatest(self):
greatest = [self.model.width, self.model.height, self.model.depth]
greatest.sort() greatest.sort()
greatest = greatest[-1] self.greatest = greatest[-1]
def snap(name, camera, width=640, height=640, project="ORTHO"): def copy_original(self):
filename = name_builder.fill(name) copy_original(
workbench_path = workbench.joinpath(filename) self.entry, self.orig_filename,
self.name_builder.fill('{basename}{ext}'))
def _snap(self, keyname, name, camera, size, project="ORTHO"):
filename = self.name_builder.fill(name)
workbench_path = self.workbench.joinpath(filename)
shot = { shot = {
"model_path": queued_filename, "model_path": self.orig_filename,
"model_ext": ext, "model_ext": self.ext,
"camera_coord": camera, "camera_coord": camera,
"camera_focus": model.average, "camera_focus": self.model.average,
"camera_clip": greatest*10, "camera_clip": self.greatest*10,
"greatest": greatest, "greatest": self.greatest,
"projection": project, "projection": project,
"width": width, "width": size[0],
"height": height, "height": size[1],
"out_file": workbench_path, "out_file": workbench_path,
} }
blender_render(shot) blender_render(shot)
@ -126,70 +141,139 @@ def process_stl(proc_state):
assert os.path.exists(workbench_path) assert os.path.exists(workbench_path)
# copy it up! # copy it up!
with open(workbench_path, 'rb') as rendered_file: store_public(self.entry, keyname, workbench_path, filename)
public_path = create_pub_filepath(entry, filename)
with mgg.public_store.get_file(public_path, "wb") as public_file: def generate_thumb(self, thumb_size=None):
public_file.write(rendered_file.read()) if not thumb_size:
thumb_size = (mgg.global_config['media:thumb']['max_width'],
mgg.global_config['media:thumb']['max_height'])
return public_path self._snap(
"thumb",
thumb_path = snap(
"{basename}.thumb.jpg", "{basename}.thumb.jpg",
[0, greatest*-1.5, greatest], [0, self.greatest*-1.5, self.greatest],
mgg.global_config['media:thumb']['max_width'], thumb_size,
mgg.global_config['media:thumb']['max_height'],
project="PERSP") project="PERSP")
perspective_path = snap( def generate_perspective(self, size=None):
if not size:
size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
self._snap(
"perspective",
"{basename}.perspective.jpg", "{basename}.perspective.jpg",
[0, greatest*-1.5, greatest], project="PERSP") [0, self.greatest*-1.5, self.greatest],
size,
project="PERSP")
topview_path = snap( def generate_topview(self, size=None):
if not size:
size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
self._snap(
"top",
"{basename}.top.jpg", "{basename}.top.jpg",
[model.average[0], model.average[1], greatest*2]) [self.model.average[0], self.model.average[1],
self.greatest*2],
size)
frontview_path = snap( def generate_frontview(self, size=None):
if not size:
size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
self._snap(
"front",
"{basename}.front.jpg", "{basename}.front.jpg",
[model.average[0], greatest*-2, model.average[2]]) [self.model.average[0], self.greatest*-2,
self.model.average[2]],
size)
sideview_path = snap( def generate_sideview(self, size=None):
if not size:
size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
self._snap(
"side",
"{basename}.side.jpg", "{basename}.side.jpg",
[greatest*-2, model.average[1], model.average[2]]) [self.greatest*-2, self.model.average[1],
self.model.average[2]],
size)
## Save the public file stuffs def store_dimensions(self):
model_filepath = create_pub_filepath( """
entry, name_builder.fill('{basename}{ext}')) Put model dimensions into the database
"""
with mgg.public_store.get_file(model_filepath, 'wb') as model_file:
with open(queued_filename, 'rb') as queued_file:
model_file.write(queued_file.read())
# Remove queued media file from storage and database.
# queued_filepath is in the task_id directory which should
# be removed too, but fail if the directory is not empty to be on
# the super-safe side.
mgg.queue_store.delete_file(queued_filepath) # rm file
mgg.queue_store.delete_dir(queued_filepath[:-1]) # rm dir
entry.queued_media_file = []
# Insert media file information into database
media_files_dict = entry.setdefault('media_files', {})
media_files_dict[u'original'] = model_filepath
media_files_dict[u'thumb'] = thumb_path
media_files_dict[u'perspective'] = perspective_path
media_files_dict[u'top'] = topview_path
media_files_dict[u'side'] = sideview_path
media_files_dict[u'front'] = frontview_path
# Put model dimensions into the database
dimensions = { dimensions = {
"center_x" : model.average[0], "center_x": self.model.average[0],
"center_y" : model.average[1], "center_y": self.model.average[1],
"center_z" : model.average[2], "center_z": self.model.average[2],
"width" : model.width, "width": self.model.width,
"height" : model.height, "height": self.model.height,
"depth" : model.depth, "depth": self.model.depth,
"file_type" : ext, "file_type": self.ext,
} }
entry.media_data_init(**dimensions) self.entry.media_data_init(**dimensions)
class InitialProcessor(CommonStlProcessor):
"""
Initial processing step for new stls
"""
name = "initial"
description = "Initial processing"
@classmethod
def media_is_eligible(cls, entry=None, state=None):
"""
Determine if this media type is eligible for processing
"""
if not state:
state = entry.state
return state in (
"unprocessed", "failed")
@classmethod
def generate_parser(cls):
parser = argparse.ArgumentParser(
description=cls.description,
prog=cls.name)
parser.add_argument(
'--size',
nargs=2,
metavar=('max_width', 'max_height'),
type=int)
parser.add_argument(
'--thumb-size',
nargs=2,
metavar=('max_width', 'max_height'),
type=int)
return parser
@classmethod
def args_to_request(cls, args):
return request_from_args(
args, ['size', 'thumb_size'])
def process(self, size=None, thumb_size=None):
self.common_setup()
self.generate_thumb(thumb_size=thumb_size)
self.generate_perspective(size=size)
self.generate_topview(size=size)
self.generate_frontview(size=size)
self.generate_sideview(size=size)
self.store_dimensions()
self.copy_original()
self.delete_queue_file()
class StlProcessingManager(ProcessingManager):
def __init__(self):
super(self.__class__, self).__init__()
self.add_processor(InitialProcessor)