示例#1
0
def test_read_tags(*args):
    if len(args) == 0:
        tiff_fn = "test_ninjo.tif"
    else:
        tiff_fn = args[0]

    if not os.path.exists(tiff_fn):
        LOG.error("TIFF input file %s doesn't exists" % (tiff_fn, ))
        return -1

    a = TIFF.open(tiff_fn, "r")

    image = a.read_image()
    print "Image data has shape %r" % (image.shape, )
    print "Tag %s: %s" % ("ModelTiePoint", a.GetField("ModelTiePoint"))
示例#2
0
def test_read_tags(*args):
    if len(args) == 0:
        tiff_fn = "test_ninjo.tif"
    else:
        tiff_fn = args[0]

    if not os.path.exists(tiff_fn):
        LOG.error("TIFF input file %s doesn't exists" % (tiff_fn,))
        return -1

    a = TIFF.open(tiff_fn, "r")

    image = a.read_image()
    print "Image data has shape %r" % (image.shape,)
    print "Tag %s: %s" % ("ModelTiePoint", a.GetField("ModelTiePoint"))
示例#3
0
def test_write(*args):
    """Recreate a simple NinJo compatible tiff file from an original
    GOESE example.

    Mainly a test of the tags.
    """
    if len(args) == 0:
        tiff_fn = "test_ninjo.tif"
    else:
        tiff_fn = args[0]

    # Represents original high resolution data array
    #data_array = numpy.zeros((2500,2500), dtype=numpy.uint8)
    data_array = numpy.tile(range(500), (2500, 5)).astype(numpy.uint8)
    print data_array.shape

    # Open file
    tiff_file = TIFF.open(tiff_fn, "w")

    ### Write first directory and tags ###
    tiff_file.SetDirectory(0)
    tiff_file.SetField("ImageWidth", 2500)
    tiff_file.SetField("ImageLength", 2500)
    tiff_file.SetField("BITspersample", 8)
    tiff_file.SetField("compression", libtiff.COMPRESSION_LZW)
    #FIXME: Sample file used colormap
    #tiff_file.SetField("PHOTOMETRIC", libtiff.PHOTOMETRIC_MINISBLACK)
    tiff_file.SetField("PHOTOMETRIC", libtiff.PHOTOMETRIC_PALETTE)
    tiff_file.SetField("ORIENTATION", libtiff.ORIENTATION_TOPLEFT)
    tiff_file.SetField("SamplesPerPixel", 1)
    tiff_file.SetField("SMinSampleValue", 0)
    tiff_file.SetField("SMaxsampleValue", 255)
    tiff_file.SetField("Planarconfig", libtiff.PLANARCONFIG_CONTIG)
    #tiff_file.SetField("ColorMap", [ [ x*256 for x in range(256) ],[0]*256,[0]*256 ])
    tiff_file.SetField("ColorMap", [[x * 256 for x in range(256)]] * 3)
    tiff_file.SetField("TILEWIDTH", 512)
    tiff_file.SetField("TILELENGTH", 512)
    tiff_file.SetField("sampleformat", libtiff.SAMPLEFORMAT_UINT)

    ### NINJO SPECIFIC ###
    tiff_file.SetField("ModelPixelScale", [0.071957, 0.071957])
    tiff_file.SetField("ModelTiePoint",
                       [0.0, 0.0, 0.0, -164.874313, 89.874321, 0.0])
    tiff_file.SetField("NinjoName", "NINJO")
    tiff_file.SetField("SatelliteNameID", 7300014)
    tiff_file.SetField("DateID", 1337169600)
    tiff_file.SetField("CreationDateID", 1337171130)
    tiff_file.SetField("ChannelID", 900015)
    tiff_file.SetField("HeaderVersion", 2)
    tiff_file.SetField("FileName",
                       "GOESE_AMERIKA_IR107_nq075W8km_1205161200.tif")
    tiff_file.SetField("DataType", "GORN")
    tiff_file.SetField("SatelliteNumber", "\x00")  # Hardcoded to 0
    tiff_file.SetField("ColorDepth", 16)  #Um 8? hardcoded to 8, but 16?
    tiff_file.SetField("DataSource", "EUMCAST")
    tiff_file.SetField("XMinimum", 1)
    tiff_file.SetField("XMaximum", 2500)
    tiff_file.SetField("YMinimum", 1)
    tiff_file.SetField("YMaximum", 2500)
    tiff_file.SetField("Projection", "PLAT")
    tiff_file.SetField("MeridianWest", 0.0)
    tiff_file.SetField("MeridianEast", 0.0)
    tiff_file.SetField("EarthRadiusLarge", 6370000.0)
    tiff_file.SetField("EarthRadiusSmall", 6370000.0)
    tiff_file.SetField("GeodeticDate", "\x00")  # ---?
    tiff_file.SetField("ReferenceLatitude1", 0.0)
    tiff_file.SetField("ReferenceLatitude2", 0.0)
    tiff_file.SetField("CentralMeridian", -75.000)
    tiff_file.SetField("PhysicValue", "T")
    tiff_file.SetField("PhysicUnit", "CELSIUS")
    tiff_file.SetField("MinGrayValue", 0)
    tiff_file.SetField("MaxGrayValue", 255)
    tiff_file.SetField("Gradient", -0.5)
    tiff_file.SetField("AxisIntercept", 40.0)
    tiff_file.SetField("Altitude", 42164.0)
    tiff_file.SetField("IsCalibrated", 1)
    tiff_file.SetField("IsNormalized", 0)

    tiff_file.write_tiles(data_array)
    tiff_file.WriteDirectory()

    ### Directory 2 ###
    #tiff_file.SetDirectory(1)
    #tiff_file.SetField("ModelTiePoint", write_list([0.0, 0.0, 0.0, -164.838348, 89.838341, 0.0], ctypes.c_double))
    #tiff_file.write_image(data_array[::2, ::2])

    return 0
