質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

4743閲覧

C++による魚眼レンズを用いた平面展開プログラムの作成

kpkq307

総合スコア2

Visual C++

Microsoft Visual C++はWindowsのCとC++の統合開発環境(IDE)であり、コンパイラやデバッガを含んでいます。

OpenCV

OpenCV(オープンソースコンピュータービジョン)は、1999年にインテルが開発・公開したオープンソースのコンピュータビジョン向けのクロスプラットフォームライブラリです。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/01/01 13:54

編集2021/01/01 16:20

前提・実現したいこと

 現在、大学で光学系の授業を受けており授業の中で扱った題材を自分で実際に確かめてみたいと思いプログラミングに挑戦しています。今回、作成したいプログラムが「魚眼カメラで撮影した画像を通常のカメラで撮影したように処理(平面展開処理)をするプログラム」となっています。

実装の流れは、魚眼カメラで撮影した画像(src)、通常のカメラで撮影したようにsrcを処理した画像(dst)とすると
1、srcのピクセル座標がdstのどのピクセルに対応するかを計算で求めて、unordered_map<int,Point2d> A_dash_Mapに保存。この時、mapのキーはdstの座標(x,y)をy*画像の横幅+xのように通し番号?として表現した値。キーに対応するPoint2dはsrcのピクセル座標。

2、srcのピクセル値をA_dash_mapを参照しながらdstへとコピーする。

この流れてプログラム自体は完成したのですが無駄が多いようで処理時間が大幅にかかってしまいます。平面展開をする関数のループが多いことと、ループの中身自体でもA_dash_Mapの検索を行っているため処理速度が遅いのかと思いましたが改善方法が思いつきません。高速化できる部分があればアドバイスをいただきたいです。

初心者のため読みにくいソースコードだと思います。
申し訳ありません。

該当のソースコード

C++

1#include<iostream> 2#include<opencv2/opencv.hpp> 3#include<unordered_map> 4#include<string> 5#define _USE_MATH_DEFINES 6#include<math.h> 7 8#define ANGLE 100 9#define FISHEYE_ANGLE 180 10#define CAMERA_ID 1 11#define CAMERAWIDTH 2448 12#define CAMERAHEIGHT 2048 13 14using namespace std; 15using namespace cv; 16 17// 18// 平面展開の対応点を取得する関数 19// 20//この部分は、授業で習った数式で愚直に計算しました。 21unordered_map<int,Point2d>getFisheyePoint() 22{ 23 //焦点距離[pixel] 24 double pixel_size = 3.45; 25 double focus_mm = 1.8; 26 double focus = focus_mm * 1000 / pixel_size; 27 28 //画像中心までの距離を計算 29 double angle = ANGLE * 2 * M_PI / 360; 30 double d = CAMERAWIDTH / (2 * tan(angle / 2)); 31 32 //画像中心と任意の平面座標Aの距離r 33 double r = 0; 34 35 //点Aの画角を計算 36 double c = 0; 37 38 //点Aの回転角phi 39 double phi = 0; 40 41 //点Aに対応する魚眼座標A' 42 Point2d A_dash; 43 44 //A'を格納するA_dash_Map 45 unordered_map<int, Point2d> A_dash_Map; 46 //魚眼座標A'を計算してA_dash_Mapに保存 47 for (int j = 0;j < CAMERAHEIGHT;j++) 48 { 49 for (int i = 0;i < CAMERAWIDTH;i++) 50 { 51 r = sqrt(pow((i - CAMERAWIDTH / 2), 2) + pow((j - CAMERAHEIGHT / 2), 2)); 52 if (r == 0)continue; 53 c = atan(r / d); 54 phi = acos((i - (CAMERAWIDTH / 2)) / r); 55 A_dash.x = round(focus * c * cos(phi)); 56 if (j <= CAMERAHEIGHT / 2) 57 { 58 A_dash.y = round(focus * c * sin(phi)); 59 } 60 else 61 { 62 A_dash.y = -round(focus * c * sin(phi)); 63 } 64 A_dash_Map.insert(make_pair(j * CAMERAWIDTH + i, A_dash)); 65 } 66 } 67 return A_dash_Map; 68} 69 70// 71// カメラ撮影を行う関数 72// 73Mat getCameraImage() 74{ 75 VideoCapture cap(CAMERA_ID); 76 Mat src, gray; 77 cap >> src; 78 cvtColor(src, gray, COLOR_BGR2GRAY); 79 return gray; 80} 81 82// 83// 平面展開をする関数 84// 85Mat openFisheye(Mat src, Mat dst, unordered_map<int, Point2d> A_dash_Map) 86{ 87 for (int index = 0;index < CAMERAHEIGHT * CAMERAWIDTH;index++) 88 { 89         //対応点を取得する際に座標系がずれているのでここで調節しています。 90 dst.data[index] = src.at<uchar>((CAMERAHEIGHT / 2) - A_dash_Map[index].y, A_dash_Map[index].x + (CAMERAWIDTH / 2)); 91 } 92 return dst; 93} 94 95// 96// main関数 97// 98int main() 99{ 100 cout << "計算開始\n"; 101 unordered_map<int, Point2d > fisheyepoint = getFisheyePoint(); 102 Mat src; 103 Mat open; 104 cout << "撮影開始\n"; 105 src = getCameraImage(); 106 Mat dst = Mat::zeros(Size(CAMERAWIDTH, CAMERAHEIGHT), CV_8UC1); 107 open = openFisheye(src, dst, fisheyepoint); 108 imwrite("open.png", open); 109 cout << "展開終了\n"; 110 111 return 0; 112}

