カメラはiBUFFALO マイク内蔵320万画素WEBカメラ F2.2ガラスレンズ搭載モデル ブ ラック BSW32KM03BKを使用した。
修正したサンプル 上が補正済み、下が未補正。

元画像がほとんど歪みのない画像なのであまり違いがわからない。Webカメラすぎょい。
チェッカーボードは本来印刷したりして使うんだろうが、プリンタが無いのでとりあえず画面に表示して代用した。

この画像は24インチの1920x1080液晶で表示することで1マスが25.0mmのROW9,COL18なチェッカーボードとして使用することができる。ROWとCOLはコマの数ではなく、交差しているところの数であることに注意。
補正情報を拾うための画像は適当にいろいろな角度や距離から撮影しておく。距離や向きに多様性がある方がいいと思うが、チェッカーボードが一部でも隠れていると解析に失敗してしまうので、すべての領域が撮影されている必要がある。
情報はXMLで書きだされ、今回はこんな感じになった。
<?xml version="1.0"?> <opencv_storage> <intrinsic type_id="opencv-matrix"> <rows>3</rows> <cols>3</cols> <dt>f</dt> <data> 1.04094995e+003 0. 6.19313538e+002 0. 1.15699170e+003 3.98739349e+002 0. 0. 1.</data></intrinsic> <rotation type_id="opencv-matrix"> <rows>1</rows> <cols>3</cols> <dt>f</dt> <data> -2.04190898e+000 -2.19732761e+000 2.56176054e-001</data></rotation> <translation type_id="opencv-matrix"> <rows>1</rows> <cols>3</cols> <dt>f</dt> <data> -1.47931931e+002 -1.17953056e+002 6.39266296e+002</data></translation> <distortion type_id="opencv-matrix"> <rows>1</rows> <cols>4</cols> <dt>f</dt> <data> 8.36658627e-002 -6.74404874e-002 -1.06694188e-003 -7.54280773e-005</data></distortion> </opencv_storage>
さて、とりあえずオプティカルフローでカメラの動きを検出することができるようになった。それからレンズ歪みも補正できるようになった。あとはオプティカルフローでシェイクリダクションをできるようになれば、ウェアラブルカメラの補正とかができて楽しいかな。
歪みを検出するプログラム
using OpenCvSharp;
using System;
namespace CalibrateCamera
{
class Program
{
static void Main(string[] args)
{
const int IMAGE_NUM = 25;
const int PAT_ROW = 9;
const int PAT_COL = 18;
const int PAT_SIZE = PAT_ROW * PAT_COL;
const int ALL_POINTS = IMAGE_NUM * PAT_SIZE;
const double CHESS_SIZE = 25.0;
int i, j, k;
int corner_count;
bool found;
int[] p_count = new int[IMAGE_NUM];
IplImage[] src_img = new IplImage[IMAGE_NUM];
CvSize pattern_size = Cv.Size(PAT_COL, PAT_ROW);
CvPoint3D32f[] objects = new CvPoint3D32f[ALL_POINTS];
CvPoint2D32f[][] corners = new CvPoint2D32f[IMAGE_NUM][];
CvMat object_points = null;
CvMat image_points = null;
CvMat point_counts = null;
CvMat intrinsic = Cv.CreateMat(3, 3, MatrixType.F32C1);
CvMat rotation = Cv.CreateMat(1, 3, MatrixType.F32C1);
CvMat translation = Cv.CreateMat(1, 3, MatrixType.F32C1);
CvMat distortion = Cv.CreateMat(1, 4, MatrixType.F32C1);
for (i = 0; i < IMAGE_NUM; i++)
{
string path = @"C:\Users\John\Pictures\carib\" + i + ".jpg";
src_img[i] = Cv.LoadImage(path, LoadMode.Color);
}
for (i = 0; i < IMAGE_NUM; i++)
{
for (j = 0; j < PAT_ROW; j++)
{
for (k = 0; k < PAT_COL; k++)
{
objects[i * PAT_SIZE + j * PAT_COL + k] = new CvPoint3D32f(
j * CHESS_SIZE, k * CHESS_SIZE, 0);
}
}
}
object_points = new CvMat(false);
Cv.InitMatHeader(object_points, ALL_POINTS, 3, MatrixType.F32C1, objects);
int found_num = 0;
Cv.NamedWindow("Calibration", WindowMode.AutoSize);
for (i = 0; i < IMAGE_NUM; i++)
{
bool fail = false;
found = Cv.FindChessboardCorners(src_img[i], pattern_size, out corners[i], out corner_count);
Console.WriteLine(i);
if (found)
{
Console.WriteLine("ok");
found_num++;
}
else
{
Console.WriteLine("fail");
fail = true;
}
IplImage src_gray = Cv.CreateImage(Cv.GetSize(src_img[i]), BitDepth.U8, 1);
Cv.CvtColor(src_img[i], src_gray, ColorConversion.BgrToGray);
Cv.FindCornerSubPix(src_gray, corners[i], corner_count,
Cv.Size(3, 3), Cv.Size(-1, -1), Cv.TermCriteria(CriteriaType.Iteration | CriteriaType.Epsilon, 20, 0.03));
Cv.DrawChessboardCorners(src_img[i], pattern_size, corners[i], found);
p_count[i] = corner_count;
Cv.ShowImage("Calibration", src_img[i]);
Cv.WaitKey(fail ? 0 : 1);
}
Cv.DestroyWindow("Calibration");
if (found_num != IMAGE_NUM)
{
Console.WriteLine("error:" + found_num);
Console.ReadLine();
return;
}
CvPoint2D32f[] corners2 = null;
{
int count = 0;
for (i = 0; i < corners.Length; i++)
{
count += corners[i].Length;
}
corners2 = new CvPoint2D32f[count];
for (i = j = 0; i < corners.Length; i++)
{
for (k = 0; k < corners[i].Length; k++)
{
corners2[j++] = corners[i][k];
}
}
}
image_points = new CvMat(false);
point_counts = new CvMat(false);
Cv.InitMatHeader(image_points, ALL_POINTS, 1, MatrixType.F32C2, corners2);
Cv.InitMatHeader(point_counts, IMAGE_NUM, 1, MatrixType.S32C1, p_count);
Cv.CalibrateCamera2(object_points, image_points, point_counts, new CvSize(1280, 800), intrinsic, distortion);
CvMat sub_image_points, sub_object_points;
int Base = 0;
Cv.GetRows(image_points, out sub_image_points, Base * PAT_SIZE, (Base + 1) * PAT_SIZE);
Cv.GetRows(object_points, out sub_object_points, Base * PAT_SIZE, (Base + 1) * PAT_SIZE);
Cv.FindExtrinsicCameraParams2(sub_object_points, sub_image_points, intrinsic, distortion, rotation, translation);
CvFileStorage fs = Cv.OpenFileStorage("camera.xml", null, FileStorageMode.Write);
fs.Write("intrinsic", intrinsic);
fs.Write("rotation", rotation);
fs.Write("translation", translation);
fs.Write("distortion", distortion);
Cv.ReleaseFileStorage(fs);
for (i = 0; i < IMAGE_NUM; i++)
{
Cv.ReleaseImage(src_img[i]);
}
return;
}
}
}
歪みを補正するプログラム
using OpenCvSharp;
using System;
using System.Windows.Forms;
namespace undistort
{
class Program
{
[STAThread]
static void Main(string[] args)
{
IplImage src_img, dst_img;
CvMat intrinsic, distortion;
CvFileStorage fs;
CvFileNode param;
while (true)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "JPEG|*.jpg|AllType|*.*";
if (ofd.ShowDialog() != DialogResult.OK)
{
return;
}
string src_path = ofd.FileName;
src_img = Cv.LoadImage(ofd.FileName, LoadMode.Color);
dst_img = Cv.CloneImage(src_img);
ofd.Filter = "XML|*.xml";
if (ofd.ShowDialog() != DialogResult.OK)
{
return;
}
fs = Cv.OpenFileStorage(ofd.FileName, null, FileStorageMode.Read);
param = Cv.GetFileNodeByName(fs, null, "intrinsic");
intrinsic = Cv.Read<CvMat>(fs, param);
param = Cv.GetFileNodeByName(fs, null, "distortion");
distortion = Cv.Read<CvMat>(fs, param);
Cv.ReleaseFileStorage(fs);
Cv.Undistort2(src_img, dst_img, intrinsic, distortion);
dst_img.SaveImage(src_path + ".undistort.jpg");
Cv.NamedWindow("Distortion");
Cv.ShowImage("Distortion", src_img);
Cv.NamedWindow("UnDistortion");
Cv.ShowImage("UnDistortion", dst_img);
Cv.WaitKey(0);
Cv.DestroyAllWindows();
Cv.ReleaseImage(src_img);
Cv.ReleaseImage(dst_img);
Cv.ReleaseMat(intrinsic);
Cv.ReleaseMat(distortion);
}
return;
}
}
}
0 件のコメント:
コメントを投稿