Export image

The Export image step saves the converted BitmapImage with additional metadata information into a standard file format.

After the Convert pipeline (see Convert Pipeline) produced a BitmapImage data, one might want to store the result on the file system as part of a delivery, or to further process the image with another application. This step is meant for this use case.

The current version supports the following output formats:

  • Joint Photographic Experts Group (JPEG) format

  • JPEG 2000 (JP2) format

  • Tag Image File Format (TIFF) with GeoTIFF extension

  • PGM Image File Format (PGM) - a textual greyscale image format

Exporting into Joint Photographic Experts Group (JPEG) format

To export into JPEG format one must obtain both the original RawImage and the produced BitmapImage along with the JpegConfig configuring the output file.

P1::ImageSdk::RawImage rawImage;
P1::ImageSdk::BitmapImage bitmap;
P1::ImageSdk::JpegConfig config;
std::string filename;

config.compression = 80;

P1::ImageSdk::JpegWriter jpegWriter(filename, bitmap, rawImage, config);
P1.ImageSdk.RawImage rawImage;
P1.ImageSdk.BitmapImage bitmap;
P1.ImageSdk.JpegConfig config;
System.String filename;

config.compression = 80;

rawImage.WriteAsJpeg(filename, bitmap, config);

Exporting into JPEG 2000 (JP2) format

To export into JP2 format one must obtain both the original RawImage and the produced BitmapImage along with the Jp2Config configuring the output file.

P1::ImageSdk::RawImage rawImage;
P1::ImageSdk::BitmapImage bitmap;
P1::ImageSdk::Jp2Config config;
std::string filename;

P1::ImageSdk::Jp2Writer jp2Writer(filename, bitmap, rawImage, config);
P1.ImageSdk.RawImage rawImage;
P1.ImageSdk.BitmapImage bitmap;
P1.ImageSdk.Jp2Config config;
System.String filename;

rawImage.WriteAsJp2(filename, bitmap, config);

Exporting into Tag Image File Format (TIFF)

To export into TIFF format one must obtain both the original RawImage and the produced BitmapImage along with the TiffConfig configuring the output file.

P1::ImageSdk::RawImage rawImage;
P1::ImageSdk::BitmapImage bitmap;
P1::ImageSdk::TiffConfig config;
std::string filename;

config.tileSize = P1::ImageSdk::tileSize512;

P1::ImageSdk::TiffWriter tiffWriter(filename, bitmap, rawImage, config);
P1.ImageSdk.RawImage rawImage;
P1.ImageSdk.BitmapImage bitmap;
P1.ImageSdk.TiffConfig config;
System.String filename;

config.tileSize = P1.ImageSdk.TiffTileSize.tileSize512;

rawImage.WriteAsTiff(filename, bitmap, config);

Exporting into PGM Format (PGM)

To export into PGM format one must obtain both the original RawImage and the produced BitmapImage The PGM image format is a textual greyscale format. The greyscale values are found by taking the luminosity value of the Red, Green and Blue channel.

P1::ImageSdk::RawImage image;
P1::ImageSdk::BitmapImage bitmap;
std::string filename;

P1::ImageSdk::PgmWriter(filename, bitmap, image);
P1.ImageSdk.RawImage rawImage;
P1.ImageSdk.BitmapImage bitmap;
P1.ImageSdk.Jp2Config config;
System.String filename;

rawImage.WriteAsPgm(filename, bitmap);

Output color space

By default the ImageSDK will output images in sRGB color space (IEC 61966-2-1). Although this color space has relatively small gamut, it is adequate for most applications. In case the end user needs to output the images in wider color gamut, ImageSDK can produce them in Adobe RGB (1998) color space (IEC 61966-2-5:2007).

config.SetOutputColorSpace(P1::ImageSdk::ColorSpace::adobeRGB);
config.SetOutputColorSpace(P1.ImageSdk.ColorSpace.adobeRGB);

The such converted bitmap needs to be supplemented with an Adobe RGB (1998) color profile so that the writer can output a file which is interpreted correctly by any color managed viewer.


        std::string colorProfileFilename = "Adobe RGB (1998).icm";

		P1::ImageSdk::TiffConfig outputConfig; // The same applies to all other output formats   

		char* colorProfileBuffer = nullptr;
		FILE* fp = fopen(colorProfileFilename.c_str(), "rb");
		if (fp)
		{
			long colorProfileFileLength;
			if (fseek(fp, 0, SEEK_END) == 0 &&
				0 <= (colorProfileFileLength  = ftell(fp)) )
			{
				rewind(fp);

				colorProfileBuffer = new char[colorProfileFileLength];
				if (fread(colorProfileBuffer, 1, colorProfileFileLength, fp) == (size_t)colorProfileFileLength)
				{
					outputConfig.commonConfig.iccProfileData = colorProfileBuffer;
					outputConfig.commonConfig.iccProfileSize = colorProfileFileLength;
				}
				else
				{
					std::cout << "Could not read icc profile" << std::endl;
				}

				fclose(fp);
			}
			else
			{
				std::cout << "Could not find size of icc profile" << std::endl;
			}
		}
		else
		{
			std::cout << "Could not find icc profile" << std::endl;
		}
TiffConfig tiffConfig = new TiffConfig(); // The same applies to all other output formats

tiffConfig.commonConfig.iccProfileData = File.ReadAllBytes("Adobe RGB (1998).icm");

Adding GeoTIFF fields to the metadata

