Skip to content

Instantly share code, notes, and snippets.

@thai-ng
Last active January 19, 2018 21:51
Show Gist options
  • Save thai-ng/19cc39704de5726799166469e586adf8 to your computer and use it in GitHub Desktop.
Save thai-ng/19cc39704de5726799166469e586adf8 to your computer and use it in GitHub Desktop.
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/opencv.hpp>
#include <array>
#include <string_view>
#include <utility>
using namespace std::string_view_literals;
// Wrapper for setMouseCallback to use capture lambda
template <typename F>
void setMouseAction(std::string_view windowName, F& func)
{
cv::setMouseCallback(windowName.data(),
[](int e, int x, int y, int flags, void* data)
{
(*static_cast<F*>(data))(e, x, y, flags);
},
static_cast<void*>(&func));
}
// Display image, take 2 clicks, close image
std::pair<cv::Point, cv::Point> get2Points(cv::Mat const& im)
{
auto window = "Choose 2 points, left to right"sv;
cv::namedWindow(window.data());
cv::imshow(window.data(), im);
std::array<cv::Point, 2> clicks;
int clickCount = 0;
auto closure = [&clicks, &clickCount, window](int e, int x, int y, int flags)
{
if (e == cv::MouseEventTypes::EVENT_LBUTTONDOWN)
{
clicks[clickCount] = cv::Point(x, y);
++clickCount;
if (clickCount == 2)
{
// Close window to continue
cv::destroyWindow(window.data());
}
}
};
setMouseAction(window.data(), closure);
cv::waitKey();
return { clicks[0], clicks[1] };
}
inline
cv::Point meanPoint(cv::Point p1, cv::Point p2)
{
auto meanX = (p1.x + p2.x) / 2.0;
auto meanY = (p1.y + p2.y) / 2.0;
return cv::Point(static_cast<int>(meanX), static_cast<int>(meanY));
}
cv::Mat padImage(cv::Mat im, cv::Point centerPoint)
{
auto toTranslateTop = (im.rows / 2) - centerPoint.y;
auto topPad = (toTranslateTop > 0) ? toTranslateTop * 2 : 0;
auto bottomPad = (toTranslateTop < 0) ? -toTranslateTop * 2 : 0;
auto toTranslateLeft = (im.cols / 2) - centerPoint.x;
auto leftPad = (toTranslateLeft > 0) ? toTranslateLeft * 2 : 0;
auto rightPad = (toTranslateLeft < 0) ? -toTranslateLeft * 2 : 0;
cv::copyMakeBorder(im, im, topPad, bottomPad, leftPad, rightPad, cv::BorderTypes::BORDER_CONSTANT, 0);
return im;
}
cv::Mat rotateImage(cv::Mat im, double angle)
{
auto rotation = cv::getRotationMatrix2D(cv::Point(im.cols / 2, im.rows / 2), angle, 1);
cv::warpAffine(im, im, rotation, im.size());
return im;
}
cv::Mat rotateAndPadImage(cv::Mat im, cv::Point p1, cv::Point p2)
{
auto centerPoint = meanPoint(p1, p2);
im = padImage(im, centerPoint);
auto angle = std::atan2(p2.y - centerPoint.y, p2.x - centerPoint.x) * 180 / std::acos(-1.0);
im = rotateImage(im, angle);
return im;
}
cv::Mat openSingleGrayImage(std::string_view name)
{
auto img = cv::imread(name.data(), cv::ImreadModes::IMREAD_UNCHANGED);
img.convertTo(img, CV_32F);
img *= 1 / 255.0;
if (img.channels() == 3)
{
cv::cvtColor(img, img, CV_RGB2GRAY);
}
return img;
}
cv::Mat scaleImage(cv::Mat im, double scale)
{
cv::resize(im, im, cv::Size(static_cast<int>(im.size().width * scale), static_cast<int>(im.size().height * scale)));
return im;
}
std::pair<cv::Mat, cv::Mat> align2Images(cv::Mat im1, cv::Mat im2)
{
auto[im1p1, im1p2] = get2Points(im1);
im1 = rotateAndPadImage(im1, im1p1, im1p2);
auto[im2p1, im2p2] = get2Points(im2);
im2 = rotateAndPadImage(im2, im2p1, im2p2);
auto firstLength = std::hypot(im1p2.x - im1p1.x, im1p2.y - im1p1.y);
auto secondLength = std::hypot(im2p2.x - im2p1.x, im2p2.y - im2p1.y);
if (firstLength < secondLength)
{
auto scale = firstLength / secondLength;
im2 = scaleImage(im2, scale);
}
else if (firstLength > secondLength)
{
auto scale = secondLength/ firstLength;
im1 = scaleImage(im1, scale);
}
auto croppedWidth = std::min(im1.size().width, im2.size().width);
auto croppedHeight = std::min(im1.size().height, im2.size().height);
auto im1ROI = cv::Rect(im1.cols / 2 - (croppedWidth / 2), im1.rows / 2 - (croppedHeight / 2), croppedWidth, croppedHeight);
im1 = im1(im1ROI);
auto im2ROI = cv::Rect(im2.cols / 2 - (croppedWidth / 2), im2.rows / 2 - (croppedHeight / 2), croppedWidth, croppedHeight);
im2 = im2(im2ROI);
return { im1, im2 };
}
int main()
{
auto im1 = openSingleGrayImage("DerekPicture.jpg");
auto im2 = openSingleGrayImage("nutmeg.jpg");
auto [i1, i2] = align2Images(im1, im2);
cv::namedWindow("Image1");
cv::imshow("Image1", i1);
cv::namedWindow("Image2");
cv::imshow("Image2", i2);
cv::waitKey();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment