Added initial stl processor
This commit is contained in:
parent
ab64ca3474
commit
77daec9224
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user