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
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import argparse
import os
import json
import logging
@ -21,8 +22,11 @@ import subprocess
import pkg_resources
from mediagoblin import mg_globals as mgg
from mediagoblin.processing import create_pub_filepath, \
FilenameBuilder
from mediagoblin.processing import (
FilenameBuilder, MediaProcessor,
ProcessingManager, request_from_args,
get_orig_filename, store_public,
copy_original)
from mediagoblin.media_types.stl import model_loader
@ -75,49 +79,60 @@ def blender_render(config):
env=env)
def process_stl(proc_state):
"""Code to process an stl or obj model. Will be run by celery.
A Workbench() represents a local tempory dir. It is automatically
cleaned up when this function exits.
class CommonStlProcessor(MediaProcessor):
"""
Provides a common base for various stl processing steps
"""
entry = proc_state.entry
workbench = proc_state.workbench
queued_filepath = entry.queued_media_file
queued_filename = workbench.localized_file(
mgg.queue_store, queued_filepath, 'source')
name_builder = FilenameBuilder(queued_filename)
def common_setup(self):
# Pull down and set up the original file
self.orig_filename = get_orig_filename(
self.entry, self.workbench)
self.name_builder = FilenameBuilder(self.orig_filename)
ext = queued_filename.lower().strip()[-4:]
if ext.startswith("."):
ext = ext[1:]
else:
self._set_ext()
self._set_model()
self._set_greatest()
def _set_ext(self):
ext = self.name_builder.ext
if not ext:
ext = None
# Attempt to parse the model file and divine some useful
# information about it.
with open(queued_filename, 'rb') as model_file:
model = model_loader.auto_detect(model_file, ext)
self.ext = ext
# generate preview images
greatest = [model.width, model.height, model.depth]
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()
greatest = greatest[-1]
self.greatest = greatest[-1]
def snap(name, camera, width=640, height=640, project="ORTHO"):
filename = name_builder.fill(name)
workbench_path = workbench.joinpath(filename)
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 = {
"model_path": queued_filename,
"model_ext": ext,
"model_path": self.orig_filename,
"model_ext": self.ext,
"camera_coord": camera,
"camera_focus": model.average,
"camera_clip": greatest*10,
"greatest": greatest,
"camera_focus": self.model.average,
"camera_clip": self.greatest*10,
"greatest": self.greatest,
"projection": project,
"width": width,
"height": height,
"width": size[0],
"height": size[1],
"out_file": workbench_path,
}
blender_render(shot)
@ -126,70 +141,139 @@ def process_stl(proc_state):
assert os.path.exists(workbench_path)
# copy it up!
with open(workbench_path, 'rb') as rendered_file:
public_path = create_pub_filepath(entry, filename)
store_public(self.entry, keyname, workbench_path, filename)
with mgg.public_store.get_file(public_path, "wb") as public_file:
public_file.write(rendered_file.read())
def generate_thumb(self, thumb_size=None):
if not thumb_size:
thumb_size = (mgg.global_config['media:thumb']['max_width'],
mgg.global_config['media:thumb']['max_height'])
return public_path
thumb_path = snap(
self._snap(
"thumb",
"{basename}.thumb.jpg",
[0, greatest*-1.5, greatest],
mgg.global_config['media:thumb']['max_width'],
mgg.global_config['media:thumb']['max_height'],
[0, self.greatest*-1.5, self.greatest],
thumb_size,
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",
[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",
[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",
[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",
[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
model_filepath = create_pub_filepath(
entry, name_builder.fill('{basename}{ext}'))
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
def store_dimensions(self):
"""
Put model dimensions into the database
"""
dimensions = {
"center_x" : model.average[0],
"center_y" : model.average[1],
"center_z" : model.average[2],
"width" : model.width,
"height" : model.height,
"depth" : model.depth,
"file_type" : ext,
"center_x": self.model.average[0],
"center_y": self.model.average[1],
"center_z": self.model.average[2],
"width": self.model.width,
"height": self.model.height,
"depth": self.model.depth,
"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)