示例#4
0
def test_write_tags(*args):
    """Create a sample NinJo file that writes all ninjo tags and a fake data
    array to a new tiff file.
    """
    if len(args) == 0:
        tiff_fn = "test_ninjo.tif"
    else:
        tiff_fn = args[0]

    # Represents original high resolution data array
    #data_array = numpy.zeros((5,5), dtype=numpy.uint8)
    data_array = numpy.zeros((2500, 2500), dtype=numpy.uint8)

    # Open file
    tiff_file = TIFF.open(tiff_fn, "w")
    tiff_file.SetDirectory(0)

    ### Write first set of tags ###
    print "ModelTiePoint"
    tiff_file.SetField("ModelTiePoint", [1, 2, 3, 4, 5, 6])
    print "ModelPixelScale"
    tiff_file.SetField("ModelPixelScale", [1, 2])
    print "TransparentPixel"
    tiff_file.SetField("TransparentPixel", 1)
    print "NinjoName"
    tiff_file.SetField("NinjoName", "NINJO")
    print "SatelliteNameID"
    tiff_file.SetField("SatelliteNameID", 1234)
    print "DateID"
    tiff_file.SetField("DateID", 1234)
    print "CreationDateID"
    tiff_file.SetField("CreationDateID", 1234)
    print "ChannelID"
    tiff_file.SetField("ChannelID", 1234)
    print "HeaderVersion"
    tiff_file.SetField("HeaderVersion", 2)
    print "Filename"
    tiff_file.SetField("Filename", "a_fake_satellite_file.h5")
    print "DataType"
    tiff_file.SetField("DataType", "P**N")
    print "SatelliteNumber"
    tiff_file.SetField("SatelliteNumber", "7")  #?
    print "ColorDepth"
    tiff_file.SetField("ColorDepth", 8)
    print "DataSource"
    tiff_file.SetField("DataSource", "PDUS")
    print "XMinimum"
    tiff_file.SetField("XMinimum", 0)
    print "XMaximum"
    tiff_file.SetField("XMaximum", 2500)
    print "YMinimum"
    tiff_file.SetField("YMinimum", 0)
    print "YMaximum"
    tiff_file.SetField("YMaximum", 2500)
    print "Projection"
    tiff_file.SetField("Projection", "NPOL")
    print "MeridianWest"
    tiff_file.SetField("MeridianWest", -180.0)
    print "MeridianEast"
    tiff_file.SetField("MeridianEast", 180.0)
    print "EarthRadiusLarge"
    tiff_file.SetField("EarthRadiusLarge", 6370000.0)
    print "EarthRadiusSmall"
    tiff_file.SetField("EarthRadiusSmall", 6370000.0)

    ### Write second set of tags ###
    print "GeodeticDate"
    tiff_file.SetField("GeodeticDate", "wgs84")
    print "ReferenceLatitude1"
    tiff_file.SetField("ReferenceLatitude1", 45.0)
    print "ReferenceLatitude2"
    tiff_file.SetField("ReferenceLatitude2", 45.0)
    print "CentralMeridian"
    tiff_file.SetField("CentralMeridian", 0.0)
    print "PhysicValue"
    tiff_file.SetField("PhysicValue", "T")
    print "PhysicUnit"
    tiff_file.SetField("PhysicUnit", "CELSIUS")
    print "MinGrayValue"
    tiff_file.SetField("MinGrayValue", 0)
    print "MaxGrayValue"
    tiff_file.SetField("MaxGrayValue", 255)
    print "Gradient"
    tiff_file.SetField("Gradient", 5.0)
    print "AxisIntercept"
    tiff_file.SetField("AxisIntercept", 0.0)
    print "ColorTable"
    tiff_file.SetField("ColorTable", "some color table")
    print "Description"
    tiff_file.SetField("Description",
                       "this is a fake/test/sample ninjo tiff file")
    print "OverflightDirection"
    tiff_file.SetField("OverflightDirection", "S")
    print "GeoLatitude"
    tiff_file.SetField("GeoLatitude", 0.0)
    print "GeoLongitude"
    tiff_file.SetField("GeoLongitude", 0.0)
    print "Altitude"
    tiff_file.SetField("Altitude", 10000.0)
    print "AOSAzimuth"
    tiff_file.SetField("AOSAzimuth", 180.0)
    print "LOSAzimuth"
    tiff_file.SetField("LOSAzimuth", 180.0)
    print "MaxElevation"
    tiff_file.SetField("MaxElevation", 45.0)
    print "OverFlightTime"
    tiff_file.SetField("OverFlightTime", 1000.0)
    print "IsBlackLinesCorrection"
    tiff_file.SetField("IsBlackLinesCorrection", 0)
    print "IsAtmosphereCorrected"
    tiff_file.SetField("IsAtmosphereCorrected", 0)
    print "IsCalibrated"
    tiff_file.SetField("IsCalibrated", 0)
    print "IsNormalized"
    tiff_file.SetField("IsNormalized", 0)
    print "OriginalHeader"
    tiff_file.SetField("OriginalHeader", "some header")
    #print "IsValueTableAvailable"
    #tiff_file.SetField("IsValueTableAvailable", 0)
    #print "ValueTableStringField"
    #tiff_file.SetField("ValueTableStringField", "Cirrus")
    #print "ValueTableFloatField"
    #tiff_file.SetField("ValueTableFloatField", 0)

    print "Writing image data..."
    tiff_file.write_image(data_array)
    print "SUCCESS"
