Example: Extract image data#
This example covers reading of a LayeredFile (A Photoshop file) after which we access the image layers that contain image data. We additionally cover how we can then extract that image data using the API as std::vector<T> or numpy.ndarray depending on the language
Relevant documentation links:
psapi.ImageLayer_8bit
/*
Example of loading a PhotoshopFile and extracting the image data, this can be freely used with any other operations present on the LayeredFile
*/
#include "PhotoshopAPI.h"
#include <unordered_map>
#include <vector>
int main()
{
using namespace NAMESPACE_PSAPI;
// In this case we already know the bit depth but otherwise one could use the PhotoshopFile.m_Header.m_Depth
// variable on the PhotoshopFile to figure it out programmatically. This would need to be done using the
// "extended" read signature shown in the ExtendedSignature example.
LayeredFile<bpp8_t> layeredFile = LayeredFile<bpp8_t>::read("ImageData.psb");
// We could also use findLayer() on the LayeredFile but this way we directly get the appropriate type
auto img_layer_ptr = find_layer_as<bpp8_t, ImageLayer>("RedLayer", layeredFile);
// We can now either extract just the channels we want:
std::vector<bpp8_t> channel_r = img_layer_ptr->get_channel(Enum::ChannelID::Red);
std::vector<bpp8_t> channel_g = img_layer_ptr->get_channel(Enum::ChannelID::Green);
std::vector<bpp8_t> channel_b = img_layer_ptr->get_channel(Enum::ChannelID::Blue);
// or extract all the channels as one unordered_map:
auto channels = img_layer_ptr->get_image_data();
// over which we could loop like this:
for (auto& [key, value] : channels)
{
// key is a Enum::ChannelIDInfo object where the .id is a Enum::ChannelID
// and .index is an unsigned integer
// value is simply a vector of type T, in our case a std::vector<bpp8_t>
// This will also extract the channel mask if it exists
}
// If we want to extract e.g. the layer mask:
auto mask_img_layer_ptr = find_layer_as<bpp8_t, ImageLayer>("Group/EmptyLayerWithMask", layeredFile);
// If this doesnt have a mask channel we will simply get an empty channel. In this case though, even though
// we have a mask it will be empty as well as Photoshop fills in the gaps in the layer with the mask_default_color
// parameter.
std::vector<bpp8_t> channel_mask = mask_img_layer_ptr->get_mask();
// To extract this default color we can do this:
if (mask_img_layer_ptr->has_mask())
{
// This value is always uint8_t even for 16- and 32- bit files!
uint8_t default_color = mask_img_layer_ptr->mask_default_color();
}
// This would tell us that we have an empty white layer mask with no pixel values.
// One can however write out explicit zeroes for mask channels or set a default color
}
# Example of creating a simple document with a single layer using the PhotoshopAPI.
import os
import numpy as np
import photoshopapi as psapi
def main() -> None:
# In the python bindings we expose a wrapper which reads a LayeredFile instance with the
# correct bit-depth
layered_file = psapi.LayeredFile.read(os.path.join(os.path.dirname(__file__), "ImageData.psb"))
# We can verify this by printing the type of layered_file
print(type(layered_file)) # <--- Will print psapi.LayeredFile_8bit
# One massive advantage is that we can use dict-like indexing to retrieve the layers
img_layer: psapi.ImageLayer_8bit = layered_file["RedLayer"]
# and then again for channels
channel_r = img_layer[0]
channel_g = img_layer[1]
channel_b = img_layer[2]
print(f"Extracted image data: {channel_r}, {channel_g}, {channel_b}")
# or if we just want all the data as dict
img_data: dict[int, np.ndarray] = img_layer.get_image_data()
print(f"Image data retrieved as dict: {img_data}")
# Similarly we can also extract masks in many different ways
mask_layer: psapi.ImageLayer_8bit = layered_file["Group"]["EmptyLayerWithMask"]
mask_data = mask_layer[-2] # -2 is the index for mask channels
mask_data = mask_layer.get_channel_by_id(psapi.enum.ChannelID.mask) # If we want to be more explicit
mask_data = mask_layer.mask
# Dont worry if the mask data shows up as empty since photoshop has optimized this mask channel
# away due to it being full white. The `mask_default_color` property will hold the pixel value applied
# outside of the masks bounding box
# There is also no reason to actually store the mask layer, we can simply chain indexing calls
mask_data = layered_file["Group"]["EmptyLayerWithMask"][-2]
# Now we already knew all the layers' names but if we wish to iterate we can do that too
for layer in layered_file.layers:
if isinstance(layer, psapi.GroupLayer_8bit):
# We could now recurse down to get all the levels
print(f"Found group layer: {layer.name}")
print(f"And its sub-layers: {layer.layers}")
if __name__ == "__main__":
main()