in src/opencvinterpolationframe.cpp [122:272]
void OpenCVInterpolationFrame::sliceFrame() {
// Downsamples a rectangular region a layer of a SVS and compresses frame
// output.
// Allocate memory to retrieve layer data from openslide
std::unique_ptr<uint32_t[]> buf_bytes = std::make_unique<uint32_t[]>(
static_cast<size_t>((frameWidthDownsampled_ + padWidth_) *
(frameHeightDownsampled_ + padHeight_)));
// If progressively downsampling then dcmFrameRegionReader_ contains
// previous level downsample files and their assocciated frames. If no
// files are contained, either the highest resolution image is being
// downsampled or progressive downsampling is not being used. If this is
// the case the image is retrieved using openslide.
const bool dcmFrameRegionReaderNotInitalized =
dcmFrameRegionReader_->dicomFileCount() == 0;
if (dcmFrameRegionReaderNotInitalized) {
// Open slide API samples using xy coordinages from level 0 image.
// upsample coordinates to level 0 to compute sampleing site.
const int64_t Level0_x = ((locationX_ - padLeft_) * level0Width_) /
levelWidth_;
const int64_t Level0_y = ((locationY_ - padTop_) * level0Height_) /
levelHeight_;
// Open slide read region returns ARGB formated pixels
// Values are pre-multiplied with alpha
// https://github.com/openslide/openslide/wiki/PremultipliedARGB
openslide_read_region(osptr_->osr(), buf_bytes.get(), Level0_x,
Level0_y, level_,
frameWidthDownsampled_ + padWidth_,
frameHeightDownsampled_ + padHeight_);
if (openslide_get_error(osptr_->osr())) {
BOOST_LOG_TRIVIAL(error) << openslide_get_error(osptr_->osr());
throw 1;
}
// Uncommon, openslide C++ API premults RGB by alpha.
// if alpha is not zero reverse transform to get RGB
// https://openslide.org/api/openslide_8h.html
const int yend = frameHeightDownsampled_ + padHeight_;
int yoffset = 0;
for (uint32_t y = 0; y < yend; ++y) {
const int xend = frameWidthDownsampled_ + padWidth_ + yoffset;
for (uint32_t x = yoffset; x < xend; ++x) {
const uint32_t pixel = buf_bytes[x]; // Pixel value to be downsampled
const int alpha = pixel >> 24; // Alpha value of pixel
if (alpha == 0) { // If transparent skip
continue;
}
uint32_t red = (pixel >> 16) & 0xFF; // Get RGB Bytes
uint32_t green = (pixel >> 8) & 0xFF;
uint32_t blue = pixel & 0xFF;
// Uncommon, openslide C++ API premults RGB by alpha.
// if alpha is not zero reverse transform to get RGB
// https://openslide.org/api/openslide_8h.html
if (alpha != 0xFF) {
red = red * 255 / alpha;
green = green * 255 / alpha;
blue = blue * 255 / alpha;
}
// Swap red and blue channel for dicom compatiability.
buf_bytes[x] = (alpha << 24) | (blue << 16) | (green << 8) | red;
}
yoffset += frameWidthDownsampled_ + padWidth_;
}
} else {
if (!dcmFrameRegionReader_->readRegion(locationX_ - padLeft_,
locationY_ - padTop_,
frameWidthDownsampled_ + padWidth_,
frameHeightDownsampled_ + padHeight_,
buf_bytes.get())) {
BOOST_LOG_TRIVIAL(error) << "Error occured decoding previous level "
"region.";
throw 1;
}
}
const size_t frame_mem_size = static_cast<size_t>(frameWidth_ * frameHeight_);
std::unique_ptr<uint32_t[]> raw_bytes;
if (!resized_) {
// If image is not being resized move memory from buffer to raw_bytes
raw_bytes = std::move(buf_bytes);
} else {
// Initalize OpenCV image with source image bits padded out to
// provide context beyond frame boundry.
cv::Mat source_image(frameHeightDownsampled_+padHeight_,
frameWidthDownsampled_+padWidth_, CV_8UC4,
buf_bytes.get());
cv::Mat resized_image;
const int resize_width = frameWidth_ + (padWidth_ / widthScaleFactor_);
const int resize_height = frameHeight_ + (padHeight_ / heightScaleFactor_);
/*
ResizeFlags
cv::INTER_NEAREST = 0,
cv::INTER_LINEAR = 1,
cv::INTER_CUBIC = 2,
cv::INTER_AREA = 3,
cv::INTER_LANCZOS4 = 4,
cv::INTER_LINEAR_EXACT = 5,
cv::INTER_NEAREST_EXACT = 6,
cv::INTER_MAX = 7,
*/
// Open CV resize image
cv::resize(source_image, resized_image,
cv::Size(resize_width, resize_height), 0, 0,
openCVInterpolationMethod_);
// Copy area of intrest from resized source image to raw bytres buffer
raw_bytes = std::make_unique<uint32_t[]>(frame_mem_size);
const int xstart = padLeft_ / widthScaleFactor_;
const int ystart = padTop_ / heightScaleFactor_;
const int yend = frameHeight_ + ystart;
uint32_t raw_offset = 0;
uint32_t source_yoffset = ystart * resize_width;
uint32_t* resized_source_img =
reinterpret_cast<uint32_t*>(resized_image.data);
for (uint32_t y = ystart; y < yend; ++y) {
const uint32_t xend = frameWidth_+xstart + source_yoffset;
for (uint32_t x = xstart+source_yoffset; x < xend; ++x) {
raw_bytes[raw_offset] = resized_source_img[x];
raw_offset += 1;
}
source_yoffset += resize_width;
}
}
// Create a boot::gil view of memory in downsample_bytes
boost::gil::rgba8c_view_t gil = boost::gil::interleaved_view(
frameWidth_, frameHeight_,
(const boost::gil::rgba8c_pixel_t *)raw_bytes.get(),
frameWidth_ * sizeof(uint32_t));
// Create gil::image to copy bytes into
boost::gil::rgb8_image_t exp(frameWidth_, frameHeight_);
boost::gil::rgb8_view_t rgbView = view(exp);
boost::gil::copy_pixels(gil, rgbView);
// Compress memory (RAW, jpeg, or jpeg2000)
uint64_t size;
std::unique_ptr<uint8_t[]>mem = std::move(compressor_->compress(rgbView,
&size));
setDicomFrameBytes(std::move(mem), size);
if (!storeRawBytes_) {
rawCompressedBytes_ = nullptr;
rawCompressedBytesSize_ = 0;
} else {
rawCompressedBytes_ = std::move(compress_memory(
reinterpret_cast<uint8_t*>(raw_bytes.get()),
frame_mem_size * sizeof(uint32_t),
&rawCompressedBytesSize_));
}
done_ = true;
}