示例#5
0
def create_ninjo_tiff(image_data, output_fn, **kwargs):
    """Create a NinJo compatible TIFF file with the tags used
    by the DWD's version of NinJo.  Also stores the image as tiles on disk
    and creates a multi-resolution/pyramid/overview set of images
    (deresolution: 2,4,8,16).

    :Parameters:
        image_data : 2D numpy array
            Satellite image data to be put into the NinJo compatible tiff
        output_fn : str
            The name of the TIFF file to be created

    :Keywords:
        data_kind : int
            polar2grid constant describing the sensor type of the
            image data, such as DKIND_REFLECTANCE or DKIND_BTEMP. This is
            optional,
            but if not specified then certain keywords below are required. If
            it is specified then a default can be determined for some of the
            keywords (such as `physic_value`).
        cmap : tuple/list of 3 lists of uint16's
            Individual RGB arrays describing the color value for the
            corresponding data value.  For example, image data with a data
            type of unsigned 8-bit integers have 256 possible values (0-255).
            So each list in cmap will have 256 values ranging from 0 to
            65535 (2**16 - 1). (default linear B&W colormap)
        sat_id : int
            DWD NinJo Satellite ID number
        chan_id : int
            DWD NinJo Satellite Channel ID number
        data_source : str
            String describing where the data came from (SSEC, EUMCAST)
        tile_width : int
            Width of tiles on disk (default 512)
        tile_length : int
            Length of tiles on disk (default 512)
        data_cat : str
            NinJo specific data category
                - data_cat[0] = P (polar) or G (geostat)
                - data_cat[1] = O (original) or P (product)
                - data_cat[2:4] = RN or RB or RA or RN or AN (Raster, Bufr, ASCII, NIL)

            Example: 'P**N' or 'GORN' or 'GPRN' or 'PPRN'
            (default 'P**N')
        pixel_xres : float
            Nadir view pixel resolution in degrees longitude
        pixel_yres : float
            Nadir view pixel resolution in degrees latitude
        origin_lat : float
            Top left corner latitude
        origin_lon : float
            Top left corner longitude
        image_dt : datetime object
            Python datetime object describing the date and time of the image
            data provided in UTC
        projection : str
            NinJo compatible projection name (NPOL,PLAT,etc.)
        meridian_west : float
            Western image border (default 0.0)
        meridian_east : float
            Eastern image border (default 0.0)
        radius_a : float
            Large/equatorial radius of the earth (default <not written>)
        radius_b : float
            Small/polar radius of the earth (default <not written>)
        ref_lat1 : float
            Reference latitude 1 (default <not written>)
        ref_lat2 : float
            Reference latitude 2 (default <not written>)
        central_meridian : float
            Central Meridian (default <not written>)
        physic_value : str
            Physical value type. Examples:
                - Temperature = 'T'
                - Albedo = 'ALBEDO'

            Defaults to appropriate value based on `data_kind`, see `itype2physical`
            Specifying this overrides the default of `itype2physical`. If `data_kind`
            is not specified then this keyword is required.
        physic_unit : str
            Physical value units. Examples:
                - 'CELSIUS'
                - '%'

            Defaults to appropriate value based on `data_kind`, see `itype2physical`
            Specifying this overrides the default of `itype2physical`. If `data_kind`
            is not specified then this keyword is required.
        min_gray_val : int
            Minimum gray value (default 0)
        max_gray_val : int
            Maximum gray value (default 255)
        gradient : float
            Gradient/Slope
            Defaults to appropriate value based on `data_kind`, see `itype2grad`
            Specifying this overrides the default of `itype2grad`. If `data_kind`
            is not specified then this keyword is required.
        axis_intercept : float
            Axis Intercept
            Defaults to appropriate value based on `data_kind`, see `itype2grad`
            Specifying this overrides the default of `itype2grad`. If `data_kind`
            is not specified then this keyword is required.
        altitude : float
            Altitude of the data provided (default 0.0)
        is_atmo_corrected : bool
            Is the data atmosphere corrected? (True/1 for yes) (default False/0)
        is_calibrated : bool
            Is the data calibrated? (True/1 for yes) (default False/0)
        is_normalized : bool
            Is the data normalized (True/1 for yes) (default False/0)
        description : str
            Description string to be placed in the output TIFF (optional)

    :Raises:
        KeyError :
            if required keyword is not provided
    """
    LOG.debug("Creating NinJo TIFF file '%s'" % (output_fn, ))
    out_tiff = TIFF.open(output_fn, "w")

    image_data = clip_to_data_type(image_data, DTYPE_UINT8)

    # Extract keyword arguments
    data_kind = kwargs.pop("data_kind", None)  # called as a backend
    if data_kind is not None and (data_kind not in dkind2physical
                                  or data_kind not in dkind2grad):
        # Must do the check here since it matters when pulling out physic value
        LOG.warning(
            "'data_kind' is not known to the ninjo tiff creator, it will be ignored"
        )
        data_kind = None
    cmap = kwargs.pop("cmap", None)
    sat_id = int(kwargs.pop("sat_id"))
    chan_id = int(kwargs.pop("chan_id"))
    data_source = str(kwargs.pop("data_source"))
    tile_width = int(kwargs.pop("tile_width", 512))
    tile_length = int(kwargs.pop("tile_length", 512))
    data_cat = str(kwargs.pop("data_cat", "P**N"))
    pixel_xres = float(kwargs.pop("pixel_xres"))
    pixel_yres = float(kwargs.pop("pixel_yres"))
    origin_lat = float(kwargs.pop("origin_lat"))
    origin_lon = float(kwargs.pop("origin_lon"))
    image_dt = kwargs.pop("image_dt")
    projection = kwargs.pop("projection")
    meridian_west = float(kwargs.pop("meridian_west", 0.0))
    meridian_east = float(kwargs.pop("meridian_east", 0.0))
    radius_a = kwargs.pop("radius_a", None)
    radius_b = kwargs.pop("radius_b", None)
    ref_lat1 = kwargs.pop("ref_lat1", None)
    ref_lat2 = kwargs.pop("ref_lat2", None)
    central_meridian = kwargs.pop("central_meridian", None)
    min_gray_val = int(kwargs.pop("min_gray_val", 0))
    max_gray_val = int(kwargs.pop("max_gray_val", 255))
    altitude = float(kwargs.pop("altitude", 0.0))
    is_atmo_corrected = int(bool(kwargs.pop("is_atmo_corrected", 0)))
    is_calibrated = int(bool(kwargs.pop("is_calibrated", 0)))
    is_normalized = int(bool(kwargs.pop("is_normalized", 0)))
    description = kwargs.pop("description", None)

    # Special cases
    if data_kind is not None:
        physic_value, physic_unit = dkind2physical[data_kind]
        gradient, axis_intercept = dkind2grad[data_kind]
        physic_value = kwargs.pop("physic_value", physic_value)
        physic_unit = kwargs.pop("physic_unit", physic_unit)
        gradient = float(kwargs.pop("gradient", gradient))
        axis_intercept = float(kwargs.pop("axis_intercept", axis_intercept))
    else:
        physic_value = kwargs.pop("physic_value")
        physic_unit = kwargs.pop("physic_unit")
        gradient = float(kwargs.pop("gradient"))
        axis_intercept = float(kwargs.pop("axis_intercept"))

    # Keyword checks / verification
    if cmap is None:
        if data_kind == "brightness_temperature":
            cmap = get_default_lw_colortable()
        else:
            cmap = get_default_sw_colortable()
    elif len(cmap) != 3:
        LOG.error("Colormap (cmap) must be a list of 3 lists (RGB), not %d" %
                  len(cmap))

    if len(data_cat) != 4:
        LOG.error("NinJo data type must be 4 characters")
        raise ValueError("NinJo data type must be 4 characters")
    if data_cat[0] not in ["P", "G"]:
        LOG.error(
            "NinJo data type's first character must be 'P' or 'G' not '%s'" %
            data_cat[0])
        raise ValueError(
            "NinJo data type's first character must be 'P' or 'G' not '%s'" %
            data_cat[0])
    if data_cat[1] not in ["O", "P"]:
        LOG.error(
            "NinJo data type's second character must be 'O' or 'P' not '%s'" %
            data_cat[1])
        raise ValueError(
            "NinJo data type's second character must be 'O' or 'P' not '%s'" %
            data_cat[1])
    if data_cat[2:4] not in ["RN", "RB", "RA", "BN", "AN"]:
        LOG.error(
            "NinJo data type's last 2 characters must be one of %s not '%s'" %
            ("['RN','RB','RA','BN','AN']", data_cat[2:4]))
        raise ValueError(
            "NinJo data type's last 2 characters must be one of %s not '%s'" %
            ("['RN','RB','RA','BN','AN']", data_cat[2:4]))

    if description is not None and len(description) >= 1000:
        LOG.error("NinJo description must be less than 1000 characters")
        raise ValueError("NinJo description must be less than 1000 characters")

    file_dt = datetime.utcnow()
    file_epoch = calendar.timegm(file_dt.timetuple())
    image_epoch = calendar.timegm(image_dt.timetuple())

    def _write_oneres(image_data, pixel_xres, pixel_yres, subfile=False):
        LOG.debug("Writing tag data for a resolution of the output file '%s'" %
                  (output_fn, ))

        ### Write Tag Data ###
        # Built ins
        out_tiff.SetField("ImageWidth", image_data.shape[1])
        out_tiff.SetField("ImageLength", image_data.shape[0])
        out_tiff.SetField("BitsPerSample", 8)
        out_tiff.SetField("Compression", libtiff.COMPRESSION_LZW)
        out_tiff.SetField("Photometric", libtiff.PHOTOMETRIC_PALETTE)
        out_tiff.SetField("Orientation", libtiff.ORIENTATION_TOPLEFT)
        out_tiff.SetField("SamplesPerPixel", 1)
        out_tiff.SetField("SMinSampleValue", 0)
        out_tiff.SetField("SMaxsampleValue", 255)
        out_tiff.SetField("PlanarConfig", libtiff.PLANARCONFIG_CONTIG)
        out_tiff.SetField("ColorMap", cmap)  # Basic B&W colormap
        out_tiff.SetField("TileWidth", tile_width)
        out_tiff.SetField("TileLength", tile_length)
        out_tiff.SetField("SampleFormat", libtiff.SAMPLEFORMAT_UINT)

        # NinJo specific tags
        if description is not None:
            out_tiff.SetField("Description", description)

        out_tiff.SetField("ModelPixelScale", [pixel_xres, pixel_yres])
        out_tiff.SetField("ModelTiePoint",
                          [0.0, 0.0, 0.0, origin_lon, origin_lat, 0.0])
        out_tiff.SetField("NinjoName", "NINJO")
        out_tiff.SetField("SatelliteNameID", sat_id)
        out_tiff.SetField("DateID", image_epoch)
        out_tiff.SetField("CreationDateID", file_epoch)
        out_tiff.SetField("ChannelID", chan_id)
        out_tiff.SetField("HeaderVersion", 2)
        out_tiff.SetField("FileName", output_fn)
        out_tiff.SetField("DataType", data_cat)
        out_tiff.SetField("SatelliteNumber", "\x00")  # Hardcoded to 0
        out_tiff.SetField("ColorDepth", 8)  # Hardcoded to 8
        out_tiff.SetField("DataSource", data_source)
        out_tiff.SetField("XMinimum", 1)
        out_tiff.SetField("XMaximum", image_data.shape[1])
        out_tiff.SetField("YMinimum", 1)
        out_tiff.SetField("YMaximum", image_data.shape[0])
        out_tiff.SetField("Projection", projection)
        out_tiff.SetField("MeridianWest", meridian_west)
        out_tiff.SetField("MeridianEast", meridian_east)
        if radius_a is not None:
            out_tiff.SetField("EarthRadiusLarge", float(radius_a))
        if radius_b is not None:
            out_tiff.SetField("EarthRadiusSmall", float(radius_b))
        out_tiff.SetField("GeodeticDate", "\x00")  # ---?
        if ref_lat1 is not None:
            out_tiff.SetField("ReferenceLatitude1", ref_lat1)
        if ref_lat2 is not None:
            out_tiff.SetField("ReferenceLatitude2", ref_lat2)
        if central_meridian is not None:
            out_tiff.SetField("CentralMeridian", central_meridian)
        out_tiff.SetField("PhysicValue", physic_value)
        out_tiff.SetField("PhysicUnit", physic_unit)
        out_tiff.SetField("MinGrayValue", min_gray_val)
        out_tiff.SetField("MaxGrayValue", max_gray_val)
        out_tiff.SetField("Gradient", gradient)
        out_tiff.SetField("AxisIntercept", axis_intercept)
        out_tiff.SetField("Altitude", altitude)
        out_tiff.SetField("IsAtmosphereCorrected", is_atmo_corrected)
        out_tiff.SetField("IsCalibrated", is_calibrated)
        out_tiff.SetField("IsNormalized", is_normalized)

        ### Write Base Data Image ###
        out_tiff.write_tiles(image_data)
        out_tiff.WriteDirectory()

    ### Write multi-resolution overviews ###
    out_tiff.SetDirectory(0)
    _write_oneres(image_data, pixel_xres, pixel_yres)
    out_tiff.SetDirectory(1)
    _write_oneres(image_data[::2, ::2], pixel_xres * 2, pixel_yres * 2)
    out_tiff.SetDirectory(2)
    _write_oneres(image_data[::4, ::4], pixel_xres * 4, pixel_yres * 4)
    out_tiff.SetDirectory(3)
    _write_oneres(image_data[::8, ::8], pixel_xres * 8, pixel_yres * 8)
    out_tiff.SetDirectory(4)
    _write_oneres(image_data[::16, ::16], pixel_xres * 16, pixel_yres * 16)
    out_tiff.close()

    LOG.info("Successfully created a NinJo tiff file: '%s'" % (output_fn, ))

    return
