Skip to content

Instantly share code, notes, and snippets.

@kinchungwong
Created October 8, 2013 01:37
Show Gist options
  • Save kinchungwong/141bfa2d996cb5ae8f42 to your computer and use it in GitHub Desktop.
Save kinchungwong/141bfa2d996cb5ae8f42 to your computer and use it in GitHub Desktop.
This is a collection of 6 C++ files that work around the OpenCV bug #1337 issue on x86/x64 architecture. http://code.opencv.org/issues/1337
/*
* AffineTransformer.cpp
* Copyright 2013 Compulink Management Center, Inc.
*/
#include "stdafx.h"
#include "AffineTransformer.h"
namespace TiledAffine
{
template class AffineTransformer<cv::Point2f, float>;
template class AffineTransformer<cv::Point2d, double>;
template <class PointType, class ValueType>
AffineTransformer<PointType, ValueType>::AffineTransformer()
{
memset(m_coefs, 0, sizeof(m_coefs));
}
template <class PointType, class ValueType>
AffineTransformer<PointType, ValueType>::AffineTransformer(const cv::Mat& matTransform)
{
cv::Size sz = matTransform.size();
if (sz.width != 3 || sz.height != 2)
throw std::exception();
cv::Mat_<ValueType> matTransform2;
matTransform.convertTo(matTransform2, matTransform2.type());
m_coefs[0] = matTransform2(0, 0);
m_coefs[1] = matTransform2(0, 1);
m_coefs[2] = matTransform2(0, 2);
m_coefs[3] = matTransform2(1, 0);
m_coefs[4] = matTransform2(1, 1);
m_coefs[5] = matTransform2(1, 2);
}
template <class PointType, class ValueType>
PointType AffineTransformer<PointType, ValueType>::operator() (const PointType& in) const
{
PointType out;
out.x = (PointType::value_type)(m_coefs[0] * in.x + m_coefs[1] * in.y + m_coefs[2]);
out.y = (PointType::value_type)(m_coefs[3] * in.x + m_coefs[4] * in.y + m_coefs[5]);
return out;
}
} // TiledAffine
/*
* AffineTransformer.h
* Copyright 2013 Compulink Management Center, Inc.
*/
#pragma once
namespace TiledAffine
{
template < class PointType, class ValueType = PointType::value_type >
class AffineTransformer
{
public:
AffineTransformer();
AffineTransformer(const cv::Mat& matTransform);
PointType operator() (const PointType& in) const;
private:
ValueType m_coefs[6];
};
typedef AffineTransformer<cv::Point2f, float> AffineTransformer2f;
typedef AffineTransformer<cv::Point2d, double> AffineTransformer2d;
}
/*
* TiledAffineProcessor.cpp
* Copyright 2013 Compulink Management Center, Inc.
*/
#include "stdafx.h"
#include "TiledAffineProcessor.h"
namespace TiledAffine
{
TiledAffineProcessor::TiledAffineProcessor(const cv::Mat& matSrc, cv::Mat& matDst, const cv::Mat& matTransform, int tileSize)
: m_matSrc(matSrc)
, m_matDst(matDst)
{
if (matSrc.type() != matDst.type())
throw std::exception();
m_interpMode = cv::INTER_LINEAR;
m_borderMode = cv::BORDER_TRANSPARENT;
m_interpMargin = 3; // depends on interpolation type
m_tileSize = tileSize;
m_fillColor = cv::Scalar(0);
m_srcSize = matSrc.size();
m_dstSize = matDst.size();
m_trans = AffineTransformer2f(matTransform);
m_dstParts = TilePartitioner(m_dstSize, m_tileSize);
}
TiledAffineProcessor::TileIndex TiledAffineProcessor::begin() const
{
return 0;
}
TiledAffineProcessor::TileIndex TiledAffineProcessor::end() const
{
return m_dstParts.NumTiles();
}
void TiledAffineProcessor::operator() (TileIndex tileId)
{
if (tileId < 0 || tileId >= m_dstParts.NumTiles())
return;
const std::pair<cv::Range, cv::Range> dstRangeXY = m_dstParts.TileToXyRange(tileId);
const std::pair<cv::Range, cv::Range> srcRangeXY = CoalesceIfSourceEmpty(AddSourceInterpMargin(ComputeSourceRange(dstRangeXY)));
if (srcRangeXY.first.empty() || srcRangeXY.second.empty())
return;
const cv::Range& dstRangeX = dstRangeXY.first;
const cv::Range& dstRangeY = dstRangeXY.second;
const cv::Range& srcRangeX = srcRangeXY.first;
const cv::Range& srcRangeY = srcRangeXY.second;
const cv::Mat_<cv::Vec2f> mapXY = ComputeMapping(srcRangeXY, dstRangeXY);
const cv::Mat roiSrc = m_matSrc(srcRangeY, srcRangeX).clone(); // note: clone to ensure source matrix width less than 32768
cv::Mat roiDst = m_matDst(dstRangeY, dstRangeX);
cv::remap(roiSrc, roiDst, mapXY, cv::noArray(), m_interpMode, m_borderMode, m_fillColor);
}
std::pair<cv::Range, cv::Range> TiledAffineProcessor::ComputeSourceRange(const std::pair<cv::Range, cv::Range>& dstRangeXY) const
{
const cv::Range& dstRangeX = dstRangeXY.first;
const cv::Range& dstRangeY = dstRangeXY.second;
cv::Point2f dstCorner00 = cv::Point2i(dstRangeX.start, dstRangeY.start);
cv::Point2f dstCorner01 = cv::Point2i(dstRangeX.start, dstRangeY.end - 1);
cv::Point2f dstCorner10 = cv::Point2i(dstRangeX.end - 1, dstRangeY.start);
cv::Point2f dstCorner11 = cv::Point2i(dstRangeX.end - 1, dstRangeY.end - 1);
cv::Point2f srcCorner00 = m_trans(dstCorner00);
cv::Point2f srcCorner01 = m_trans(dstCorner01);
cv::Point2f srcCorner10 = m_trans(dstCorner10);
cv::Point2f srcCorner11 = m_trans(dstCorner11);
int srcMinX = (int)floor(std::min(std::min(srcCorner00.x, srcCorner01.x), std::min(srcCorner10.x, srcCorner11.x)));
int srcMinY = (int)floor(std::min(std::min(srcCorner00.y, srcCorner01.y), std::min(srcCorner10.y, srcCorner11.y)));
int srcMaxX = (int)ceil(std::max(std::max(srcCorner00.x, srcCorner01.x), std::max(srcCorner10.x, srcCorner11.x)));
int srcMaxY = (int)ceil(std::max(std::max(srcCorner00.y, srcCorner01.y), std::max(srcCorner10.y, srcCorner11.y)));
cv::Range srcRangeX(srcMinX, srcMaxX);
cv::Range srcRangeY(srcMinY, srcMaxY);
return std::make_pair(srcRangeX, srcRangeY);
}
std::pair<cv::Range, cv::Range> TiledAffineProcessor::AddSourceInterpMargin(const std::pair<cv::Range, cv::Range>& srcRangeXY) const
{
const cv::Range& srcRangeX = srcRangeXY.first;
const cv::Range& srcRangeY = srcRangeXY.second;
int srcMinX = std::max(srcRangeX.start - m_interpMargin, 0);
int srcMaxX = std::min(srcRangeX.end + m_interpMargin, m_srcSize.width);
int srcMinY = std::max(srcRangeY.start - m_interpMargin, 0);
int srcMaxY = std::min(srcRangeY.end + m_interpMargin, m_srcSize.height);
return std::make_pair(cv::Range(srcMinX, srcMaxX), cv::Range(srcMinY, srcMaxY));
}
std::pair<cv::Range, cv::Range> TiledAffineProcessor::CoalesceIfSourceEmpty(const std::pair<cv::Range, cv::Range>& srcRangeXY) const
{
if (srcRangeXY.first.start >= m_srcSize.width || srcRangeXY.second.start >= m_srcSize.height ||
srcRangeXY.first.end < 0 || srcRangeXY.second.end < 0)
{
return std::make_pair(cv::Range(0, 0), cv::Range(0, 0));
}
return srcRangeXY;
}
cv::Mat_<cv::Vec2f> TiledAffineProcessor::ComputeMapping(const RangeXY& srcRangeXY, const RangeXY& dstRangeXY) const
{
const cv::Range& dstRangeX = dstRangeXY.first;
const cv::Range& dstRangeY = dstRangeXY.second;
const cv::Range& srcRangeX = srcRangeXY.first;
const cv::Range& srcRangeY = srcRangeXY.second;
cv::Mat_<cv::Vec2f> mapXY(cv::Size(dstRangeX.size(), dstRangeY.size()));
const cv::Point2f sourceTileOffset = cv::Point2i(srcRangeX.start, srcRangeY.start);
for (int dstY = dstRangeY.start; dstY < dstRangeY.end; ++dstY)
{
for (int dstX = dstRangeX.start; dstX < dstRangeX.end; ++dstX)
{
cv::Point2f dstXY = cv::Point2i(dstX, dstY);
cv::Point2f srcXY = m_trans(dstXY) - sourceTileOffset;
mapXY(dstY - dstRangeY.start, dstX - dstRangeX.start) = srcXY;
}
}
return mapXY;
}
} // TiledAffine
/*
* TiledAffineProcessor.h
* Copyright 2013 Compulink Management Center, Inc.
*/
#pragma once
#include "AffineTransformer.h"
#include "TilePartitioner.h"
namespace TiledAffine
{
/// <summary>
/// Processes the affine transform (such as arbitrary rotation) on a large OpenCV image
/// by computing the output tiles and copying corresponding sections of input image.
/// This is a workaround for OpenCV Bug #1337 (remap fails if input widthstep greater than 32768 bytes)
/// http://code.opencv.org/issues/1337
/// </summary>
///
class TiledAffineProcessor
{
public:
typedef int TileIndex;
typedef std::pair<cv::Range, cv::Range> RangeXY;
public:
TiledAffineProcessor(const cv::Mat& matSrc, cv::Mat& matDst, const cv::Mat& matTransform, int tileSize = 1024);
TileIndex begin() const;
TileIndex end() const;
void operator() (TileIndex tileId);
private:
TiledAffineProcessor(); // not defined
TiledAffineProcessor(const TiledAffineProcessor&); // not defined
TiledAffineProcessor& operator = (const TiledAffineProcessor&); // not defined
private:
RangeXY ComputeSourceRange(const RangeXY& dstRangeXY) const;
RangeXY AddSourceInterpMargin(const RangeXY& srcRangeXY) const;
RangeXY CoalesceIfSourceEmpty(const RangeXY& srcRangeXY) const;
cv::Mat_<cv::Vec2f> ComputeMapping(const RangeXY& srcRangeXY, const RangeXY& dstRangeXY) const;
private:
const cv::Mat m_matSrc;
cv::Mat m_matDst;
cv::Size m_srcSize;
cv::Size m_dstSize;
int m_tileSize;
int m_interpMode;
int m_borderMode;
cv::Scalar m_fillColor;
// m_interpMargin: size radius of neighboring source pixels that might be used to compute the interpolated value of a single output pixel.
int m_interpMargin;
TilePartitioner m_dstParts;
AffineTransformer2f m_trans;
};
} // TiledAffine
/*
* TilePartitioner.cpp
* Copyright 2013 Compulink Management Center, Inc.
*/
#include "stdafx.h"
#include "TilePartitioner.h"
namespace TiledAffine
{
TilePartitioner::TilePartitioner()
{
m_size = cv::Size(0, 0);
m_tileSize = 0;
m_numHorz = 0;
m_numVert = 0;
}
TilePartitioner::TilePartitioner(const cv::Size& size, int tileSize)
{
m_size = size;
m_tileSize = tileSize;
m_numHorz = (m_size.width + (m_tileSize - 1)) / m_tileSize;
m_numVert = (m_size.height + (m_tileSize - 1)) / m_tileSize;
}
int TilePartitioner::NumTiles() const
{
return m_numHorz * m_numVert;
}
cv::Range TilePartitioner::AllTiles() const
{
return cv::Range(0, m_numHorz * m_numVert);
}
std::pair<cv::Range, cv::Range> TilePartitioner::TileToXyRange(int tileId) const
{
int yTileId = tileId / m_numHorz;
int xTileId = tileId % m_numHorz;
int yStart = yTileId * m_tileSize;
int xStart = xTileId * m_tileSize;
int yStop = std::min(yStart + m_tileSize, m_size.height);
int xStop = std::min(xStart + m_tileSize, m_size.width);
cv::Range xRange(xStart, xStop);
cv::Range yRange(yStart, yStop);
return std::make_pair(xRange, yRange);
}
} // TiledAffine
/*
* TilePartitioner.h
* Copyright 2013 Compulink Management Center, Inc.
*/
#pragma once
namespace TiledAffine
{
/// <summary>Utility class for iterating through tile partitioning of a 2D image.</summary>
///
class TilePartitioner
{
public:
TilePartitioner();
TilePartitioner(const cv::Size& size, int tileSize);
int NumTiles() const;
cv::Range AllTiles() const;
std::pair<cv::Range, cv::Range> TileToXyRange(int tileId) const;
private:
cv::Size m_size;
int m_tileSize;
int m_numHorz;
int m_numVert;
};
} // TiledAffine
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment