[OpenCV] QR Code generator로 QR 코드 만들고 Mat으로 변환하기

이번 글에서는 QR코드를 만드는 방법에 대해 알아보겠습니다.

QR코드 생성과 관련해서는 여러 라이브러리가 있지만 이 글에서는 QR Code generator를 사용하겠습니다.
여기에서 파일을 다운 받아 줍니다

cpp 폴더에 파일명이 qrcodegen인 hpp와 cpp파일을 프로젝트에 복사해 추가해줍니다.

QR Code generator를 사용하기 위해 코드에 qrcodegen.hpp을 include 해줍니다.

#include "qrcodegen.hpp"
using qrcodegen::QrCode;

라이브러리에서 제공해주는 QR Code 클래스 명칭은 QrCode 입니다. QrCode 클래스의 encodeText 멤버 함수를 사용하면 쉽게 QR코드를 생성할 수 있습니다.

const QrCode qr = QrCode::encodeText("https://3001ssw.tistory.com", QrCode::Ecc::LOW);

첫번째 인자는 QR코드에 들어갈 텍스트, 두번째 인자는 오류복원 레벨입니다.
참고로 오류복원 레벨은 QR Code 이미지가 훼손되더라도 데이터를 복구 할 수 있는 기능을 말하며, 레벨을 높이면 코드의 크기가 커집니다.

어쨌든 encodeText 함수를 수행하며 QrCode 객체를 반환받았습니다.
이 QrCode 객체를 Mat 객체로로 변환하기 위해서 MakeQrCodeMat 함수를 선언해줍니다.

Mat MakeQrCodeMat(const QrCode &qr, int iBorder = 0);

첫번째 인자는 입력 QrCode 객체, 두번째 인자는 테두리 입니다.
QR Code 이미지에 테두리를 넣으려면 iBorder에 값을 입력하면 됩니다.

MakeQrCodeMat 함수 정의는 아래와 같습니다.

Mat MakeQrCodeMat(const QrCode &qr, int iBorder/* = 0*/)
{
	int iQrSize = qr.getSize();
	int iNewSize = iQrSize + iBorder*2;
	Mat img(iNewSize, iNewSize, CV_8UC1, Scalar(255));
	for (int iQrPosX = 0; iQrPosX < iQrSize; iQrPosX++)
	{
		for (int iQrPosY = 0; iQrPosY < iQrSize; iQrPosY++)
		{
			if (qr.getModule(iQrPosX, iQrPosY))
			{
				int iNewX = iQrPosX + iBorder;
				int iNewY = iQrPosY + iBorder;
				img.at<uchar>(iNewY, iNewX) = 0;
			}
		}
	}

	return img;
}

main 함수에 아래와 같이 입력해줍니다.

const QrCode qr = QrCode::encodeText("https://3001ssw.tistory.com", QrCode::Ecc::LOW); // 텍스트, 오류복원 레벨
Mat img = MakeQrCodeMat(qr, 2); // QrCode => Mat

imshow("img", img);

waitKey();
destroyAllWindows();

실행 결과는 아래와 같습니다.

QR Code 이미지가 너무 작습니다. 이미지가 너무 작으면 인식이 안되므로 이럴땐 OpenCV에서 제공하는 resize 함수를 사용하면 편하게 사이즈 조절이 가능합니다.

const QrCode qr = QrCode::encodeText("https://3001ssw.tistory.com", QrCode::Ecc::LOW); // 텍스트, 오류복원 레벨
Mat img = MakeQrCodeMat(qr, 2); // QrCode => Mat

Mat img_resize;
resize(img, img_resize, Size(300, 300), 0, 0, INTER_NEAREST); // 사이즈 변환

imshow("img", img);
imshow("img_resize", img_resize);

waitKey();
destroyAllWindows();

다시 실행하여 확인해봅니다.

사이즈가 넓어진 것을 볼 수 있습니다.

전체 코드입니다.

#include <iostream>
#include "opencv2/opencv.hpp"

#ifdef _DEBUG
#pragma comment(lib, "opencv_world451d.lib")
#else
#pragma comment(lib, "opencv_world451.lib")
#endif

#include "qrcodegen.hpp"

#include "Windows.h"

using namespace std;
using namespace cv;
using qrcodegen::QrCode;
using namespace qrcodegen;

// QrCode => Mat
Mat MakeQrCodeMat(const QrCode &qr, int iBorder = 0);
void DetectQrCode(Mat src);

int main()
{
	const QrCode qr = QrCode::encodeText("https://3001ssw.tistory.com", QrCode::Ecc::LOW); // 텍스트, 오류복원 레벨
	Mat img = MakeQrCodeMat(qr, 2); // QrCode => Mat

	Mat img_resize;
	resize(img, img_resize, Size(300, 300), 0, 0, INTER_NEAREST); // 사이즈 변환

	imshow("img", img);
	imshow("img_resize", img_resize);

	waitKey();
	destroyAllWindows();

	return 0;
}

Mat MakeQrCodeMat(const QrCode &qr, int iBorder/* = 0*/)
{
	int iQrSize = qr.getSize();
	int iNewSize = iQrSize + iBorder*2;
	Mat img(iNewSize, iNewSize, CV_8UC1, Scalar(255));
	for (int iQrPosX = 0; iQrPosX < iQrSize; iQrPosX++)
	{
		for (int iQrPosY = 0; iQrPosY < iQrSize; iQrPosY++)
		{
			if (qr.getModule(iQrPosX, iQrPosY))
			{
				int iNewX = iQrPosX + iBorder;
				int iNewY = iQrPosY + iBorder;
				img.at<uchar>(iNewY, iNewX) = 0;
			}
		}
	}

	return img;
}

이상으로 QR코드 생성에 대해 알아보았습니다.