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:
ext = None
# Attempt to parse the model file and divine some useful def _set_ext(self):
# information about it. ext = self.name_builder.ext
with open(queued_filename, 'rb') as model_file:
model = model_loader.auto_detect(model_file, ext)
# generate preview images if not ext:
greatest = [model.width, model.height, model.depth] ext = None
greatest.sort()
greatest = greatest[-1]
def snap(name, camera, width=640, height=640, project="ORTHO"): self.ext = ext
filename = name_builder.fill(name)
workbench_path = workbench.joinpath(filename) def _set_model(self):
"""
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()
self.greatest = greatest[-1]
def copy_original(self):
copy_original(
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",
"{basename}.thumb.jpg",
[0, self.greatest*-1.5, self.greatest],
thumb_size,
project="PERSP")
thumb_path = snap( def generate_perspective(self, size=None):
"{basename}.thumb.jpg", if not size:
[0, greatest*-1.5, greatest], size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:thumb']['max_width'], mgg.global_config['media:medium']['max_height'])
mgg.global_config['media:thumb']['max_height'],
project="PERSP")
perspective_path = snap( self._snap(
"{basename}.perspective.jpg", "perspective",
[0, greatest*-1.5, greatest], project="PERSP") "{basename}.perspective.jpg",
[0, self.greatest*-1.5, self.greatest],
size,
project="PERSP")
topview_path = snap( def generate_topview(self, size=None):
"{basename}.top.jpg", if not size:
[model.average[0], model.average[1], greatest*2]) size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
frontview_path = snap( self._snap(
"{basename}.front.jpg", "top",
[model.average[0], greatest*-2, model.average[2]]) "{basename}.top.jpg",
[self.model.average[0], self.model.average[1],
self.greatest*2],
size)
sideview_path = snap( def generate_frontview(self, size=None):
"{basename}.side.jpg", if not size:
[greatest*-2, model.average[1], model.average[2]]) size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
## Save the public file stuffs self._snap(
model_filepath = create_pub_filepath( "front",
entry, name_builder.fill('{basename}{ext}')) "{basename}.front.jpg",
[self.model.average[0], self.greatest*-2,
self.model.average[2]],
size)
with mgg.public_store.get_file(model_filepath, 'wb') as model_file: def generate_sideview(self, size=None):
with open(queued_filename, 'rb') as queued_file: if not size:
model_file.write(queued_file.read()) size = (mgg.global_config['media:medium']['max_width'],
mgg.global_config['media:medium']['max_height'])
# Remove queued media file from storage and database. self._snap(
# queued_filepath is in the task_id directory which should "side",
# be removed too, but fail if the directory is not empty to be on "{basename}.side.jpg",
# the super-safe side. [self.greatest*-2, self.model.average[1],
mgg.queue_store.delete_file(queued_filepath) # rm file self.model.average[2]],
mgg.queue_store.delete_dir(queued_filepath[:-1]) # rm dir size)
entry.queued_media_file = []
# Insert media file information into database def store_dimensions(self):
media_files_dict = entry.setdefault('media_files', {}) """
media_files_dict[u'original'] = model_filepath Put model dimensions into the database
media_files_dict[u'thumb'] = thumb_path """
media_files_dict[u'perspective'] = perspective_path dimensions = {
media_files_dict[u'top'] = topview_path "center_x": self.model.average[0],
media_files_dict[u'side'] = sideview_path "center_y": self.model.average[1],
media_files_dict[u'front'] = frontview_path "center_z": self.model.average[2],
"width": self.model.width,
"height": self.model.height,
"depth": self.model.depth,
"file_type": self.ext,
}
self.entry.media_data_init(**dimensions)
# Put model dimensions into the database
dimensions = { class InitialProcessor(CommonStlProcessor):
"center_x" : model.average[0], """
"center_y" : model.average[1], Initial processing step for new stls
"center_z" : model.average[2], """
"width" : model.width, name = "initial"
"height" : model.height, description = "Initial processing"
"depth" : model.depth,
"file_type" : ext, @classmethod
} def media_is_eligible(cls, entry=None, state=None):
entry.media_data_init(**dimensions) """
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)