Skip to content

Commit

Permalink
Integrate eldak/refCountingPrototype into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Project Philly committed Feb 19, 2016
2 parents a053024 + c916cce commit fb47c8d
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 212 deletions.
137 changes: 66 additions & 71 deletions Source/Readers/ImageReader/ImageDataDeserializer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@
#include "ImageDataDeserializer.h"
#include "ImageConfigHelper.h"

#ifndef UNREFERENCED_PARAMETER
#define UNREFERENCED_PARAMETER(P) (P)
#endif

namespace Microsoft { namespace MSR { namespace CNTK {

class ImageDataDeserializer::LabelGenerator
Expand Down Expand Up @@ -45,6 +41,65 @@ class TypedLabelGenerator : public ImageDataDeserializer::LabelGenerator
TElement m_value;
};

// Used to keep track of the image. Accessed only using DenseSequenceData interface.
struct DeserializedImage : DenseSequenceData
{
cv::Mat m_image;
};

// For image, chunks correspond to a single image.
class ImageDataDeserializer::ImageChunk : public Chunk, public std::enable_shared_from_this<ImageChunk>
{
ImageSequenceDescription m_description;
ImageDataDeserializer& m_parent;

public:
ImageChunk(ImageSequenceDescription& description, ImageDataDeserializer& parent)
: m_description(description), m_parent(parent)
{
}

virtual std::vector<SequenceDataPtr> GetSequence(const size_t& sequenceId) override
{
assert(sequenceId == m_description.m_id);
UNUSED(sequenceId);
const auto& imageSequence = m_description;

auto image = std::make_shared<DeserializedImage>();
image->m_image = std::move(cv::imread(imageSequence.m_path, cv::IMREAD_COLOR));
auto& cvImage = image->m_image;

if (!cvImage.data)
{
RuntimeError("Cannot open file '%s'", imageSequence.m_path.c_str());
}

// Convert element type.
int dataType = m_parent.m_featureElementType == ElementType::tfloat ? CV_32F : CV_64F;
if (cvImage.type() != CV_MAKETYPE(dataType, cvImage.channels()))
{
cvImage.convertTo(cvImage, dataType);
}

if (!cvImage.isContinuous())
{
cvImage = cvImage.clone();
}
assert(cvImage.isContinuous());

image->m_data = image->m_image.data;
ImageDimensions dimensions(cvImage.cols, cvImage.rows, cvImage.channels());
image->m_sampleLayout = std::make_shared<TensorShape>(dimensions.AsTensorShape(HWC));
image->m_numberOfSamples = 1;
image->m_chunk = shared_from_this();

SparseSequenceDataPtr label = std::make_shared<SparseSequenceData>();
label->m_chunk = shared_from_this();
m_parent.m_labelGenerator->CreateLabelFor(imageSequence.m_classId, *label);
return std::vector<SequenceDataPtr> { image, label };
}
};

ImageDataDeserializer::ImageDataDeserializer(const ConfigParameters& config)
{
ImageConfigHelper configHelper(config);
Expand Down Expand Up @@ -81,7 +136,7 @@ ImageDataDeserializer::ImageDataDeserializer(const ConfigParameters& config)

void ImageDataDeserializer::CreateSequenceDescriptions(std::string mapPath, size_t labelDimension)
{
UNREFERENCED_PARAMETER(labelDimension);
UNUSED(labelDimension);

std::ifstream mapFile(mapPath);
if (!mapFile)
Expand Down Expand Up @@ -127,72 +182,6 @@ std::vector<StreamDescriptionPtr> ImageDataDeserializer::GetStreamDescriptions()
return m_streams;
}

std::vector<std::vector<SequenceDataPtr>> ImageDataDeserializer::GetSequencesById(const std::vector<size_t>& ids)
{
if (ids.empty())
{
RuntimeError("Number of requested sequences cannot be zero.");
}

m_currentImages.resize(ids.size());
m_labels.resize(ids.size());

std::vector<std::vector<SequenceDataPtr>> result;
result.resize(ids.size());

#pragma omp parallel for ordered schedule(dynamic)
for (int i = 0; i < ids.size(); ++i)
{
if (ids[i] >= m_imageSequences.size())
{
RuntimeError("Invalid sequence id is provided '%d', expected range [0..%d].",
static_cast<int>(ids[i]),
static_cast<int>(m_imageSequences.size()) - 1);
}

const auto& imageSequence = m_imageSequences[ids[i]];

// Construct image
m_currentImages[i] = std::move(cv::imread(imageSequence.m_path, cv::IMREAD_COLOR));
cv::Mat& cvImage = m_currentImages[i];

if (!cvImage.data)
{
RuntimeError("Cannot open file '%s'", imageSequence.m_path.c_str());
}

// Convert element type.
// TODO We should all native CV element types to be able to match the behavior of the old reader.
int dataType = m_featureElementType == ElementType::tfloat ? CV_32F : CV_64F;
if (cvImage.type() != CV_MAKETYPE(dataType, cvImage.channels()))
{
cvImage.convertTo(cvImage, dataType);
}

if (!cvImage.isContinuous())
{
cvImage = cvImage.clone();
}
assert(cvImage.isContinuous());

ImageDimensions dimensions(cvImage.cols, cvImage.rows, cvImage.channels());
auto image = std::make_shared<DenseSequenceData>();
image->m_data = cvImage.data;
image->m_sampleLayout = std::make_shared<TensorShape>(dimensions.AsTensorShape(HWC));
image->m_numberOfSamples = 1;

if (m_labels[i] == nullptr)
{
m_labels[i] = std::make_shared<SparseSequenceData>();
}

m_labelGenerator->CreateLabelFor(imageSequence.m_classId, *m_labels[i]);
result[i] = std::move(std::vector<SequenceDataPtr>{image, m_labels[i]});
}

return result;
}

void ImageDataDeserializer::FillSequenceDescriptions(SequenceDescriptions& timeline) const
{
timeline.resize(m_imageSequences.size());
Expand All @@ -206,4 +195,10 @@ void ImageDataDeserializer::FillSequenceDescriptions(SequenceDescriptions& timel
});
}

ChunkPtr ImageDataDeserializer::GetChunk(size_t chunkId)
{
auto sequenceDescription = m_imageSequences[chunkId];
return std::make_shared<ImageChunk>(sequenceDescription, *this);
}

}}}
10 changes: 3 additions & 7 deletions Source/Readers/ImageReader/ImageDataDeserializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class ImageDataDeserializer : public DataDeserializerBase
std::vector<StreamDescriptionPtr> GetStreamDescriptions() const override;