示例#6
0
def test_write(*args):
    """Recreate a simple NinJo compatible tiff file from an original
    GOESE example.

    Mainly a test of the tags.
    """
    if len(args) == 0:
        tiff_fn = "test_ninjo.tif"
    else:
        tiff_fn = args[0]

    # Represents original high resolution data array
    #data_array = numpy.zeros((2500,2500), dtype=numpy.uint8)
    data_array = numpy.tile(range(500), (2500,5)).astype(numpy.uint8)
    print data_array.shape

    # Open file
    tiff_file = TIFF.open(tiff_fn, "w")

    ### Write first directory and tags ###
    tiff_file.SetDirectory(0)
    tiff_file.SetField("ImageWidth", 2500)
    tiff_file.SetField("ImageLength", 2500)
    tiff_file.SetField("BITspersample", 8)
    tiff_file.SetField("compression", libtiff.COMPRESSION_LZW)
    #FIXME: Sample file used colormap
    #tiff_file.SetField("PHOTOMETRIC", libtiff.PHOTOMETRIC_MINISBLACK)
    tiff_file.SetField("PHOTOMETRIC", libtiff.PHOTOMETRIC_PALETTE)
    tiff_file.SetField("ORIENTATION", libtiff.ORIENTATION_TOPLEFT)
    tiff_file.SetField("SamplesPerPixel", 1)
    tiff_file.SetField("SMinSampleValue", 0)
    tiff_file.SetField("SMaxsampleValue", 255)
    tiff_file.SetField("Planarconfig", libtiff.PLANARCONFIG_CONTIG)
    #tiff_file.SetField("ColorMap", [ [ x*256 for x in range(256) ],[0]*256,[0]*256 ])
    tiff_file.SetField("ColorMap", [[ x*256 for x in range(256) ]]*3)
    tiff_file.SetField("TILEWIDTH", 512)
    tiff_file.SetField("TILELENGTH", 512)
    tiff_file.SetField("sampleformat", libtiff.SAMPLEFORMAT_UINT)

    ### NINJO SPECIFIC ###
    tiff_file.SetField("ModelPixelScale", [0.071957,0.071957])
    tiff_file.SetField("ModelTiePoint", [0.0, 0.0, 0.0, -164.874313, 89.874321, 0.0])
    tiff_file.SetField("NinjoName", "NINJO")
    tiff_file.SetField("SatelliteNameID", 7300014)
    tiff_file.SetField("DateID", 1337169600)
    tiff_file.SetField("CreationDateID", 1337171130)
    tiff_file.SetField("ChannelID", 900015)
    tiff_file.SetField("HeaderVersion", 2)
    tiff_file.SetField("FileName", "GOESE_AMERIKA_IR107_nq075W8km_1205161200.tif")
    tiff_file.SetField("DataType", "GORN")
    tiff_file.SetField("SatelliteNumber", "\x00") # Hardcoded to 0
    tiff_file.SetField("ColorDepth", 16) #Um 8? hardcoded to 8, but 16?
    tiff_file.SetField("DataSource", "EUMCAST")
    tiff_file.SetField("XMinimum", 1)
    tiff_file.SetField("XMaximum", 2500)
    tiff_file.SetField("YMinimum", 1)
    tiff_file.SetField("YMaximum", 2500)
    tiff_file.SetField("Projection", "PLAT")
    tiff_file.SetField("MeridianWest", 0.0)
    tiff_file.SetField("MeridianEast", 0.0)
    tiff_file.SetField("EarthRadiusLarge", 6370000.0)
    tiff_file.SetField("EarthRadiusSmall", 6370000.0)
    tiff_file.SetField("GeodeticDate", "\x00") # ---?
    tiff_file.SetField("ReferenceLatitude1", 0.0)
    tiff_file.SetField("ReferenceLatitude2", 0.0)
    tiff_file.SetField("CentralMeridian", -75.000)
    tiff_file.SetField("PhysicValue", "T")
    tiff_file.SetField("PhysicUnit", "CELSIUS")
    tiff_file.SetField("MinGrayValue", 0)
    tiff_file.SetField("MaxGrayValue", 255)
    tiff_file.SetField("Gradient", -0.5)
    tiff_file.SetField("AxisIntercept", 40.0)
    tiff_file.SetField("Altitude", 42164.0)
    tiff_file.SetField("IsCalibrated", 1)
    tiff_file.SetField("IsNormalized", 0)

    tiff_file.write_tiles(data_array)
    tiff_file.WriteDirectory()

    ### Directory 2 ###
    #tiff_file.SetDirectory(1)
    #tiff_file.SetField("ModelTiePoint", write_list([0.0, 0.0, 0.0, -164.838348, 89.838341, 0.0], ctypes.c_double))
    #tiff_file.write_image(data_array[::2, ::2])

    return 0
