def optimizeTextures(mesh): previous_images = [] for cimg in mesh.images: previous_images.append(cimg.path) pilimg = cimg.pilimage #PIL doesn't support DDS, so if loading failed, try and load it as a DDS with panda3d if pilimg is None: imgdata = cimg.data #if we can't even load the image's data, can't convert if imgdata is None: print("Couldn't load image data", file=sys.stderr) continue try: from panda3d.core import Texture from panda3d.core import StringStream from panda3d.core import PNMImage except ImportError: #if panda3d isn't installed and PIL failed, can't convert print('Tried loading image with PIL and DDS and both failed', file=sys.stderr) continue t = Texture() success = t.readDds(StringStream(imgdata)) if success == 0: #failed to load as DDS, so let's give up print('Tried loading image as DDS and failed', file=sys.stderr) continue #convert DDS to PNG outdata = t.getRamImageAs('RGB').getData() try: im = Image.fromstring('RGB', (t.getXSize(), t.getYSize()), outdata) im.load() except IOError: #Any problem with panda3d might generate an invalid image buffer, so don't convert this print('Problem loading DDS file with PIL', file=sys.stderr) continue pilimg = im if pilimg.format == 'JPEG': #PIL image is already in JPG format so don't convert continue if 'A' in pilimg.getbands(): alpha = numpy.array(pilimg.split()[-1].getdata()) if not numpy.any(alpha < 255): alpha = None #this means that none of the pixels are using alpha, so convert to RGB pilimg = pilimg.convert('RGB') if 'A' in pilimg.getbands(): #save textures with an alpha channel in PNG output_format = 'PNG' output_extension = '.png' output_options = {'optimize':True} else: if pilimg.format != 'RGB': pilimg = pilimg.convert("RGB") #otherwise save as JPEG since it gets output_format = 'JPEG' output_extension = '.jpg' output_options = {'quality':95, 'optimize':True} if cimg.path.lower()[-len(output_extension):] != output_extension: dot = cimg.path.rfind('.') before_ext = cimg.path[0:dot] if dot != -1 else cimg.path while before_ext + output_extension in previous_images: before_ext = before_ext + '-x' cimg.path = before_ext + output_extension previous_images.append(cimg.path) outbuf = StringIO() try: pilimg.save(outbuf, output_format, **output_options) except IOError as ex: print(ex) cimg.data = outbuf.getvalue()
def getMipMaps(mesh): mipmaps = {} for effect in mesh.effects: for prop in effect.supported: propval = getattr(effect, prop) if isinstance(propval, collada.material.Map): image_name = propval.sampler.surface.image.path image_data = propval.sampler.surface.image.data try: im = Image.open(StringIO(image_data)) im.load() except IOError: from panda3d.core import Texture from panda3d.core import StringStream from panda3d.core import PNMImage #PIL failed, so lets try DDS reader with panda3d t = Texture(image_name) success = t.readDds(StringStream(image_data)) if success == 0: raise FilterException("Failed to read image file %s" % image_name) #convert DDS to PNG outdata = t.getRamImageAs('RGBA').getData() try: im = Image.fromstring('RGBA', (t.getXSize(), t.getYSize()), outdata) im.load() except IOError: raise FilterException("Failed to read image file %s" % image_name) #Keep JPG in same format since JPG->PNG is pretty bad if im.format == 'JPEG': output_format = 'JPEG' output_extension = 'jpg' output_options = {'quality': 95, 'optimize': True} else: output_format = 'PNG' output_extension = 'png' output_options = {'optimize': True} #store a copy to the original image so we can resize from it directly each time orig_im = im width, height = im.size #round down to power of 2 width = int(math.pow(2, int(math.log(width, 2)))) height = int(math.pow(2, int(math.log(height, 2)))) pil_images = [] while True: im = orig_im.resize((width, height), Image.ANTIALIAS) pil_images.insert(0, im) if width == 1 and height == 1: break width = max(width / 2, 1) height = max(height / 2, 1) tar_buf = StringIO() tar = tarfile.TarFile(fileobj=tar_buf, mode='w') cur_offset = 0 byte_ranges = [] for i, pil_img in enumerate(pil_images): buf = StringIO() pil_img.save(buf, output_format, **output_options) file_len = buf.tell() cur_name = '%dx%d.%s' % (pil_img.size[0], pil_img.size[1], output_extension) tar_info = tarfile.TarInfo(name=cur_name) tar_info.size = file_len buf.seek(0) tar.addfile(tarinfo=tar_info, fileobj=buf) #tar files have a 512 byte header cur_offset += 512 file_start = cur_offset byte_ranges.append({ 'offset': file_start, 'length': file_len, 'width': pil_img.size[0], 'height': pil_img.size[1] }) #file lengths are rounded up to nearest 512 multiple file_len = 512 * ((file_len + 512 - 1) / 512) cur_offset += file_len tar.close() mipmaps[propval.sampler.surface.image.path] = ( tar_buf.getvalue(), byte_ranges) return mipmaps
class Panda3dCameraSensor(object): def __init__(self, base, color=True, depth=False, size=None, near_far=None, hfov=None, title=None): if size is None: size = (640, 480) if near_far is None: near_far = (0.01, 10000.0) if hfov is None: hfov = 60 winprops = WindowProperties.size(*size) winprops.setTitle(title or 'Camera Sensor') fbprops = FrameBufferProperties() # Request 8 RGB bits, 8 alpha bits, and a depth buffer. fbprops.setRgbColor(True) fbprops.setRgbaBits(8, 8, 8, 8) fbprops.setDepthBits(24) self.graphics_engine = GraphicsEngine(base.pipe) window_type = base.config.GetString('window-type', 'onscreen') flags = GraphicsPipe.BFFbPropsOptional if window_type == 'onscreen': flags = flags | GraphicsPipe.BFRequireWindow elif window_type == 'offscreen': flags = flags | GraphicsPipe.BFRefuseWindow self.buffer = self.graphics_engine.makeOutput(base.pipe, "camera sensor buffer", -100, fbprops, winprops, flags) if not color and not depth: raise ValueError("At least one of color or depth should be True") if color: self.color_tex = Texture("color_texture") self.buffer.addRenderTexture(self.color_tex, GraphicsOutput.RTMCopyRam, GraphicsOutput.RTPColor) else: self.color_tex = None if depth: self.depth_tex = Texture("depth_texture") self.buffer.addRenderTexture(self.depth_tex, GraphicsOutput.RTMCopyRam, GraphicsOutput.RTPDepth) else: self.depth_tex = None self.cam = base.makeCamera(self.buffer, scene=base.render, camName='camera_sensor') self.lens = self.cam.node().getLens() self.lens.setFov(hfov) self.lens.setFilmSize( *size) # this also defines the units of the focal length self.lens.setNearFar(*near_far) def observe(self): for _ in range(self.graphics_engine.getNumWindows()): self.graphics_engine.renderFrame() self.graphics_engine.syncFrame() images = [] if self.color_tex: data = self.color_tex.getRamImageAs('RGBA') if sys.version_info < (3, 0): data = data.get_data() image = np.frombuffer(data, np.uint8) image.shape = (self.color_tex.getYSize(), self.color_tex.getXSize(), self.color_tex.getNumComponents()) image = np.flipud(image) image = image[ ..., : -1] # remove alpha channel; if alpha values are needed, set alpha bits to 8 images.append(image) if self.depth_tex: depth_data = self.depth_tex.getRamImage() if sys.version_info < (3, 0): depth_data = depth_data.get_data() depth_image_size = self.depth_tex.getYSize( ) * self.depth_tex.getXSize() * self.depth_tex.getNumComponents() if len(depth_data) == 2 * depth_image_size: dtype = np.float16 elif len(depth_data) == 3 * depth_image_size: dtype = np.float24 elif len(depth_data) == 4 * depth_image_size: dtype = np.float32 else: raise ValueError( "Depth data has %d bytes but the size of the depth image is %d" % (len(depth_data), depth_image_size)) depth_image = np.frombuffer(depth_data, dtype) depth_image.shape = (self.depth_tex.getYSize(), self.depth_tex.getXSize(), self.depth_tex.getNumComponents()) depth_image = np.flipud(depth_image) depth_image = depth_image.astype( np.float32, copy=False) # copy only if necessary images.append(depth_image) return tuple(images)
def getMipMaps(mesh): mipmaps = {} for effect in mesh.effects: for prop in effect.supported: propval = getattr(effect, prop) if isinstance(propval, collada.material.Map): image_name = propval.sampler.surface.image.path image_data = propval.sampler.surface.image.data try: im = Image.open(StringIO(image_data)) im.load() except IOError: from panda3d.core import Texture from panda3d.core import StringStream from panda3d.core import PNMImage #PIL failed, so lets try DDS reader with panda3d t = Texture(image_name) success = t.readDds(StringStream(image_data)) if success == 0: raise FilterException("Failed to read image file %s" % image_name) #convert DDS to PNG outdata = t.getRamImageAs('RGBA').getData() try: im = Image.fromstring('RGBA', (t.getXSize(), t.getYSize()), outdata) im.load() except IOError: raise FilterException("Failed to read image file %s" % image_name) #Keep JPG in same format since JPG->PNG is pretty bad if im.format == 'JPEG': output_format = 'JPEG' output_extension = 'jpg' output_options = {'quality': 95, 'optimize':True} else: output_format = 'PNG' output_extension = 'png' output_options = {'optimize':True} #store a copy to the original image so we can resize from it directly each time orig_im = im width, height = im.size #round down to power of 2 width = int(math.pow(2, int(math.log(width, 2)))) height = int(math.pow(2, int(math.log(height, 2)))) pil_images = [] while True: im = orig_im.resize((width, height), Image.ANTIALIAS) pil_images.insert(0, im) if width == 1 and height == 1: break width = max(width / 2, 1) height = max(height / 2, 1) tar_buf = StringIO() tar = tarfile.TarFile(fileobj=tar_buf, mode='w') cur_offset = 0 byte_ranges = [] for i, pil_img in enumerate(pil_images): buf = StringIO() pil_img.save(buf, output_format, **output_options) file_len = buf.tell() cur_name = '%dx%d.%s' % (pil_img.size[0], pil_img.size[1], output_extension) tar_info = tarfile.TarInfo(name=cur_name) tar_info.size=file_len buf.seek(0) tar.addfile(tarinfo=tar_info, fileobj=buf) #tar files have a 512 byte header cur_offset += 512 file_start = cur_offset byte_ranges.append({'offset':file_start, 'length':file_len, 'width':pil_img.size[0], 'height':pil_img.size[1]}) #file lengths are rounded up to nearest 512 multiple file_len = 512 * ((file_len + 512 - 1) / 512) cur_offset += file_len tar.close() mipmaps[propval.sampler.surface.image.path] = (tar_buf.getvalue(), byte_ranges) return mipmaps