補足情報(FW/ツールのバージョンなど)

OS:bootcampにてMacbookair上でWindous10を動かしています
visual studio2019
CPU:第6世代core-i5
メモリ:8GB

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

A_kirisaki

2021/01/01 15:19

unordered_map は遅そう。まだ Vector 使ったほうが速い?メモリ管理する根性があるなら多分それが一番早い。もしくは行列計算の Eigen 使うとか。いずれにしろわたしの C++ の知識が古いので識者の意見を待ちましょう。
jbpb0

2021/01/01 16:11 編集

ご質問の処理速度に関することではないのですが、srcの画素数とdstの画素数を一致させると、srcは画像の4隅あたりに無効な(被写体が写ってない)画素があるため、srcの有効な(被写体が写っている)画素数よりも、dstの画素数(処理後の画像全体)の方が多くなり、単純に対応する画素位置関係をマッピングさせて移動させるだけだと、dstに(対応するsrcの画素が無くて)値を持たない画素が発生してしまうと思うのですが、それはどのようにして回避しているのでしょうか? dstの画素数をsrcのよりも少なくするとか、dstを補間するとかしているのでしょうか? もし差し支えなければ、教えてください (めんどくさければスルーしてください) 【追記】「mapのキーはdstの座標(x,y)をy*画像の横幅+xのように通し番号?として表現した値。キーに対応するPoint2dはdstのピクセル座標。」は、どちらかがsrcですよね?? キーがsrc座標だと、srcの各画素はdstの一つの画素にしか対応しないので、上記の問題が発生するかもしれません(srcとdstの画素数の関係にもよりますが) キーがdst座標なら、srcの各画素がdstの複数の画素に対応できるので、上記の問題は発生しません(全dst座標のA_dash_Mapを計算すれば)
kpkq307

2021/01/01 16:11

>>A_kirisakさん コメントありがとうございます。 vectorで管理するほうが早いのですね、少し挑戦してみようと思います。
kpkq307

2021/01/01 16:18

>>jbpb0さん ご指摘ありがとうございました。 追記でご指摘いただいた通りキーの部分はdst座標です。
jbpb0

2021/01/01 21:29

とりあえず、main()の各行でかかっている時間を分けて測ることをお勧めします (変数宣言や文字表示の行は、測らなくていいけど)
jbpb0

2021/01/03 00:55

これも処理速度の話では無いですが、画像の中心の座標は CAMERAWIDTH / 2 CAMERAHEIGHT / 2 ではありません 画素数が4x4の画像を想像してください その場合の画像の中心は、画素と画素の間ですから、その位置は 4/2=2 ではありません C言語やPythonの配列のルールに合わせて画素の並びを0から数えるとすれば、画像中心の座標は (4-1)/2=1.5 です 上記は画素数が 偶数x偶数 で説明してますが、奇数の場合も計算式は同じです 画素数が 数千x数千 とかの画像では、処理後の画像を見比べても違いは分からないでしょうけど、一応書いときます
fana

2021/01/06 06:05

(実用上はキャリブレーションで得た光学中心を使うところをとりあえず簡便に適当な値を与えているだけなのだろうから,「画像の中心」とかいう無意味な座標が欲しいわけではないはず.)
guest

回答1

0

ベストアンサー

普通に cv::remap() あたりを使うことを考えてはどうでしょうか.

投稿2021/01/02 12:35

fana

総合スコア11701

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kpkq307

2021/01/07 00:40

fanaさん 回答頂きありがとうございます。 remapを使うことでかなり高速になりました。 そのような関数があることを知らなかったのでとても勉強になりました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問