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

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

新規登録して質問してみよう
ただいま回答率
85.47%
OpenCV

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

Qt

QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。

C++

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

Q&A

解決済

2回答

6193閲覧

OpenCV Matからunsigned char への変換に関して[C++/Qt]

M_Aki

総合スコア1

OpenCV

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

Qt

QtはGUIプログラムの開発で広く使われているクロスプラットフォーム開発のフレームワークです。

C++

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

0グッド

0クリップ

投稿2021/07/30 03:19

編集2021/08/04 06:54

前提・実現したいこと

カメラからとってきた画像を任意のサイズで切り取り、処理するソフトを作成しています。
OpenCVを利用してカメラの画像を取得したので元の画像はMat形式なのですが、処理部分はunsigned charで処理をするのでMatからunsigned charへ変換する必要があります。

カメラからの画像はカラーなのですが、処理の都合グレースケールへ変換しています。2021/08/04追記
frame_buffer:カメラからの画像
frame_GRAY:グレースケールへ変換後の画像

C++

1 cv::cvtColor(frame_buffer,frame_GRAY,cv::COLOR_BGR2GRAY);

また、切り抜く幅が、4の倍数意外だと画像が乱れることが分かったので、一旦画像を大きく切り取ってunsigned charの変数へコピーした後、目的の大きさのunsigned charの変数へコピーをしています。
そこで下記のような関数を作成しました。

発生している問題・エラーメッセージ

エラーは起こっていないのですが、切り取る幅を4の倍数以外にすると画像が乱れてしまいます。
具体的には列ごとにずれが起こり、画像が斜めにゆがんだようになります。
また、画像の下部に想定している画像のデータ以外のものが入っているようで、黒い線が入ったようになります。

例 2021/08/04追記
処理する画像(グレースケール変換後)900×583pixel
元の画像
幅300pixel 高さ300pixelで切り取り
幅300pixel
幅301pixel 高さ300pixelで切り取り
イメージ説明

該当のソースコード

C++

