アプリ版:「スタンプのみでお礼する」機能のリリースについて

いつも大変お世話になっております。
ピクセルの値を「ゼロ(黒)」にしたピクセルを読み込んでも「255(白)」となってしまい困っています。cvShowImageでこの画像を表示すると、どうみてもその場所は黒です。
なぜ「255(白)」になるのかわからず困っています。
原因を教えていただけませんでしょうか。
詳細は以下のとおりです。

out_imgという変数名のCV_LOAD_IMAGE_COLORの真っ白の画像
out_img = cvLoadImage("C:...\shiro.jpg", CV_LOAD_IMAGE_COLOR);
の6×6ピクセルの値を、以下の文で「黒」にして
   out_img->imageData[out_img->widthStep * 6 + 6 * 3] = 0;
   out_img->imageData[out_img->widthStep * 6 + 6 * 3 + 1] = 0;
   out_img->imageData[out_img->widthStep * 6 + 6 * 3 + 2] = 0;
保存し、再び(別のソリューションで)src_img変数としてこの画像を読み込み
   src_img = cvLoadImage("C:...¥shiro.jpg", CV_LOAD_IMAGE_COLOR);
グレースケール化し、   
   src_img_gray = cvCreateImage(cvGetSize(src_img), IPL_DEPTH_8U, 1);
