K-meansについて教えてください。
OpenCVを利用し、肌色部分をHSV値の範囲により緑色に変えています。(while文)ここから顔、右手、左手の3つにK-meansを使って三色に分けたいのですが、どのように変えればいいですか?クラスタの中心の座標を使うので座標でクラスタリングしたいです。
#include "stdafx.h"
#include <cv.h>
#include <highgui.h>
#pragma comment(lib,"cv.lib")
#pragma comment(lib,"cxcore.lib")
#pragma comment(lib,"highgui.lib")
#include <math.h>
#define LINE_THICKNESS -1
#define LINE_TYPE 8
#define SHIFT 0
#define MAX_CLUSTERS (3)
int _tmain(int argc, _TCHAR* argv[])
{
CvSeq *objects;
CvRect *r;
char *cascade_name;
int key;
int i = 1;
//////////////////////////////////////////////////////////////////
int x, y, W, H;
int ofs, idx;
unsigned char *pix;
int pixCnt;
int pixCnt2;
int h, s, v;
double n, m;
double aveH, aveS, aveV;
double divh, divs, divv;
double divH, divS, divV;
double aveH2, aveS2, aveV2;
double sigmaH, sigmaS, sigmaV;
///////////////////////////////////////////////////////////////////////////
CvCapture* src;
IplImage *frame1;
IplImage *frame2;
IplImage *frame3;
CvHaarClassifierCascade *cascade;
CvMemStorage *storage;
cvNamedWindow ("原画像");
cvNamedWindow ("HSV画像");
cvNamedWindow ("処理結果");
// カスケード・ファイルの読み込み
cascade_name = "haarcascade_frontalface_default.xml";
cascade = (CvHaarClassifierCascade *) cvLoad(cascade_name, 0, 0, 0);
storage = cvCreateMemStorage (0);
cvClearMemStorage(storage);
src = cvCaptureFromCAM(0);
if(src == NULL){printf("映像が取得できません。\n");
cvWaitKey(0);
return -1;
}
frame1 = cvQueryFrame(src);
frame2 = cvCreateImage(cvGetSize(frame1), IPL_DEPTH_8U, 3);
frame3 = cvCreateImage(cvGetSize(frame1), IPL_DEPTH_8U, 3);
//顔領域の検出
objects = cvHaarDetectObjects(frame1, cascade, storage, 1.1, 3, 0, cvSize(0, 0));
r = (CvRect *) cvGetSeqElem(objects, i);
cvRectangle(frame1, cvPoint(r->x, r->y)
, cvPoint(r->x + r->width, r->y + r->height), CV_RGB(0, 255, 0), 4);
cvCvtColor(frame1, frame2, CV_BGR2HSV);
/////////////////////////////////////////////////////////////////////////
pix = (unsigned char*)frame2->imageData;
pixCnt = frame2->width*frame2->height;
pixCnt2 = r->width/6*r->height/6;
printf( "%d\n", pixCnt );
printf("pixCnt2: %d\n", pixCnt2);
aveV = aveS = aveH = 0;
for( y=r->y+r->height/2; y<r->y+r->height/2+r->height/6; y++ ) {
ofs = y*frame2->widthStep;
for( x=r->x+r->width*2/3; x<r->x+r->width*2/3+r->width/6; x++ ) {
idx = ofs + x*3;
aveS += pix[idx+1];
if (pix[idx+1] >= 0) {
aveH += pix[idx];
} else {
pixCnt2--;
}
}
}
aveS /= pixCnt2;
aveH /= pixCnt2;
printf("H%f,S%f,V%f\n", aveH, aveS, aveV);
aveH2 = 0;
divh = 0;
for( y=r->y; y<r->y+r->height; y++ ) {
ofs = y*frame2->widthStep;
for( x=r->x; x<r->x+r->width; x++ ) {
idx = ofs + x*3;
aveH2 += (pix[idx]-aveH)*(pix[idx]-aveH);
}
}
aveH2 /= pixCnt2;
divH = aveH2;
sigmaH = sqrt(aveH2);
while(1){
frame1 = cvQueryFrame(src); if(frame1 == NULL) break; // 1フレーム取得
cvRectangle(frame1, cvPoint(r->x, r->y) // 検出部に四角枠描画
, cvPoint(r->x + r->width, r->y + r->height), CV_RGB(0, 255, 0), 4);
cvCvtColor(frame1, frame2, CV_BGR2HSV);
cvShowImage ("原画像", frame1); // 検出結果表示
cvShowImage ("HSV画像", frame2); // 検出結果表示
for( y=0; y<frame2->height; y++ ) {
ofs = y*frame2->widthStep;
for( x=0; x<frame2->width; x++ ) {
idx = ofs + x*3;
v = pix[idx+2];
s = pix[idx+1];
h = pix[idx];
if ((h > aveH-sigmaH && h < aveH+sigmaH)&& (s > aveS-10 && s < aveS+10)) {
pix[idx+2] = 230;
pix[idx+1] = 230;
pix[idx] = 60;
}
else {
pix[idx+2] = 0;
pix[idx+1] = 0;
pix[idx] = 0;
}
}
}
cvCvtColor(frame2, frame3, CV_HSV2BGR);
cvShowImage ("処理結果", frame3); // 検出結果表示
if(cvWaitKey(33) == 27) break; // ESCキーを押した時終了
}
// ウィンドウ・キャプチャ・メモリの解放
cvReleaseImage(&frame1);
cvReleaseImage(&frame2);
cvReleaseImage(&frame3);
cvDestroyWindow("原画像");
cvDestroyWindow("HSV画像");
cvDestroyWindow("処理結果");
cvReleaseCapture(&src);
cvReleaseMemStorage(&storage);
return 0;
}
お礼
ややこしくなってすみません、以下に続きです。 if(HSV[y][x].H<0){ HSV[y][x].H += 360;} HSV[y][x].S = 256*(max-min)/max; //s /= max; 円柱モデルの場合 HSV[y][x].V = max; } } } です. ・frameの中身: cvCreateImage等の設定はどうなっているのか 動画像をの一枚の静止画を取り込んでいます. frameに移すときはcvQueryFrameを使用しています. ・src_bufの中身: 変数宣言、領域確保など >src_buf = new short[ frame->width * frame->height ]; で領域を確保しています. ・HSVの中身: 変数宣言、領域確保、この箇所に来た段階での値など >HSVはプログラムの最初に示していた構造体を使用しています. 今プログラムを動かせる環境ではないために値を見ることができませんが正常値でした. ・画像の作成方法: frame をcvSaveImageしたもの? >cvSaveImageを使用しています. 長々と申し訳ありません. 回答ありがとうございます. よろしくおねがいします.
補足
説明が不足していてすみません. 一応デバックして,2値化の処理の手前までは通常に画像が表示されていたので, ここの部分が原因かと思い貼り付けてしまいました… >> xx=1920; >> yy=1080; >としたものをループカウンタに使ってしまっていますが、これはよいのですか? >ループ終了時点で xx = frame->width, yy=frame->heghtになって、元にもどっているかもしれませんが。 の部分ですが,これは掲示板で書き込む際に間違えて書き込んでしまいました.ややこしくてすみません. 不足していたプログラムを以下に貼り付けます. この続きが上記のプログラムへ続きます. bmpへの表示はSaveImage("output.bpm",frame);をつかっています. struct _HSV { int H; int S; int V; }HSV[1920][1080]; int main (int argc, char **argv) { … //初期設定 // (1)コマンド引数によって指定された番号のカメラに対するキャプチャ構造体を作成する if (argc == 1 || (argc == 2 && strlen (argv[1]) == 1 && isdigit (argv[1][0]))) capture = cvCreateCameraCapture (argc == 2 ? argv[1][0] - '0' : 0); /* この設定は,利用するカメラに依存する*/ // (2)キャプチャサイズを設定する. cvNamedWindow ("Capture", CV_WINDOW_AUTOSIZE); frame = cvQueryFrame (capture); src_buf = new short[ frame->width * frame->height ]; // (3)カメラから画像をキャプチャする while (1) { frame = cvQueryFrame (capture); for(y = 0; y < frame->height; y++) { for(x = 0; x < frame->width; x++) { p[0] = frame->imageData[frame->widthStep * y + x * 3]; // B p[1] = frame->imageData[frame->widthStep * y + x * 3 + 1]; // G p[2] = frame->imageData[frame->widthStep * y + x * 3 + 2]; // R if (p[0] >= p[1] && p[0] >= p[2]) {max = p[0];} //Bがmax else if (p[1] >= p[0] && p[1] >= p[2]) {max = p[1];} //Gがmax else {max = p[2];} //Rがmax if (p[0] <= p[1] && p[0] <= p[2]) {min = p[0];} //Bがmin else if (p[1] <= p[0] && p[1] <= p[2]) {min = p[1];} //Gがmin else {min = p[2];} //Rがmin if(max==min){ HSV[y][x].H = 0; HSV[y][x].S = 0; HSV[y][x].V = p[0]; //R } else { if(max==p[2]) { HSV[y][x].H = 60 * (p[1]-p[0])/(max-min); } else if(max==p[1]) { HSV[y][x].H = 60 * (p[0]-p[2])/(max-min); HSV[y][x].H += 120; } else { HSV[y][x].H = 60 * (p[2]-p[1])/(max-min); HSV[y][x].H += 240; }