示例#7
0
def test_write_tags(*args):
    """Create a sample NinJo file that writes all ninjo tags and a fake data
    array to a new tiff file.
    """
    if len(args) == 0:
        tiff_fn = "test_ninjo.tif"
    else:
        tiff_fn = args[0]

    # Represents original high resolution data array
    #data_array = numpy.zeros((5,5), dtype=numpy.uint8)
    data_array = numpy.zeros((2500,2500), dtype=numpy.uint8)

    # Open file
    tiff_file = TIFF.open(tiff_fn, "w")
    tiff_file.SetDirectory(0)

    ### Write first set of tags ###
    print "ModelTiePoint"
    tiff_file.SetField("ModelTiePoint", [1,2,3,4,5,6])
    print "ModelPixelScale"
    tiff_file.SetField("ModelPixelScale", [1,2])
    print "TransparentPixel"
    tiff_file.SetField("TransparentPixel", 1)
    print "NinjoName"
    tiff_file.SetField("NinjoName", "NINJO")
    print "SatelliteNameID"
    tiff_file.SetField("SatelliteNameID", 1234)
    print "DateID"
    tiff_file.SetField("DateID", 1234)
    print "CreationDateID"
    tiff_file.SetField("CreationDateID", 1234)
    print "ChannelID"
    tiff_file.SetField("ChannelID", 1234)
    print "HeaderVersion"
    tiff_file.SetField("HeaderVersion", 2)
    print "Filename"
    tiff_file.SetField("Filename", "a_fake_satellite_file.h5")
    print "DataType"
    tiff_file.SetField("DataType", "P**N")
    print "SatelliteNumber"
    tiff_file.SetField("SatelliteNumber", "7") #?
    print "ColorDepth"
    tiff_file.SetField("ColorDepth", 8)
    print "DataSource"
    tiff_file.SetField("DataSource", "PDUS")
    print "XMinimum"
    tiff_file.SetField("XMinimum", 0)
    print "XMaximum"
    tiff_file.SetField("XMaximum", 2500)
    print "YMinimum"
    tiff_file.SetField("YMinimum", 0)
    print "YMaximum"
    tiff_file.SetField("YMaximum", 2500)
    print "Projection"
    tiff_file.SetField("Projection", "NPOL")
    print "MeridianWest"
    tiff_file.SetField("MeridianWest", -180.0)
    print "MeridianEast"
    tiff_file.SetField("MeridianEast", 180.0)
    print "EarthRadiusLarge"
    tiff_file.SetField("EarthRadiusLarge", 6370000.0)
    print "EarthRadiusSmall"
    tiff_file.SetField("EarthRadiusSmall", 6370000.0)

    ### Write second set of tags ###
    print "GeodeticDate"
    tiff_file.SetField("GeodeticDate", "wgs84")
    print "ReferenceLatitude1"
    tiff_file.SetField("ReferenceLatitude1", 45.0)
    print "ReferenceLatitude2"
    tiff_file.SetField("ReferenceLatitude2", 45.0)
    print "CentralMeridian"
    tiff_file.SetField("CentralMeridian", 0.0)
    print "PhysicValue"
    tiff_file.SetField("PhysicValue", "T")
    print "PhysicUnit"
    tiff_file.SetField("PhysicUnit", "CELSIUS")
    print "MinGrayValue"
    tiff_file.SetField("MinGrayValue", 0)
    print "MaxGrayValue"
    tiff_file.SetField("MaxGrayValue", 255)
    print "Gradient"
    tiff_file.SetField("Gradient", 5.0)
    print "AxisIntercept"
    tiff_file.SetField("AxisIntercept", 0.0)
    print "ColorTable"
    tiff_file.SetField("ColorTable", "some color table")
    print "Description"
    tiff_file.SetField("Description", "this is a fake/test/sample ninjo tiff file")
    print "OverflightDirection"
    tiff_file.SetField("OverflightDirection", "S")
    print "GeoLatitude"
    tiff_file.SetField("GeoLatitude", 0.0)
    print "GeoLongitude"
    tiff_file.SetField("GeoLongitude", 0.0)
    print "Altitude"
    tiff_file.SetField("Altitude", 10000.0)
    print "AOSAzimuth"
    tiff_file.SetField("AOSAzimuth", 180.0)
    print "LOSAzimuth"
    tiff_file.SetField("LOSAzimuth", 180.0)
    print "MaxElevation"
    tiff_file.SetField("MaxElevation", 45.0)
    print "OverFlightTime"
    tiff_file.SetField("OverFlightTime", 1000.0)
    print "IsBlackLinesCorrection"
    tiff_file.SetField("IsBlackLinesCorrection", 0)
    print "IsAtmosphereCorrected"
    tiff_file.SetField("IsAtmosphereCorrected", 0)
    print "IsCalibrated"
    tiff_file.SetField("IsCalibrated", 0)
    print "IsNormalized"
    tiff_file.SetField("IsNormalized", 0)
    print "OriginalHeader"
    tiff_file.SetField("OriginalHeader", "some header")
    #print "IsValueTableAvailable"
    #tiff_file.SetField("IsValueTableAvailable", 0)
    #print "ValueTableStringField"
    #tiff_file.SetField("ValueTableStringField", "Cirrus")
    #print "ValueTableFloatField"
    #tiff_file.SetField("ValueTableFloatField", 0)

    print "Writing image data..."
    tiff_file.write_image(data_array)
    print "SUCCESS"