tmp_img = cvCreateImage(cvGetSize(src_img), IPL_DEPTH_8U, 1
ゼロイチの2値化(バイナリ画像)に変換し、
cvCvtColor(src_img, src_img_gray, CV_BGR2GRAY);
閾値でゼロイチに変換した後(なぜか上のsrc_img_grayでは黒がゼロにはならないため)
cvAdaptiveThreshold(src_img_gray, tmp_img, 255, CV_ADAPTIVE_THRESH_GAUSSIAN_C, CV_THRESH_BINARY, 7, 8);

6×6の位置の値(変数iro)を確認すると「255」となります。

for (x = 0; x < tmp_img->width; x = x + 1){ // x 座標を 1ピクセルずつ進める
for (y = 0; y < tmp_img->height; y = y + 1){ // y 座標を 1ピクセルずつ進める

if (x == 6 && y == 6){
x = 6; (ここでデバックを掛けて、下の変数iroの値がいくつかを確認しています。ゼロであってほしいのですが、255です。
}

uchar iro = tmp_img->imageData[tmp_img->widthStep * y + x * 3];
if (iro == 0){

画像読み込みの引数がおかしいのだろうと思い、
CV_LOAD_IMAGE_ANYCOLOR や「0」にしたところ
今度はfor文のところでエラーになり、走査が始まりません。

for (x = 0; x < tmp_img->width; x = x + 1){ // x 座標を 1ピクセルずつ進める
for (y = 0; y < tmp_img->height; y = y + 1){ // y 座標を 1ピクセルずつ進める

初歩的な質問で申し訳ありません。
ぜひアドバイスをいただけると助かります。
どうぞよろしくお願い致します。

A 回答 (2件)

imgData の中は、次のようにデータが並んでいます


0 * widthStep から
 X=0,Y=0のデータ
 X=1,Y=0のデータ
...
 X=x,Y=0のデータ
...
 X=width-1,Y=0のデータ

1 * widthStep から
 X=0,Y=1のデータ
 X=1,Y=1のデータ
...
 X=x,Y=1のデータ
...
 X=width-1,Y=1のデータ

y * widthStep から
...
 X=x,Y=yのデータ
...

y * widthStep から
...
 X=x,Y=yのデータ
...

(height-1)*widthStepから
...
 X=width-1,Y=height-1のデータ


各座標データは次のようになっています
チャンネル1のデータ
チャンネル2のデータ(2チャンネル以上のとき)
チャンネル3のデータ(3チャンネル以上のとき)
チャンネル4のデータ(4チャンネル以上のとき)

各チャンネルのデータは IPL_DEPTH_* で示されるバイト数だけ使われます。
IPL_DEPTH_8U なら1バイト
IPL_DEPTH_32F なら4バイト

以上のことから、座標(x,y)のチャンネルch のデータは
imageData[ y * widhtStep + 1データあたりのバイト数 * ( x * 総チャンネル数 + (ch -1)) ]
から始まる 「1データあたりのバイト数」のデータ、ということになります



out_img = cvLoadImage("C:...\shiro.jpg", CV_LOAD_IMAGE_COLOR);
だと、おそらく IPL_DEPTH_8U(1バイト), チャンネル数3になっているはずです。
これだと
out_img->imageData[out_img->widthStep * y + x * 3] = 0; // 3チャンネルのうちのチャンネル1: B
out_img->imageData[out_img->widthStep * y + x * 3 + 1] = 0; // 3チャンネルのうちのチャンネル2: G
out_img->imageData[out_img->widthStep * y + x * 3 + 2] = 0; // 3チャンネルのうちのチャンネル3: R
となります。
/* 元の画像が 6x6 なら、 x=6,y=6という座標は存在しません。 */
/* よって out_img->widthStep * 6 + 6 * 3 は間違いです */

ですが
cvCreateImage(cvGetSize(src_img), IPL_DEPTH_8U, 1)
だと、IPL_DEPTH_8U(1バイト), チャンネル数1 です。
imageData[ y * widhtStep + 1データあたりのバイト数 * ( x * 総チャンネル数 + (ch -1)) ]
にあてはめれば
imageData[ y * widhtStep + 1 * ( x * 1 + (1 -1)) ]
です。



前にも言ったと思いますが、C言語では、配列の範囲外かどうかのチェックはしません。
例え範囲外でも、とにかくそのアドレスをアクセスしに行きます。
ちゃんと正しい範囲かどうかを管理するのは、プログラム制作者の仕事です。

そこが不完全なら、まずは、チェックしてくれるSet/Getを使って「正しい処理」をするプログラムを作ることから始めてはどうでしょうか。
    • good
    • 0

>    out_img->imageData[out_img->widthStep * 6 + 6 * 3] = 0;


>   out_img->imageData[out_img->widthStep * 6 + 6 * 3 + 1] = 0;
>   out_img->imageData[out_img->widthStep * 6 + 6 * 3 + 2] = 0;

これでは座標(6,6)であり、6x6の画像(x=0~5,y=0~5)に存在しない点です


> なぜか上のsrc_img_grayでは黒がゼロにはならない

JPEGで保存しているようですが、JPEGは非可逆圧縮を使っています。
不可逆とは、元に戻らない、という意味です。
「見た目が似ていれば、ちょっとくらい色が違っていてもいい」という考えで、高圧縮率を実現しています。
元の色が黒(0)だったら、JPEG保存したものを読み出したときに「だいたい黒(1とか2とか)」になってればいい、というものです。
そのまま扱いたいのなら、PNG等の可逆圧縮やBMP等の非圧縮形式を使いましょう。



> uchar iro = tmp_img->imageData[tmp_img->widthStep * y + x * 3];

なぜ x *3 になっているのですか?

前にも言いましたが、配列や画像の範囲について注意が足りません。
まずは、imageDataに直接アクセスするのではなく、 cvSet2D/cvGet2D やcvSetReal2D/cvGetReal2D で x,y を指定するようにしては。

imageDataを使いたいのなら、C言語の配列とポインタをしっかり学習してマスターしてからにしましょう。
    • good
    • 0
この回答へのお礼

kmee様

すぐに回答くださってありがとうございます。
いろいろと今回もミスを教えてくださって助かりました。
pngにしてみたところゼロになりました。
画像の拡張子にも注意します。

できればぜひ、もうひとつ教えていただきたいのですが
なぜimageDataの場合、「*3」は不要なのでしょうか?
いくら2値にしたとはいえ、配列はRGBなので
3番目の値=画素値、だと理解していました。
cvSet2Dなどを使用してみますが、
なぜimageDataでRGB画像の場合に「*3」ではNGなのか
自分で調べた結果の理解ですので、
恥ずかしいのですが混乱しています。
もし教えていただけると嬉しいです。
よろしくお願い致します。

お礼日時:2015/01/20 01:16

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!