def stream_to_array(stream, width, height, color_type, bit_depth, interlace=0): # `stream` is 1-d numpy array with dytpe np.uint8 containing the # data from one or more IDAT or fdAT chunks. # # This function converts `stream` to a numpy array. img_color_dim, img_dtype = img_color_format(color_type, bit_depth) if img_color_dim == 1: img_shape = (height, width) else: img_shape = (height, width, img_color_dim) img = np.empty(img_shape, dtype=img_dtype) if interlace == 1: passes = numpngw._interlace_passes(img) else: passes = [img] pass_start_index = 0 for a in passes: if a.size == 0: continue pass_height, pass_width = a.shape[:2] ncols, rembits = divmod(pass_width*bit_depth, 8) ncols += rembits > 0 if bit_depth < 8: bytes_per_pixel = 1 # Not really, but we need 1 here for later. data_bytes_per_line = ncols else: # nchannels is a map from color_type to the number of color # channels (e.g. an RGB image has three channels). nchannels = {0: 1, 2: 3, 3: 1, 4: 2, 6: 4} bytes_per_channel = bit_depth // 8 bytes_per_pixel = bytes_per_channel * nchannels[color_type] data_bytes_per_line = bytes_per_pixel * pass_width data_width = data_bytes_per_line // bytes_per_pixel pass_end_index = (pass_start_index + pass_height * (data_bytes_per_line + 1)) shp = (pass_height, data_bytes_per_line + 1) lines = stream[pass_start_index:pass_end_index].reshape(shp) pass_start_index = pass_end_index prev = np.zeros((data_width, bytes_per_pixel), dtype=np.uint8) shp = (pass_height, data_width, bytes_per_pixel) p = np.empty(shp, dtype=np.uint8) for k in range(lines.shape[0]): line_filter_type = lines[k, 0] filtered = lines[k, 1:].reshape(-1, bytes_per_pixel) if line_filter_type == 0: p[k] = filtered elif line_filter_type == 1: p[k] = numpngw._filter1inv(filtered, prev) elif line_filter_type == 2: p[k] = numpngw._filter2inv(filtered, prev) elif line_filter_type == 3: p[k] = numpngw._filter3inv(filtered, prev) elif line_filter_type == 4: p[k] = numpngw._filter4inv(filtered, prev) else: raise ValueError('invalid filter type: %i' % (line_filter_type,)) prev = p[k] # As this point, p has data type uint8 and has shape # (height, width, bytes_per_pixel). # 16 bit components of the pixel are stored in big-endian format. uint8to16 = np.array([256, 1], dtype=np.uint16) if color_type == 0: # grayscale if bit_depth == 16: pass_img = p.dot(uint8to16) elif bit_depth == 8: pass_img = p[:, :, 0] else: # bit_depth is 1, 2 or 4. pass_img = numpngw._unpack(p.reshape(pass_height, -1), bitdepth=bit_depth, width=pass_width) elif color_type == 2: # RGB if bit_depth == 16: # Combine high and low bytes to 16-bit values. shp = (pass_height, pass_width, 3, 2) pass_img = p.reshape(shp).dot(uint8to16) else: # bit_depth is 8. pass_img = p elif color_type == 3: # indexed if bit_depth < 8: pass_img = numpngw._unpack(p[:, :, 0], bitdepth=bit_depth, width=pass_width) else: pass_img = p[:, :, 0] elif color_type == 4: # grayscale with alpha if bit_depth == 16: # Combine high and low bytes to 16-bit values. shp = (pass_height, pass_width, 2, 2) pass_img = p.reshape(shp).dot(uint8to16) else: # bit_depth is 8. pass_img = p elif color_type == 6: # RGBA if bit_depth == 16: # Combine high and low bytes to 16-bit values. shp = (pass_height, pass_width, 4, 2) pass_img = p.reshape(shp).dot(uint8to16) else: # bit_depth is 8. pass_img = p else: raise RuntimeError('invalid color type %r' % (color_type,)) a[...] = pass_img return img
def stream_to_array_old(stream, width, height, color_type, bit_depth): # `stream` is 1-d numpy array with dytpe np.uint8 containing the # data from one or more IDAT or fdAT chunks. # # This function converts `stream` to a numpy array. ncols, rembits = divmod(width*bit_depth, 8) ncols += rembits > 0 if bit_depth < 8: bytes_per_pixel = 1 # Not really, but we need 1 here for later. data_bytes_per_line = ncols else: # nchannels is a map from color_type to the number of color # channels (e.g. an RGB image has three channels). nchannels = {0: 1, 2: 3, 3: 1, 4: 2, 6: 4} bytes_per_channel = bit_depth // 8 bytes_per_pixel = bytes_per_channel * nchannels[color_type] data_bytes_per_line = bytes_per_pixel * width data_width = data_bytes_per_line / bytes_per_pixel lines = stream.reshape(height, data_bytes_per_line + 1) prev = np.zeros((data_width, bytes_per_pixel), dtype=np.uint8) p = np.empty((height, data_width, bytes_per_pixel), dtype=np.uint8) for k in range(lines.shape[0]): line_filter_type = lines[k, 0] filtered = lines[k, 1:].reshape(-1, bytes_per_pixel) if line_filter_type == 0: p[k] = filtered elif line_filter_type == 1: p[k] = numpngw._filter1inv(filtered, prev) elif line_filter_type == 2: p[k] = numpngw._filter2inv(filtered, prev) elif line_filter_type == 3: p[k] = numpngw._filter3inv(filtered, prev) elif line_filter_type == 4: p[k] = numpngw._filter4inv(filtered, prev) else: raise ValueError('invalid filter type: %i' % (line_filter_type,)) prev = p[k] # As this point, p has data type uint8 and has shape # (height, width, bytes_per_pixel). # 16 bit components of the pixel are stored in big-endian format. uint8to16 = np.array([256, 1], dtype=np.uint16) if color_type == 0: # grayscale if bit_depth == 16: img = p.dot(uint8to16) elif bit_depth == 8: img = p[:, :, 0] else: # bit_depth is 1, 2 or 4. img = numpngw._unpack(p.reshape(height, -1), bitdepth=bit_depth, width=width) elif color_type == 2: # RGB if bit_depth == 16: # Combine high and low bytes to 16-bit values. img = p.reshape(height, width, 3, 2).dot(uint8to16) else: # bit_depth is 8. img = p elif color_type == 3: # indexed img = p[:, :, 0] elif color_type == 4: # grayscale with alpha if bit_depth == 16: # Combine high and low bytes to 16-bit values. img = p.reshape(height, width, 2, 2).dot(uint8to16) else: # bit_depth is 8. img = p elif color_type == 6: # RGBA if bit_depth == 16: # Combine high and low bytes to 16-bit values. img = p.reshape(height, width, 4, 2).dot(uint8to16) else: # bit_depth is 8. img = p else: raise RuntimeError('invalid color type %r' % (color_type,)) return img