自定义生成相机标定棋盘图
自定义生成相机标定棋盘图
Written on 2023-01-22.
参考资料:
A4、A3、A2、A1纸张尺寸大小
毫米(mm)转像素点(px)
生成棋盘格(标定板图片)
摘自图片像素尺寸(厘米/英寸)换算器、在线转换:
一张图片的打印出来的实际尺寸是由此图片的像素和分辨率共同决定的,像素(Pixel)是指构成图片的小色点,分辨率(单位DPI)是指每英寸(Inch)上的像素数量,可以看做是这些小色点的分布密度;像素相同时,分辨率越高则像素密度越大,实际打印尺寸越小,图像也越细腻。
但是OpenCV保存的图像 DPI = 92,有点小,因此使用了网上查询到的修改DPI方法,修改后作为最终生成的棋盘图(同时会保存DPI = 92的图像)。本文默认改为 300 DPI。
使用本文的工具,可以自定义生成棋盘图的纸张大小、格子实际边长(单位为毫米)、每行每列的角点个数,并且生成的棋盘图是在纸张的中央。
详见代码的 16 - 21 行。
// ==============> 自定义区域
#define DPI 300 // 图像分辨率DPI
#define PAPER_HEIGHT 210 // 纸张高度(mm)
#define PAPER_WIDTH 297 // 纸张宽度(mm)
#define SQUARE_SIZE 25 // 正方形格子边长(mm)
#define COL_CORNER 9 // 每行的角点个数
#define ROW_CORNER 6 // 每列的角点个数
// <============== 自定义区域
但是要注意:设置的交点个数以及格子边长的大小,是否会使得超过设置的纸张大小,不然程序会报错的。
生成的效果:
A3 格子边长30mm:
#include <fcntl.h>
#include <io.h>
#include <iostream>
#include <cmath>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
// A3横向尺寸: 420mm * 297mm
// A4横向尺寸: 297mm * 210mm
// ==============> 自定义区域
#define DPI 300 // 图像分辨率DPI
#define PAPER_HEIGHT 210 // 纸张高度(mm)
#define PAPER_WIDTH 297 // 纸张宽度(mm)
#define SQUARE_SIZE 25 // 正方形格子边长(mm)
#define COL_CORNER 9 // 每行的角点个数
#define ROW_CORNER 6 // 每列的角点个数
// <============== 自定义区域
#define MM_TO_PIXEL(dpi, mm) ceil((DPI / 25.4) * mm); // 毫米转像素
bool SetResolution(const char* path, int iResolution)
{
FILE* file = fopen(path, "rb+");// - 打开图片文件
if (!file)return false;
int len = _filelength(_fileno(file));// - 获取文件大小
char* buf = new char[len];
fread(buf, sizeof(char), len, file);// - 将图片数据读入缓存
char* pResolution = (char*)&iResolution;// - iResolution为要设置的分辨率的数值,如72dpi
// - 设置JPG图片的分辨率
buf[0x0D] = 1;// - 设置使用图片密度单位
// - 水平密度,水平分辨率
buf[0x0E] = pResolution[1];
buf[0x0F] = pResolution[0];
// - 垂直密度,垂直分辨率
buf[0x10] = pResolution[1];
buf[0x11] = pResolution[0];
// - 将文件指针移动回到文件开头
fseek(file, 0, SEEK_SET);
// - 将数据写入文件,覆盖原始的数据,让修改生效
fwrite(buf, sizeof(char), len, file);
fclose(file);
return true;
}
void main() {
int paperHeight = MM_TO_PIXEL(DPI, PAPER_HEIGHT)
int paperWidth = MM_TO_PIXEL(DPI, PAPER_WIDTH)
Mat map = Mat(paperHeight, paperWidth, CV_8UC3, Scalar::all(255)); //初始化将所有的像素设为1
int n_cols = COL_CORNER;
int n_rows = ROW_CORNER;
int n_pix = MM_TO_PIXEL(DPI, SQUARE_SIZE) // 每个格子的像素边长,根据毫米转换像素
int col = n_pix * (n_cols + 1); // 标定图的列
int row = n_pix * (n_rows + 1); // 标定图的行
int startX = (paperWidth - col) / 2; // 初始生成位置,目的是居中生成
int startY = (paperHeight - row) / 2;
for (int i = startY; i < startY + row; i++) { // 遍历所有像素点
for (int j = startX; j < startX + col; j++) {
if (int((i - startY) / n_pix) % 2 == 0) { //如果是奇数行
if (int((j - startX) / n_pix) % 2 != 0) {
map.at<Vec3b>(i, j)[0] = 0;
map.at<Vec3b>(i, j)[1] = 0;
map.at<Vec3b>(i, j)[2] = 0;
}
}
if (int((i - startY) / n_pix) % 2 != 0) { //如果是偶数行
if (int((j - startX) / n_pix) % 2 == 0) {
map.at<Vec3b>(i, j)[0] = 0;
map.at<Vec3b>(i, j)[1] = 0;
map.at<Vec3b>(i, j)[2] = 0;
}
}
}
}
// OpenCV DPI 92 保存图像名称
imwrite("opencv-dpi92.jpg", map);
// 自定分辨率保存图像名称
char path[20] = "change-dpi.jpg";
imwrite(path, map);
SetResolution(path, DPI);
waitKey(0);
return;
}