Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender-addons.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'io_convert_image_to_mesh_img/import_img.py')
-rw-r--r--io_convert_image_to_mesh_img/import_img.py786
1 files changed, 786 insertions, 0 deletions
diff --git a/io_convert_image_to_mesh_img/import_img.py b/io_convert_image_to_mesh_img/import_img.py
new file mode 100644
index 00000000..1735d1c3
--- /dev/null
+++ b/io_convert_image_to_mesh_img/import_img.py
@@ -0,0 +1,786 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+"""
+This script can import a HiRISE DTM .IMG file.
+"""
+
+import bpy
+from bpy.props import *
+
+from struct import pack, unpack, unpack_from
+import os
+import queue, threading
+
+class image_props:
+ ''' keeps track of image attributes throughout the hirise_dtm_helper class '''
+ def __init__(self, name, dimensions, pixel_scale):
+ self.name( name )
+ self.dims( dimensions )
+ self.processed_dims( dimensions )
+ self.pixel_scale( pixel_scale )
+
+ def dims(self, dims=None):
+ if dims is not None:
+ self.__dims = dims
+ return self.__dims
+
+ def processed_dims(self, processed_dims=None):
+ if processed_dims is not None:
+ self.__processed_dims = processed_dims
+ return self.__processed_dims
+
+ def name(self, name=None):
+ if name is not None:
+ self.__name = name
+ return self.__name
+
+ def pixel_scale(self, pixel_scale=None):
+ if pixel_scale is not None:
+ self.__pixel_scale = pixel_scale
+ return self.__pixel_scale
+
+class hirise_dtm_helper(object):
+ ''' methods to understand/import a HiRISE DTM formatted as a PDS .IMG '''
+
+ def __init__(self, context, filepath):
+ self.__context = context
+ self.__filepath = filepath
+ self.__ignore_value = 0x00000000
+ self.__bin_mode = 'BIN6'
+ self.scale( 1.0 )
+ self.__cropXY = False
+ self.marsRed(False)
+
+ def bin_mode(self, bin_mode=None):
+ if bin_mode != None:
+ self.__bin_mode = bin_mode
+ return self.__bin_mode
+
+ def scale(self, scale=None):
+ if scale is not None:
+ self.__scale = scale
+ return self.__scale
+
+ def crop(self, widthX, widthY, offX, offY):
+ self.__cropXY = [ widthX, widthY, offX, offY ]
+ return self.__cropXY
+
+ def marsRed(self, marsRed=None):
+ if marsRed is not None:
+ self.__marsRed = marsRed
+ return self.__marsRed
+
+ def dbg(self, mesg):
+ print(mesg)
+
+ ############################################################################
+ ## PDS Label Operations
+ ############################################################################
+
+ def parsePDSLabel(self, labelIter, currentObjectName=None, level = ""):
+ # Let's parse this thing... semi-recursively
+ ## I started writing this caring about everything in the PDS standard but ...
+ ## it's a mess and I only need a few things -- thar be hacks below
+ ## Mostly I just don't care about continued data from previous lines
+ label_structure = []
+
+ # When are we done with this level?
+ endStr = "END"
+ if not currentObjectName is None:
+ endStr = "END_OBJECT = %s" % currentObjectName
+ line = ""
+
+ while not line.rstrip() == endStr:
+ line = next(labelIter)
+
+ # Get rid of comments
+ comment = line.find("/*")
+ if comment > -1:
+ line = line[:comment]
+
+ # Take notice of objects
+ if line[:8] == "OBJECT =":
+ objName = line[8:].rstrip()
+ label_structure.append(
+ (
+ objName.lstrip().rstrip(),
+ self.parsePDSLabel(labelIter, objName.lstrip().rstrip(), level + " ")
+ )
+ )
+ elif line.find("END_OBJECT =") > -1:
+ pass
+ elif len(line.rstrip().lstrip()) > 0:
+ key_val = line.split(" = ", 2)
+ if len(key_val) == 2:
+ label_structure.append( (key_val[0].rstrip().lstrip(), key_val[1].rstrip().lstrip()) )
+
+ return label_structure
+
+ # There has got to be a better way in python?
+ def iterArr(self, label):
+ for line in label:
+ yield line
+
+ def getPDSLabel(self, img):
+ # Just takes file and stores it into an array for later use
+ label = []
+ done = False;
+ # Grab label into array of lines
+ while not done:
+ line = str(img.readline(), 'utf-8')
+ if line.rstrip() == "END":
+ done = True
+ label.append(line)
+ return (label, self.parsePDSLabel(self.iterArr(label)))
+
+ def getLinesAndSamples(self, label):
+ ''' uses the parsed PDS Label to get the LINES and LINE_SAMPLES parameters
+ from the first object named "IMAGE" -- is hackish
+ '''
+ lines = None
+ line_samples = None
+ for obj in label:
+ if obj[0] == "IMAGE":
+ return self.getLinesAndSamples(obj[1])
+ if obj[0] == "LINES":
+ lines = int(obj[1])
+ if obj[0] == "LINE_SAMPLES":
+ line_samples = int(obj[1])
+
+ return ( line_samples, lines )
+
+ def getValidMinMax(self, label):
+ ''' uses the parsed PDS Label to get the VALID_MINIMUM and VALID_MAXIMUM parameters
+ from the first object named "IMAGE" -- is hackish
+ '''
+ lines = None
+ line_samples = None
+ for obj in label:
+ if obj[0] == "IMAGE":
+ return self.getValidMinMax(obj[1])
+ if obj[0] == "VALID_MINIMUM":
+ vmin = float(obj[1])
+ if obj[0] == "VALID_MAXIMUM":
+ vmax = float(obj[1])
+
+ return ( vmin, vmax )
+
+ def getMissingConstant(self, label):
+ ''' uses the parsed PDS Label to get the MISSING_CONSTANT parameter
+ from the first object named "IMAGE" -- is hackish
+ '''
+
+ lines = None
+ line_samples = None
+ for obj in label:
+ if obj[0] == "IMAGE":
+ return self.getMissingConstant(obj[1])
+ if obj[0] == "MISSING_CONSTANT":
+ bit_string_repr = obj[1]
+
+ # This is always the same for a HiRISE image, so we are just checking it
+ # to be a little less insane here. If someone wants to support another
+ # constant then go for it. Just make sure this one continues to work too
+ pieces = bit_string_repr.split("#")
+ if pieces[0] == "16" and pieces[1] == "FF7FFFFB":
+ ignore_value = unpack("f", pack("I", 0xFF7FFFFB))[0]
+
+ return ( ignore_value )
+
+ ############################################################################
+ ## Image operations
+ ############################################################################
+
+ # decorator to run a generator in a thread
+ def threaded_generator(func):
+ def start(*args,**kwargs):
+ # Setup a queue of returned items
+ yield_q = queue.Queue()
+ # Thread to run generator inside of
+ def worker():
+ for obj in func(*args,**kwargs): yield_q.put(obj)
+ yield_q.put(StopIteration)
+ t = threading.Thread(target=worker)
+ t.start()
+ # yield from the queue as fast as we can
+ obj = yield_q.get()
+ while obj is not StopIteration:
+ yield obj
+ obj = yield_q.get()
+
+ # return the thread-wrapped generator
+ return start
+
+ @threaded_generator
+ def bin2(self, image_iter, bin2_method_type="SLOW"):
+ ''' this is an iterator that: Given an image iterator will yield binned lines '''
+
+ img_props = next(image_iter)
+ # dimensions shrink as we remove pixels
+ processed_dims = img_props.processed_dims()
+ processed_dims = ( processed_dims[0]//2, processed_dims[1]//2 )
+ img_props.processed_dims( processed_dims )
+ # each pixel is larger as binning gets larger
+ pixel_scale = img_props.pixel_scale()
+ pixel_scale = ( pixel_scale[0]*2, pixel_scale[1]*2 )
+ img_props.pixel_scale( pixel_scale )
+ yield img_props
+
+ # Take two lists [a1, a2, a3], [b1, b2, b3] and combine them into one
+ # list of [a1 + b1, a2+b2, ... ] as long as both values are not ignorable
+ combine_fun = lambda a, b: a != self.__ignore_value and b != self.__ignore_value and a + b or self.__ignore_value
+
+ line_count = 0
+ ret_list = []
+ for line in image_iter:
+ if line_count == 1:
+ line_count = 0
+ tmp_list = list(map(combine_fun, line, last_line))
+ while len(tmp_list) > 1:
+ ret_list.append( combine_fun( tmp_list[0], tmp_list[1] ) )
+ del tmp_list[0:2]
+ yield ret_list
+ ret_list = []
+ last_line = line
+ line_count += 1
+
+ @threaded_generator
+ def bin6(self, image_iter, bin6_method_type="SLOW"):
+ ''' this is an iterator that: Given an image iterator will yield binned lines '''
+
+ img_props = next(image_iter)
+ # dimensions shrink as we remove pixels
+ processed_dims = img_props.processed_dims()
+ processed_dims = ( processed_dims[0]//6, processed_dims[1]//6 )
+ img_props.processed_dims( processed_dims )
+ # each pixel is larger as binning gets larger
+ pixel_scale = img_props.pixel_scale()
+ pixel_scale = ( pixel_scale[0]*6, pixel_scale[1]*6 )
+ img_props.pixel_scale( pixel_scale )
+ yield img_props
+
+ if bin6_method_type == "FAST":
+ bin6_method = self.bin6_real_fast
+ else:
+ bin6_method = self.bin6_real
+
+ raw_data = []
+ line_count = 0
+ for line in image_iter:
+ raw_data.append( line )
+ line_count += 1
+ if line_count == 6:
+ yield bin6_method( raw_data )
+ line_count = 0
+ raw_data = []
+
+ def bin6_real(self, raw_data):
+ ''' does a 6x6 sample of raw_data and returns a single line of data '''
+ # TODO: make this more efficient
+
+ binned_data = []
+
+ # Filter out those unwanted hugely negative values...
+ filter_fun = lambda a: self.__ignore_value.__ne__(a)
+
+ base = 0
+ for i in range(0, len(raw_data[0])//6):
+
+ ints = list(filter( filter_fun, raw_data[0][base:base+6] +
+ raw_data[1][base:base+6] +
+ raw_data[2][base:base+6] +
+ raw_data[3][base:base+6] +
+ raw_data[4][base:base+6] +
+ raw_data[5][base:base+6] ))
+ len_ints = len( ints )
+
+ # If we have all pesky values, return a pesky value
+ if len_ints == 0:
+ binned_data.append( self.__ignore_value )
+ else:
+ binned_data.append( sum(ints) / len(ints) )
+
+ base += 6
+ return binned_data
+
+ def bin6_real_fast(self, raw_data):
+ ''' takes a single value from each 6x6 sample of raw_data and returns a single line of data '''
+ # TODO: make this more efficient
+
+ binned_data = []
+
+ base = 0
+ for i in range(0, len(raw_data[0])//6):
+ binned_data.append( raw_data[0][base] )
+ base += 6
+
+ return binned_data
+
+ @threaded_generator
+ def bin12(self, image_iter, bin12_method_type="SLOW"):
+ ''' this is an iterator that: Given an image iterator will yield binned lines '''
+
+ img_props = next(image_iter)
+ # dimensions shrink as we remove pixels
+ processed_dims = img_props.processed_dims()
+ processed_dims = ( processed_dims[0]//12, processed_dims[1]//12 )
+ img_props.processed_dims( processed_dims )
+ # each pixel is larger as binning gets larger
+ pixel_scale = img_props.pixel_scale()
+ pixel_scale = ( pixel_scale[0]*12, pixel_scale[1]*12 )
+ img_props.pixel_scale( pixel_scale )
+ yield img_props
+
+ if bin12_method_type == "FAST":
+ bin12_method = self.bin12_real_fast
+ else:
+ bin12_method = self.bin12_real
+
+ raw_data = []
+ line_count = 0
+ for line in image_iter:
+ raw_data.append( line )
+ line_count += 1
+ if line_count == 12:
+ yield bin12_method( raw_data )
+ line_count = 0
+ raw_data = []
+
+ def bin12_real(self, raw_data):
+ ''' does a 12x12 sample of raw_data and returns a single line of data '''
+
+ binned_data = []
+
+ # Filter out those unwanted hugely negative values...
+ filter_fun = lambda a: self.__ignore_value.__ne__(a)
+
+ base = 0
+ for i in range(0, len(raw_data[0])//12):
+
+ ints = list(filter( filter_fun, raw_data[0][base:base+12] +
+ raw_data[1][base:base+12] +
+ raw_data[2][base:base+12] +
+ raw_data[3][base:base+12] +
+ raw_data[4][base:base+12] +
+ raw_data[5][base:base+12] +
+ raw_data[6][base:base+12] +
+ raw_data[7][base:base+12] +
+ raw_data[8][base:base+12] +
+ raw_data[9][base:base+12] +
+ raw_data[10][base:base+12] +
+ raw_data[11][base:base+12] ))
+ len_ints = len( ints )
+
+ # If we have all pesky values, return a pesky value
+ if len_ints == 0:
+ binned_data.append( self.__ignore_value )
+ else:
+ binned_data.append( sum(ints) / len(ints) )
+
+ base += 12
+ return binned_data
+
+ def bin12_real_fast(self, raw_data):
+ ''' takes a single value from each 12x12 sample of raw_data and returns a single line of data '''
+ return raw_data[0][11::12]
+
+ @threaded_generator
+ def cropXY(self, image_iter, XSize=None, YSize=None, XOffset=0, YOffset=0):
+ ''' return a cropped portion of the image '''
+
+ img_props = next(image_iter)
+ # dimensions shrink as we remove pixels
+ processed_dims = img_props.processed_dims()
+
+ if XSize is None:
+ XSize = processed_dims[0]
+ if YSize is None:
+ YSize = processed_dims[1]
+
+ if XSize + XOffset > processed_dims[0]:
+ self.dbg("WARNING: Upstream dims are larger than cropped XSize dim")
+ XSize = processed_dims[0]
+ XOffset = 0
+ if YSize + YOffset > processed_dims[1]:
+ self.dbg("WARNING: Upstream dims are larger than cropped YSize dim")
+ YSize = processed_dims[1]
+ YOffset = 0
+
+ img_props.processed_dims( (XSize, YSize) )
+ yield img_props
+
+ currentY = 0
+ for line in image_iter:
+ if currentY >= YOffset and currentY <= YOffset + YSize:
+ yield line[XOffset:XOffset+XSize]
+ # Not much point in reading the rest of the data...
+ if currentY == YOffset + YSize:
+ return
+ currentY += 1
+
+ @threaded_generator
+ def getImage(self, img, img_props):
+ ''' Assumes 32-bit pixels -- bins image '''
+ dims = img_props.dims()
+ self.dbg("getting image (x,y): %d,%d" % ( dims[0], dims[1] ))
+
+ # setup to unpack more efficiently.
+ x_len = dims[0]
+ # little endian (PC_REAL)
+ unpack_str = "<"
+ # unpack_str = ">"
+ unpack_bytes_str = "<"
+ pack_bytes_str = "="
+ # 32 bits/sample * samples/line = y_bytes (per line)
+ x_bytes = 4*x_len
+ for x in range(0, x_len):
+ # 32-bit float is "d"
+ unpack_str += "f"
+ unpack_bytes_str += "I"
+ pack_bytes_str += "I"
+
+ # Each iterator yields this first ... it is for reference of the next iterator:
+ yield img_props
+
+ for y in range(0, dims[1]):
+ # pixels is a byte array
+ pixels = b''
+ while len(pixels) < x_bytes:
+ new_pixels = img.read( x_bytes - len(pixels) )
+ pixels += new_pixels
+ if len(new_pixels) == 0:
+ x_bytes = -1
+ pixels = []
+ self.dbg("Uh oh: unexpected EOF!")
+ if len(pixels) == x_bytes:
+ if 0 == 1:
+ repacked_pixels = b''
+ for integer in unpack(unpack_bytes_str, pixels):
+ repacked_pixels += pack("=I", integer)
+ yield unpack( unpack_str, repacked_pixels )
+ else:
+ yield unpack( unpack_str, pixels )
+
+ @threaded_generator
+ def shiftToOrigin(self, image_iter, image_min_max):
+ ''' takes a generator and shifts the points by the valid minimum
+ also removes points with value self.__ignore_value and replaces them with None
+ '''
+
+ # use the passed in values ...
+ valid_min = image_min_max[0]
+
+ # pass on dimensions/pixel_scale since we don't modify them here
+ yield next(image_iter)
+
+ self.dbg("shiftToOrigin filter enabled...");
+
+ # closures rock!
+ def normalize_fun(point):
+ if point == self.__ignore_value:
+ return None
+ return point - valid_min
+
+ for line in image_iter:
+ yield list(map(normalize_fun, line))
+ self.dbg("shifted all points")
+
+ @threaded_generator
+ def scaleZ(self, image_iter, scale_factor):
+ ''' scales the mesh values by a factor '''
+ # pass on dimensions since we don't modify them here
+ yield next(image_iter)
+
+ scale_factor = self.scale()
+
+ def scale_fun(point):
+ try:
+ return point * scale_factor
+ except:
+ return None
+
+ for line in image_iter:
+ yield list(map(scale_fun, line))
+
+ def genMesh(self, image_iter):
+ '''Returns a mesh object from an image iterator this has the
+ value-added feature that a value of "None" is ignored
+ '''
+
+ # Get the output image size given the above transforms
+ img_props = next(image_iter)
+
+ # Let's interpolate the binned DTM with blender -- yay meshes!
+ coords = []
+ faces = []
+ face_count = 0
+ coord = -1
+ max_x = img_props.processed_dims()[0]
+ max_y = img_props.processed_dims()[1]
+
+ scale_x = self.scale() * img_props.pixel_scale()[0]
+ scale_y = self.scale() * img_props.pixel_scale()[1]
+
+ line_count = 0
+ current_line = []
+ # seed the last line (or previous line) with a line
+ last_line = next(image_iter)
+ point_offset = 0
+ previous_point_offset = 0
+
+ # Let's add any initial points that are appropriate
+ x = 0
+ point_offset += len( last_line ) - last_line.count(None)
+ for z in last_line:
+ if z != None:
+ coords.extend([x*scale_x, 0.0, z])
+ coord += 1
+ x += 1
+
+ # We want to ignore points with a value of "None" but we also need to create vertices
+ # with an index that we can re-create on the next line. The solution is to remember
+ # two offsets: the point offset and the previous point offset.
+ # these offsets represent the point index that blender gets -- not the number of
+ # points we have read from the image
+
+ # if "x" represents points that are "None" valued then conceptually this is how we
+ # think of point indices:
+ #
+ # previous line: offset0 x x +1 +2 +3
+ # current line: offset1 x +1 +2 +3 x
+
+ # once we can map points we can worry about making triangular or square faces to fill
+ # the space between vertices so that blender is more efficient at managing the final
+ # structure.
+
+ self.dbg('generate mesh coords/faces from processed image data...')
+
+ # read each new line and generate coordinates+faces
+ for dtm_line in image_iter:
+
+ # Keep track of where we are in the image
+ line_count += 1
+ y_val = line_count*-scale_y
+ if line_count % 31 == 0:
+ self.dbg("reading image... %d of %d" % ( line_count, max_y ))
+
+ # Just add all points blindly
+ # TODO: turn this into a map
+ x = 0
+ for z in dtm_line:
+ if z != None:
+ coords.extend( [x*scale_x, y_val, z] )
+ coord += 1
+ x += 1
+
+ # Calculate faces
+ for x in range(0, max_x - 1):
+ vals = [
+ last_line[ x + 1 ],
+ last_line[ x ],
+ dtm_line[ x ],
+ dtm_line[ x + 1 ],
+ ]
+
+ # Two or more values of "None" means we can ignore this block
+ none_val = vals.count(None)
+
+ # Common case: we can create a square face
+ if none_val == 0:
+ faces.extend( [
+ previous_point_offset,
+ previous_point_offset+1,
+ point_offset+1,
+ point_offset,
+ ] )
+ face_count += 1
+ elif none_val == 1:
+ # special case: we can implement a triangular face
+ ## NB: blender 2.5 makes a triangular face when the last coord is 0
+ # TODO: implement a triangular face
+ pass
+
+ if vals[1] != None:
+ previous_point_offset += 1
+ if vals[2] != None:
+ point_offset += 1
+
+ # Squeeze the last point offset increment out of the previous line
+ if last_line[-1] != None:
+ previous_point_offset += 1
+
+ # Squeeze the last point out of the current line
+ if dtm_line[-1] != None:
+ point_offset += 1
+
+ # remember what we just saw (and forget anything before that)
+ last_line = dtm_line
+
+ self.dbg('generate mesh from coords/faces...')
+ me = bpy.data.meshes.new(img_props.name()) # create a new mesh
+
+ self.dbg('coord: %d' % coord)
+ self.dbg('len(coords): %d' % len(coords))
+ self.dbg('len(faces): %d' % len(faces))
+
+ self.dbg('setting coords...')
+ me.vertices.add(len(coords)/3)
+ me.vertices.foreach_set("co", coords)
+
+ self.dbg('setting faces...')
+ me.faces.add(len(faces)/4)
+ me.faces.foreach_set("vertices_raw", faces)
+
+ self.dbg('running update...')
+ me.update()
+
+ bin_desc = self.bin_mode()
+ if bin_desc == 'NONE':
+ bin_desc = 'No Bin'
+
+ ob=bpy.data.objects.new("DTM - %s" % bin_desc, me)
+
+ return ob
+
+ def marsRedMaterial(self):
+ ''' produce some approximation of a mars surface '''
+ mat = None
+ for material in bpy.data.materials:
+ if material.getName() == "redMars":
+ mat = material
+ if mat is None:
+ mat = bpy.data.materials.new("redMars")
+ mat.diffuse_shader = 'MINNAERT'
+ mat.setRGBCol( (0.426, 0.213, 0.136) )
+ mat.setDiffuseDarkness(0.8)
+ mat.specular_shader = 'WARDISO'
+ mat.setSpecCol( (1.000, 0.242, 0.010) )
+ mat.setSpec( 0.010 )
+ mat.setRms( 0.100 )
+ return mat
+
+ ################################################################################
+ # Yay, done with helper functions ... let's see the abstraction in action! #
+ ################################################################################
+ def execute(self):
+
+ self.dbg('opening/importing file: %s' % self.__filepath)
+ img = open(self.__filepath, 'rb')
+
+ self.dbg('read PDS Label...')
+ (label, parsedLabel) = self.getPDSLabel(img)
+
+ self.dbg('parse PDS Label...')
+ image_dims = self.getLinesAndSamples(parsedLabel)
+ img_min_max_vals = self.getValidMinMax(parsedLabel)
+ self.__ignore_value = self.getMissingConstant(parsedLabel)
+
+ self.dbg('import/bin image data...')
+
+ # MAGIC VALUE? -- need to formalize this to rid ourselves of bad points
+ img.seek(28)
+ # Crop off 4 lines
+ img.seek(4*image_dims[0])
+
+ # HiRISE images (and most others?) have 1m x 1m pixels
+ pixel_scale=(1, 1)
+
+ # The image we are importing
+ image_name = os.path.basename( self.__filepath )
+
+ # Set the properties of the image in a manageable object
+ img_props = image_props( image_name, image_dims, pixel_scale )
+
+ # Get an iterator to iterate over lines
+ image_iter = self.getImage(img, img_props)
+
+ ## Wrap the image_iter generator with other generators to modify the dtm on a
+ ## line-by-line basis. This creates a stream of modifications instead of reading
+ ## all of the data at once, processing all of the data (potentially several times)
+ ## and then handing it off to blender
+ ## TODO: find a way to alter projection based on transformations below
+
+ if self.__cropXY:
+ image_iter = self.cropXY(image_iter,
+ XSize=self.__cropXY[0],
+ YSize=self.__cropXY[1],
+ XOffset=self.__cropXY[2],
+ YOffset=self.__cropXY[3]
+ )
+
+ # Select an appropriate binning mode
+ ## TODO: generalize the binning fn's
+ bin_mode = self.bin_mode()
+ bin_mode_funcs = {
+ 'BIN2': self.bin2(image_iter),
+ 'BIN6': self.bin6(image_iter),
+ 'BIN6-FAST': self.bin6(image_iter, 'FAST'),
+ 'BIN12': self.bin12(image_iter),
+ 'BIN12-FAST': self.bin12(image_iter, 'FAST')
+ }
+ if bin_mode in bin_mode_funcs.keys():
+ image_iter = bin_mode_funcs[ bin_mode ]
+
+ image_iter = self.shiftToOrigin(image_iter, img_min_max_vals)
+
+ if self.scale != 1.0:
+ image_iter = self.scaleZ(image_iter, img_min_max_vals)
+
+ # Create a new mesh object and set data from the image iterator
+ self.dbg('generating mesh object...')
+ ob_new = self.genMesh(image_iter)
+
+ if self.marsRed():
+ mars_red = self.marsRedMaterial()
+ ob_new.materials += [mars_red]
+
+ if img:
+ img.close()
+
+ # Add mesh object to the current scene
+ scene = self.__context.scene
+ self.dbg('linking object to scene...')
+ scene.objects.link(ob_new)
+ scene.update()
+
+ # deselect other objects
+ bpy.ops.object.select_all(action='DESELECT')
+
+ # scene.objects.active = ob_new
+ # Select the new mesh
+ ob_new.select = True
+
+ self.dbg('done with ops ... now wait for blender ...')
+
+ return ('FINISHED',)
+
+def load(operator, context, filepath, scale, bin_mode, cropVars, marsRed):
+ print("Bin Mode: %s" % bin_mode)
+ print("Scale: %f" % scale)
+ helper = hirise_dtm_helper(context,filepath)
+ helper.bin_mode( bin_mode )
+ helper.scale( scale )
+ if cropVars:
+ helper.crop( cropVars[0], cropVars[1], cropVars[2], cropVars[3] )
+ helper.execute()
+ if marsRed:
+ helper.marsRed(marsRed)
+
+ print("Loading %s" % filepath)
+ return {'FINISHED'}