示例#8
0
def create_ninjo_tiff(image_data, output_fn, **kwargs):
    """Create a NinJo compatible TIFF file with the tags used
    by the DWD's version of NinJo.  Also stores the image as tiles on disk
    and creates a multi-resolution/pyramid/overview set of images
    (deresolution: 2,4,8,16).

    :Parameters:
        image_data : 2D numpy array
            Satellite image data to be put into the NinJo compatible tiff
        output_fn : str
            The name of the TIFF file to be created

    :Keywords:
        data_kind : int
            polar2grid constant describing the sensor type of the
            image data, such as DKIND_REFLECTANCE or DKIND_BTEMP. This is
            optional,
            but if not specified then certain keywords below are required. If
            it is specified then a default can be determined for some of the
            keywords (such as `physic_value`).
        cmap : tuple/list of 3 lists of uint16's
            Individual RGB arrays describing the color value for the
            corresponding data value.  For example, image data with a data
            type of unsigned 8-bit integers have 256 possible values (0-255).
            So each list in cmap will have 256 values ranging from 0 to
            65535 (2**16 - 1). (default linear B&W colormap)
        sat_id : int
            DWD NinJo Satellite ID number
        chan_id : int
            DWD NinJo Satellite Channel ID number
        data_source : str
            String describing where the data came from (SSEC, EUMCAST)
        tile_width : int
            Width of tiles on disk (default 512)
        tile_length : int
            Length of tiles on disk (default 512)
        data_cat : str
            NinJo specific data category
                - data_cat[0] = P (polar) or G (geostat)
                - data_cat[1] = O (original) or P (product)
                - data_cat[2:4] = RN or RB or RA or RN or AN (Raster, Bufr, ASCII, NIL)

            Example: 'P**N' or 'GORN' or 'GPRN' or 'PPRN'
            (default 'P**N')
        pixel_xres : float
            Nadir view pixel resolution in degrees longitude
        pixel_yres : float
            Nadir view pixel resolution in degrees latitude
        origin_lat : float
            Top left corner latitude
        origin_lon : float
            Top left corner longitude
        image_dt : datetime object
            Python datetime object describing the date and time of the image
            data provided in UTC
        projection : str
            NinJo compatible projection name (NPOL,PLAT,etc.)
        meridian_west : float
            Western image border (default 0.0)
        meridian_east : float
            Eastern image border (default 0.0)
        radius_a : float
            Large/equatorial radius of the earth (default <not written>)
        radius_b : float
            Small/polar radius of the earth (default <not written>)
        ref_lat1 : float
            Reference latitude 1 (default <not written>)
        ref_lat2 : float
            Reference latitude 2 (default <not written>)
        central_meridian : float
            Central Meridian (default <not written>)
        physic_value : str
            Physical value type. Examples:
                - Temperature = 'T'
                - Albedo = 'ALBEDO'

            Defaults to appropriate value based on `data_kind`, see `itype2physical`
            Specifying this overrides the default of `itype2physical`. If `data_kind`
            is not specified then this keyword is required.
        physic_unit : str
            Physical value units. Examples:
                - 'CELSIUS'
                - '%'

            Defaults to appropriate value based on `data_kind`, see `itype2physical`
            Specifying this overrides the default of `itype2physical`. If `data_kind`
            is not specified then this keyword is required.
        min_gray_val : int
            Minimum gray value (default 0)
        max_gray_val : int
            Maximum gray value (default 255)
        gradient : float
            Gradient/Slope
            Defaults to appropriate value based on `data_kind`, see `itype2grad`
            Specifying this overrides the default of `itype2grad`. If `data_kind`
            is not specified then this keyword is required.
        axis_intercept : float
            Axis Intercept
            Defaults to appropriate value based on `data_kind`, see `itype2grad`
            Specifying this overrides the default of `itype2grad`. If `data_kind`
            is not specified then this keyword is required.
        altitude : float
            Altitude of the data provided (default 0.0)
        is_atmo_corrected : bool
            Is the data atmosphere corrected? (True/1 for yes) (default False/0)
        is_calibrated : bool
            Is the data calibrated? (True/1 for yes) (default False/0)
        is_normalized : bool
            Is the data normalized (True/1 for yes) (default False/0)
        description : str
            Description string to be placed in the output TIFF (optional)

    :Raises:
        KeyError :
            if required keyword is not provided
    """
    LOG.debug("Creating NinJo TIFF file '%s'" % (output_fn,))
    out_tiff = TIFF.open(output_fn, "w")

    image_data = clip_to_data_type(image_data, DTYPE_UINT8)

    # Extract keyword arguments
    data_kind = kwargs.pop("data_kind", None) # called as a backend
    if data_kind is not None and (data_kind not in dkind2physical or data_kind not in dkind2grad):
        # Must do the check here since it matters when pulling out physic value
        LOG.warning("'data_kind' is not known to the ninjo tiff creator, it will be ignored")
        data_kind = None
    cmap = kwargs.pop("cmap", None)
    sat_id = int(kwargs.pop("sat_id"))
    chan_id = int(kwargs.pop("chan_id"))
    data_source = str(kwargs.pop("data_source"))
    tile_width = int(kwargs.pop("tile_width", 512))
    tile_length = int(kwargs.pop("tile_length", 512))
    data_cat = str(kwargs.pop("data_cat", "P**N"))
    pixel_xres = float(kwargs.pop("pixel_xres"))
    pixel_yres = float(kwargs.pop("pixel_yres"))
    origin_lat = float(kwargs.pop("origin_lat"))
    origin_lon = float(kwargs.pop("origin_lon"))
    image_dt = kwargs.pop("image_dt")
    projection = kwargs.pop("projection")
    meridian_west = float(kwargs.pop("meridian_west", 0.0))
    meridian_east = float(kwargs.pop("meridian_east", 0.0))
    radius_a = kwargs.pop("radius_a", None)
    radius_b = kwargs.pop("radius_b", None)
    ref_lat1 = kwargs.pop("ref_lat1", None)
    ref_lat2 = kwargs.pop("ref_lat2", None)
    central_meridian = kwargs.pop("central_meridian", None)
    min_gray_val = int(kwargs.pop("min_gray_val", 0))
    max_gray_val = int(kwargs.pop("max_gray_val", 255))
    altitude = float(kwargs.pop("altitude", 0.0))
    is_atmo_corrected = int(bool(kwargs.pop("is_atmo_corrected", 0)))
    is_calibrated = int(bool(kwargs.pop("is_calibrated", 0)))
    is_normalized = int(bool(kwargs.pop("is_normalized", 0)))
    description = kwargs.pop("description", None)

    # Special cases
    if data_kind is not None:
        physic_value,physic_unit = dkind2physical[data_kind]
        gradient,axis_intercept = dkind2grad[data_kind]
        physic_value = kwargs.pop("physic_value", physic_value)
        physic_unit = kwargs.pop("physic_unit", physic_unit)
        gradient = float(kwargs.pop("gradient", gradient))
        axis_intercept = float(kwargs.pop("axis_intercept", axis_intercept))
    else:
        physic_value = kwargs.pop("physic_value")
        physic_unit = kwargs.pop("physic_unit")
        gradient = float(kwargs.pop("gradient"))
        axis_intercept = float(kwargs.pop("axis_intercept"))

    # Keyword checks / verification
    if cmap is None:
        if data_kind == "brightness_temperature":
            cmap = get_default_lw_colortable()
        else:
            cmap = get_default_sw_colortable()
    elif len(cmap) != 3:
        LOG.error("Colormap (cmap) must be a list of 3 lists (RGB), not %d" % len(cmap))

    if len(data_cat) != 4:
        LOG.error("NinJo data type must be 4 characters")
        raise ValueError("NinJo data type must be 4 characters")
    if data_cat[0] not in ["P", "G"]:
        LOG.error("NinJo data type's first character must be 'P' or 'G' not '%s'" % data_cat[0])
        raise ValueError("NinJo data type's first character must be 'P' or 'G' not '%s'" % data_cat[0])
    if data_cat[1] not in ["O", "P"]:
        LOG.error("NinJo data type's second character must be 'O' or 'P' not '%s'" % data_cat[1])
        raise ValueError("NinJo data type's second character must be 'O' or 'P' not '%s'" % data_cat[1])
    if data_cat[2:4] not in ["RN","RB","RA","BN","AN"]:
        LOG.error("NinJo data type's last 2 characters must be one of %s not '%s'" % ("['RN','RB','RA','BN','AN']", data_cat[2:4]))
        raise ValueError("NinJo data type's last 2 characters must be one of %s not '%s'" % ("['RN','RB','RA','BN','AN']", data_cat[2:4]))

    if description is not None and len(description) >= 1000:
        LOG.error("NinJo description must be less than 1000 characters")
        raise ValueError("NinJo description must be less than 1000 characters")

    file_dt = datetime.utcnow()
    file_epoch = calendar.timegm(file_dt.timetuple())
    image_epoch = calendar.timegm(image_dt.timetuple())

    def _write_oneres(image_data, pixel_xres, pixel_yres, subfile=False):
        LOG.debug("Writing tag data for a resolution of the output file '%s'" % (output_fn,))

        ### Write Tag Data ###
        # Built ins
        out_tiff.SetField("ImageWidth", image_data.shape[1])
        out_tiff.SetField("ImageLength", image_data.shape[0])
        out_tiff.SetField("BitsPerSample", 8)
        out_tiff.SetField("Compression", libtiff.COMPRESSION_LZW)
        out_tiff.SetField("Photometric", libtiff.PHOTOMETRIC_PALETTE)
        out_tiff.SetField("Orientation", libtiff.ORIENTATION_TOPLEFT)
        out_tiff.SetField("SamplesPerPixel", 1)
        out_tiff.SetField("SMinSampleValue", 0)
        out_tiff.SetField("SMaxsampleValue", 255)
        out_tiff.SetField("PlanarConfig", libtiff.PLANARCONFIG_CONTIG)
        out_tiff.SetField("ColorMap", cmap) # Basic B&W colormap
        out_tiff.SetField("TileWidth", tile_width)
        out_tiff.SetField("TileLength", tile_length)
        out_tiff.SetField("SampleFormat", libtiff.SAMPLEFORMAT_UINT)

        # NinJo specific tags
        if description is not None:
            out_tiff.SetField("Description", description)

        out_tiff.SetField("ModelPixelScale", [pixel_xres,pixel_yres])
        out_tiff.SetField("ModelTiePoint", [0.0,  0.0, 0.0, origin_lon, origin_lat, 0.0])
        out_tiff.SetField("NinjoName", "NINJO")
        out_tiff.SetField("SatelliteNameID", sat_id)
        out_tiff.SetField("DateID", image_epoch)
        out_tiff.SetField("CreationDateID", file_epoch)
        out_tiff.SetField("ChannelID", chan_id)
        out_tiff.SetField("HeaderVersion", 2)
        out_tiff.SetField("FileName", output_fn)
        out_tiff.SetField("DataType", data_cat)
        out_tiff.SetField("SatelliteNumber", "\x00") # Hardcoded to 0
        out_tiff.SetField("ColorDepth", 8) # Hardcoded to 8
        out_tiff.SetField("DataSource", data_source)
        out_tiff.SetField("XMinimum", 1)
        out_tiff.SetField("XMaximum", image_data.shape[1])
        out_tiff.SetField("YMinimum", 1)
        out_tiff.SetField("YMaximum", image_data.shape[0])
        out_tiff.SetField("Projection", projection)
        out_tiff.SetField("MeridianWest", meridian_west)
        out_tiff.SetField("MeridianEast", meridian_east)
        if radius_a is not None:
            out_tiff.SetField("EarthRadiusLarge", float(radius_a))
        if radius_b is not None:
            out_tiff.SetField("EarthRadiusSmall", float(radius_b))
        out_tiff.SetField("GeodeticDate", "\x00") # ---?
        if ref_lat1 is not None:
            out_tiff.SetField("ReferenceLatitude1", ref_lat1)
        if ref_lat2 is not None:
            out_tiff.SetField("ReferenceLatitude2", ref_lat2)
        if central_meridian is not None:
            out_tiff.SetField("CentralMeridian", central_meridian)
        out_tiff.SetField("PhysicValue", physic_value) 
        out_tiff.SetField("PhysicUnit", physic_unit)
        out_tiff.SetField("MinGrayValue", min_gray_val)
        out_tiff.SetField("MaxGrayValue", max_gray_val)
        out_tiff.SetField("Gradient", gradient)
        out_tiff.SetField("AxisIntercept", axis_intercept)
        out_tiff.SetField("Altitude", altitude)
        out_tiff.SetField("IsAtmosphereCorrected", is_atmo_corrected)
        out_tiff.SetField("IsCalibrated", is_calibrated)
        out_tiff.SetField("IsNormalized", is_normalized)

        ### Write Base Data Image ###
        out_tiff.write_tiles(image_data)
        out_tiff.WriteDirectory()

    ### Write multi-resolution overviews ###
    out_tiff.SetDirectory(0)
    _write_oneres(image_data, pixel_xres, pixel_yres)
    out_tiff.SetDirectory(1)
    _write_oneres(image_data[::2,::2], pixel_xres*2, pixel_yres*2)
    out_tiff.SetDirectory(2)
    _write_oneres(image_data[::4,::4], pixel_xres*4, pixel_yres*4)
    out_tiff.SetDirectory(3)
    _write_oneres(image_data[::8,::8], pixel_xres*8, pixel_yres*8)
    out_tiff.SetDirectory(4)
    _write_oneres(image_data[::16,::16], pixel_xres*16, pixel_yres*16)
    out_tiff.close()

    LOG.info("Successfully created a NinJo tiff file: '%s'" % (output_fn,))

    return
    def read_image(self, verbose=False, as3d=True):
        """Read image from TIFF and return it as a numpy array.
           if as3d is passed True (default), will attempt to read 
           multiple directories, and restore as slices in a 3D array.
        """
        if not as3d:
            return TIFF.read_image(self, verbose)

        # Code is initially copy-paste from TIFF:
        width = self.GetField("ImageWidth")
        height = self.GetField("ImageLength")
        bits = self.GetField("BitsPerSample")
        sample_format = self.GetField("SampleFormat")
        compression = self.GetField("Compression")

        typ = self.get_numpy_type(bits, sample_format)

        if typ is None:
            if bits == 1:
                typ = np.uint8
                itemsize = 1
            elif bits == 4:
                typ = np.uint32
                itemsize = 4
            else:
                raise NotImplementedError(` bits `)
        else:
            itemsize = bits / 8

        # in order to allocate the numpy array, we must count the directories:
        # code borrowed from TIFF.iter_images():
        depth = 0
        while True:
            depth += 1
            if self.LastDirectory():
                break
            self.ReadDirectory()
        self.SetDirectory(0)

        # we proceed assuming all directories have the same properties from above.
        layer_size = width * height * itemsize
        total_size = layer_size * depth
        arr = np.zeros((depth, height, width), typ)

        if compression == COMPRESSION_NONE:
            ReadStrip = self.ReadRawStrip
        else:
            ReadStrip = self.ReadEncodedStrip

        layer = 0
        while True:
            pos = 0
            elem = None
            for strip in range(self.NumberOfStrips()):
                if elem is None:
                    elem = ReadStrip(strip, arr.ctypes.data + layer * layer_size + pos, layer_size)
                elif elem:
                    elem = ReadStrip(strip, arr.ctypes.data + layer * layer_size + pos, min(layer_size - pos, elem))
                pos += elem
            if self.LastDirectory():
                break
            self.ReadDirectory()
            layer += 1
        self.SetDirectory(0)
        return arr