To output a valid GeoTIFF file, the ImageSdk requires a couple of fields to be defined in the TiffConfig structure. A minimum set of required parameters are currently supported.

  • GTRasterTypeGeoKey

  • GTModelTypeGeoKey

  • ModelTiepointTag

  • ModelPixelScaleTag

  • ModelTransformationTag

  • GTCitationGeoKey

See an example of how to add the fields to the configuration.

std::vector< P1::ImageSdk::GeoTiffField > geoTiffFieldList;

// Pixel Scale tag
double pixelScaleArray[3];
if (pixelScaleArray == NULL) return false;
pixelScaleArray[0] = 10.0;  // x
pixelScaleArray[1] = 15.0;  // y
pixelScaleArray[2] = 0.0;   // z
P1::ImageSdk::GeoTiffField pixelScale;
pixelScale.key = P1::ImageSdk::GeoTiffModelPixelScaleTag;
pixelScale.valueType = P1::ImageSdk::GeoTiffValueTypeDouble;
pixelScale.data = pixelScaleArray;
pixelScale.count = 3;
geoTiffFieldList.push_back(pixelScale);

// Tie Point tag
double tiePointArray[6];
tiePointArray[0] = 0.0;   // I (raster space x)
tiePointArray[1] = 5.0;   // J (raster space y)
tiePointArray[2] = 0.0;   // K (pixel value, should be 0)
tiePointArray[3] = 0.0;   // X (model space x)
tiePointArray[4] = 5.0;   // Y (model space y)
tiePointArray[5] = 0.0;   // Z (model space z, should be 0 for 2D model space)
P1::ImageSdk::GeoTiffField tiePoint;
tiePoint.key = P1::ImageSdk::GeoTiffModelTiepointTag;
tiePoint.valueType = P1::ImageSdk::GeoTiffValueTypeDouble;
tiePoint.data = tiePointArray;
tiePoint.count = 6;
geoTiffFieldList.push_back(tiePoint);

// Model Type tag
P1::ImageSdk::GeoTiffField modelType;
modelType.key = P1::ImageSdk::GTModelType;
modelType.valueType = P1::ImageSdk::GeoTiffValueTypeShort;
modelType.count = 0;
modelType.value = 1; // ModelTypeProjected
modelType.data = nullptr;
geoTiffFieldList.push_back(modelType);

// Raster Type tag
P1::ImageSdk::GeoTiffField rasterType;
rasterType.key = P1::ImageSdk::GTRasterType;
rasterType.valueType = P1::ImageSdk::GeoTiffValueTypeShort;
rasterType.count = 0;
rasterType.value = 1; // RasterPixelIsArea
rasterType.data = nullptr;
geoTiffFieldList.push_back(rasterType);

// Citation tag (optional)
char const* citationText = "Test Citation";
auto const citationTextBuffer = (unsigned char*)malloc(strlen(citationText));
if (citationTextBuffer != NULL)
{
    memcpy(citationTextBuffer, citationText, strlen(citationText)); // move the string to the heap so is survives the exiting of the scope

    P1::ImageSdk::GeoTiffField citation;
    citation.key = P1::ImageSdk::GTCitation;
    citation.valueType = P1::ImageSdk::GeoTiffValueTypeAscii;
    citation.data = citationTextBuffer;
    citation.count = strlen(citationText);
    geoTiffFieldList.push_back(citation);
}

P1::ImageSdk::GeoTiffField* const geoTiffFieldListRaw = (P1::ImageSdk::GeoTiffField*)malloc(geoTiffFieldList.size() * sizeof(P1::ImageSdk::GeoTiffField));
if (geoTiffFieldListRaw != NULL)
{
    memcpy(geoTiffFieldListRaw, geoTiffFieldList.data(), geoTiffFieldList.size() * sizeof(GeoTiffField));
    config.commonConfig.geoTiffFieldList = geoTiffFieldListRaw;
    config.commonConfig.geoTiffFieldCount = geoTiffFieldList.size();
}
List<GeoTiffField> geoTiffFieldList = new List<GeoTiffField>();

// Pixel Scale tag
GeoTiffField pixelScale = new GeoTiffField()
{
    key = GeoTiffKey.GeoTiffModelPixelScaleTag,
    doubleArrayValue = new List<double>() { 1.0, 2.5, 0.0 },
};
geoTiffFieldList.Add(pixelScale);

// Tie Point tag
GeoTiffField tiePoint = new GeoTiffField()
{
    key = GeoTiffKey.GeoTiffModelTiepointTag,
    doubleArrayValue = new List<double>() { 0.0, 5.0, 0.0, 1.0, 42.5, 3.14159265 },
};
geoTiffFieldList.Add(tiePoint);

// Model Type tag
GeoTiffField modelType = new GeoTiffField()
{
    key = GeoTiffKey.GTModelType,
    value = 1,
};
geoTiffFieldList.Add(modelType);

// Raster Type tag
GeoTiffField rasterType = new GeoTiffField()
{
    key = GeoTiffKey.GTRasterType,
    value = 1,
};
geoTiffFieldList.Add(rasterType);

// Citation tag (optional)
GeoTiffField citation = new GeoTiffField()
{
    key = GeoTiffKey.GTCitation,
    stringValue = "Test Citation",
};
geoTiffFieldList.Add(citation);

// Add to the configuration
config.commonConfig.geoTiffFieldList = geoTiffFieldList;