// Get sequences by specified ids. Order of returned sequences corresponds to the order of provided ids.
std::vector<std::vector<SequenceDataPtr>> GetSequencesById(const std::vector<size_t>& ids) override;
virtual ChunkPtr GetChunk(size_t chunkId) override;

protected:
void FillSequenceDescriptions(SequenceDescriptions& timeline) const override;
Expand All @@ -40,6 +40,8 @@ class ImageDataDeserializer : public DataDeserializerBase
size_t m_classId;
};

class ImageChunk;

// A helper class for generation of type specific labels (currently float/double only).
class LabelGenerator;
typedef std::shared_ptr<LabelGenerator> LabelGeneratorPtr;
Expand All @@ -48,12 +50,6 @@ class ImageDataDeserializer : public DataDeserializerBase
// Sequence descriptions for all input data.
std::vector<ImageSequenceDescription> m_imageSequences;

// Buffer to store label data.
std::vector<SparseSequenceDataPtr> m_labels;

// Buffer to store feature data.
std::vector<cv::Mat> m_currentImages;

// Element type of the feature/label stream (currently float/double only).
ElementType m_featureElementType;
};
Expand Down
2 changes: 1 addition & 1 deletion Source/Readers/ImageReader/ImageReader.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,4 @@
<Target Name="CheckDependencies">
<Warning Condition="!$(HasOpenCV)" Text="ImageReader requires the OpenCV library to build. Please see https://github.com/Microsoft/CNTK/wiki/Setup-CNTK-on-Windows#opencv for installation instructions." />
</Target>
</Project>
</Project>
65 changes: 44 additions & 21 deletions Source/Readers/ImageReader/ImageTransformers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
namespace Microsoft { namespace MSR { namespace CNTK
{

struct ImageSequenceData : DenseSequenceData
{
cv::Mat m_image;
// In case we do not copy data - we have to preserve the original sequence.
SequenceDataPtr m_original;
};

void ImageTransformerBase::Initialize(TransformerPtr next,
const ConfigParameters &readerConfig)
{
Expand All @@ -34,10 +41,12 @@ void ImageTransformerBase::Initialize(TransformerPtr next,
}

SequenceDataPtr
ImageTransformerBase::Apply(const DenseSequenceData &inputSequence,
const StreamDescription &inputStream, cv::Mat &buffer,
ImageTransformerBase::Apply(SequenceDataPtr sequence,
const StreamDescription &inputStream,
const StreamDescription & /*outputStream*/)
{
assert(inputStream.m_storageType == StorageType::dense);
auto inputSequence = static_cast<const DenseSequenceData&>(*sequence.get());
ImageDimensions dimensions(*inputSequence.m_sampleLayout, HWC);
int columns = static_cast<int>(dimensions.m_width);
int rows = static_cast<int>(dimensions.m_height);
Expand All @@ -57,20 +66,25 @@ ImageTransformerBase::Apply(const DenseSequenceData &inputSequence,
RuntimeError("Unsupported type");
}

auto result = std::make_shared<ImageSequenceData>();
int type = CV_MAKETYPE(typeId, channels);
buffer = cv::Mat(rows, columns, type, inputSequence.m_data);
this->Apply(buffer);
cv::Mat buffer = cv::Mat(rows, columns, type, inputSequence.m_data);
Apply(buffer);
if (!buffer.isContinuous())
{
buffer = buffer.clone();
}
else
{
result->m_original = sequence;
}
assert(buffer.isContinuous());
result->m_image = buffer;
result->m_data = buffer.ptr();
result->m_numberOfSamples = inputSequence.m_numberOfSamples;

auto result = std::make_shared<DenseSequenceData>();
ImageDimensions outputDimensions(buffer.cols, buffer.rows, buffer.channels());
result->m_sampleLayout = std::make_shared<TensorShape>(outputDimensions.AsTensorShape(HWC));
result->m_numberOfSamples = inputSequence.m_numberOfSamples;
result->m_data = buffer.ptr();
return result;
}

Expand Down Expand Up @@ -367,7 +381,7 @@ void MeanTransformer::Apply(cv::Mat &mat)
void TransposeTransformer::Initialize(TransformerPtr next,
const ConfigParameters &readerConfig)
{
Base::Initialize(next, readerConfig);
TransformerBase::Initialize(next, readerConfig);

// Currently we only support a single stream.
ImageConfigHelper config(readerConfig);
Expand All @@ -384,52 +398,62 @@ void TransposeTransformer::Initialize(TransformerPtr next,

ImageDimensions dimensions(*stream->m_sampleLayout, HWC);

// Changing layout from NWH to NHW
// Changing from NHWC to NCHW
auto changedStream = std::make_shared<StreamDescription>(*stream);
changedStream->m_sampleLayout = std::make_shared<TensorShape>(dimensions.AsTensorShape(CHW));
m_outputStreams[id] = changedStream;
}
}

SequenceDataPtr
TransposeTransformer::Apply(const DenseSequenceData &inputSequence,
TransposeTransformer::Apply(SequenceDataPtr inputSequence,
const StreamDescription &inputStream,
vector<char> &buffer,
const StreamDescription &outputStream)
{
if (inputStream.m_elementType == ElementType::tdouble)
{
return TypedApply<double>(inputSequence, inputStream, buffer, outputStream);
return TypedApply<double>(inputSequence, inputStream, outputStream);
}

if (inputStream.m_elementType == ElementType::tfloat)
{
return TypedApply<float>(inputSequence, inputStream, buffer, outputStream);
return TypedApply<float>(inputSequence, inputStream, outputStream);
}

RuntimeError("Unsupported type");
}

template <class TElement>
// The class represents a sequence that owns an internal data buffer.
// Passed from the TransposeTransformer.
// TODO: Trasposition potentially could be done in place.
struct DenseSequenceWithBuffer : DenseSequenceData
{
std::vector<char> m_buffer;
};

template <class TElemType>
SequenceDataPtr
TransposeTransformer::TypedApply(const DenseSequenceData &inputSequence,
TransposeTransformer::TypedApply(SequenceDataPtr sequence,
const StreamDescription &inputStream,
vector<char> &buffer,
const StreamDescription &outputStream)
{
assert(inputStream.m_storageType == StorageType::dense);
auto inputSequence = static_cast<DenseSequenceData&>(*sequence.get());
assert(inputSequence.m_numberOfSamples == 1);
assert(inputStream.m_sampleLayout->GetNumElements() ==
outputStream.m_sampleLayout->GetNumElements());

size_t count = inputStream.m_sampleLayout->GetNumElements() * GetSizeByType(inputStream.m_elementType);
buffer.resize(count);

TElement* typedBuffer = reinterpret_cast<TElement*>(&buffer[0]);
auto result = std::make_shared<DenseSequenceWithBuffer>();
result->m_buffer.resize(count);

TElemType* typedBuffer = reinterpret_cast<TElemType*>(result->m_buffer.data());
ImageDimensions dimensions(*inputStream.m_sampleLayout, ImageLayoutKind::HWC);

size_t rowCount = dimensions.m_height * dimensions.m_width;
size_t channelCount = dimensions.m_numChannels;
TElement* data = reinterpret_cast<TElement*>(inputSequence.m_data);
TElemType* data = reinterpret_cast<TElemType*>(inputSequence.m_data);

for (size_t rowIndex = 0; rowIndex < rowCount; rowIndex++)
{
Expand All @@ -441,9 +465,8 @@ TransposeTransformer::TypedApply(const DenseSequenceData &inputSequence,
}
}

auto result = std::make_shared<DenseSequenceData>();
result->m_sampleLayout = outputStream.m_sampleLayout;
result->m_data = &buffer[0];
result->m_data = result->m_buffer.data();
result->m_numberOfSamples = inputSequence.m_numberOfSamples;
return result;
}
Expand Down
Loading

0 comments on commit fb47c8d

Please sign in to comment.