1void ConvMatToUchar(unsigned char *ucharImage, const cv::Mat* src, cv::Rect rect) 2{ 3 cv::Mat origin_img = *src; 4 cv::Rect tmp_rect = rect; 5 cv::Mat tmp_img; 6 7 //幅が4の倍数以外だと画像がずれるので、4の倍数になるように幅を延長。 8 int x_offset = 0; 9 int w_offset = 0; 10 int remain = rect.width % 4; 11 12 //画面右端での例外処理 13 if(remain != 0) 14 { 15 w_offset = 4 - remain; 16 if(rect.x + rect.width + w_offset > CAM_W) 17 { 18 x_offset = w_offset; 19 } 20 } 21 tmp_img = cv::Mat(origin_img,cv::Rect(rect.x + x_offset, rect.y, rect.width + w_offset, rect.height)).clone(); 22 23 //CAM_WとCAM_Hは元画像の幅と高さ 2021/08/04追記 24 unsigned char tmp_ucharImage[CAM_W * CAM_H]; 25 ::memcpy(tmp_ucharImage, tmp_img.data, static_cast<size_t>((rect.width + w_offset) * rect.height)); 26 for (int i = 0; i < rect.height; i++) 27 { 28 unsigned char *p = ucharImage + i * rect.width; 29 unsigned char *p_tmp = tmp_ucharImage + i * (rect.width + w_offset) + x_offset; 30 ::memcpy(p, p_tmp, static_cast<size_t>(rect.width)); 31 } 32 tmp_img.release(); 33}

表示部分2021/08/04追記

C++

1 int W = 301; 2 int H = 300; 3  cv::Rect cv_roi_rect = cv::Rect(0,0,W, H); 4 //関数の呼び出し 5 ConvMatToUChar(pbImage, &frame_GRAY, cv_roi_rect); 6 //Qtで表示 7 QImage tmp_img = QImage(pbImage, W, H, QImage::Format_Indexed8); 8 QPixmap tmp_pixmap = QPixmap::fromImage(tmp_img); 9 Scene.addPixmap(tmp_pixmap);

試したこと

unsigned char *p_tmp = tmp_ucharImage + i * (rect.width + w_offset) + x_offset;

上記の部分を下記のように変更すると、画像の斜めにゆがむことは解決できたように見えたのですが(Qtで表示)、下部の黒い線はなくなりませんでした。

unsigned char *p_tmp = tmp_ucharImage + i * rect.width + x_offset;

変更後画像 2021/08/04追記
幅301pixel 高さ300pixelで切り取り
イメージ説明

また、処理後の画像を確認すると斜めにゆがんでおり、問題が解決したわけでもないようです。
このことから、どうも自分が考えているようなメモリの並びになっていないことは何となくわかったのですが、OpenCVに関する知識が不足しているため、どのようなメモリ構造で保持されているのかを理解することができませんでした。

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

開発環境
Windows10 64bit
Qt5.13.2
OpenCV 4.5.2

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

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

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

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

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

guest

回答2

0

cv::Mat の一行分の大きさ(byte数)は step で得られます。

投稿2021/07/30 04:52

編集2021/07/30 04:59
episteme

総合スコア16614

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

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

0

ベストアンサー

(1)ずれるとか歪むとか言われても意味不明です

どうやらその提示コードの処理の結果というのは

unsigned char *ucharImage

の指す先に格納される様子ですが,
ふつーにこの型を見ればこれは1次元なbyte列と思われ,「斜めがどうの」言われても,何のことなのかは他者には不明です.
1次元のbyte列に縦とか横とか斜めとかいう意味をどう与えているのか不明だからです.

すなわち,
あなたがそのbyte列に何らかのフォーマットを想定しているとき,そのフォーマットの説明をしなければ他者には

  • そのフォーマットに即したデータを作ろうとしている場所(提示コード)
  • そのフォーマットに即して解釈しようとしている個所(どうなってるのか不明)

のどちらに間違いがあるのかはわからないわけで.

#あと,(差し支えなければ)現象を「ずれる」とかいう言葉で頑張って表現するよりは,実際のbefore/afterの絵を示した方が話が早いかと.

(2)そもそも元の画像はどうなってるの?

src の1画素のbit深度とかはどうなってるの? っていう.

例えば1画素の情報が3[byte]だとすれば,N画素分のデータをどこぞにコピーするには N*3[byte] 分のコピーを行うことになるかと思うのですが,そこのところ(bit深度)が扱われている様子が見えません.
そこら辺に間違いはないのですか?

(3)Suggestion

mamcpyとかで「ごっそりと」情報を持っていくという行為は,大元のデータフォーマットを完全に把握して「そうのようにやっても絶対に大丈夫」とわかった上でやるべきことです.
少なくとも cv::Mat のデータ構造を知らない状態でやるべきことではありません.

いきなり memcpy のような危険物を使うのではなく,
まずは自前のforループとかでちまちまと1画素分ずつの情報をコピーする処理を実装してみるべきではないでしょうか.

で,それで思うような結果が得られたならば,その動作を崩さない範囲で効率化する.
(思うような結果がこの時点で得られないならば,そもそも話自体がどこかで間違っているのだから,いきなり高効率なコードを書いたところで意味ない)

あと,コードを見るに各所に演算が見られるので,それらの演算結果が本当に思っている値になってるのか?という確認作業をしてみるべきでしょう.


[質問編集されたので追記]

QImage tmp_img = QImage(pbImage, W, H, QImage::Format_Indexed8);

このコードが妥当か否かは,QImage とかいうやつの仕様次第でしょうから,その点を確認すべきです.
現象から察するに,各行の先頭位置が4の倍数にアラインされるようにデータにpaddingみたいなのが必要な型なのではないかと.

まとめると,まずあなたがやるべきことは,

  • QImageの要求するデータフォーマット仕様についてきちんと把握する
  • そのうえで,この行で引数に与えられている pbImage の内容が正当であることを確認する

の2点です.

投稿2021/07/30 04:02

編集2021/08/04 03:05
fana

総合スコア11675

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

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

fana

2021/07/30 04:04

4の倍数がどうの,と言っていることから,「8bit4chみたいな画像なのかな?」とか想像できるけども,そういう情報は明示されたい.
fana

2021/08/04 03:14 編集

この話には, OpenCVのMatのデータフォーマット ↓変換(1) "unsigned charで処理する" とかいう世界が期待するデータフォーマット ↓変換(2) QImage とかいうやつのデータフォーマット と,フォーマット変換が2つ存在するわけで. であれば,当然ながらそれぞれのフォーマットをきちんと把握しなきゃならんのだから,そこからやるべき. 現状が「知識不足でわからん」なら,その知識(情報)を探すべき.コードを書くよりも前に.
M_Aki

2021/08/04 06:52 編集

大変参考になりました。 表示に使っているQImageへのフォーマットの変換時の問題でした。 QImage tmp_img = QImage(pbImage, W, H, QImage::Format_Indexed8); を QImage tmp_img = QImage(pbImage, W, H, W, QImage::Format_Indexed8); に変更することで解決しました。 明示されていない場合、追記で書かれていたように各行の先頭位置が4の倍数になるように調整されるようで、1ライン当たりのバイト数を明示的に指定する必要があったようです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問