2015-09-14 22 views
1

我想提取用戶剪影並將其放在我的圖像上方。我能夠製作一個面具,並從rgb圖像剪下用戶。但輪廓很混亂。如何在simple-openni中清除用戶的掩碼?

問題是我如何使面具更精確(適合真正的用戶)。我已經嘗試過ERODE-DILATE過濾器,但他們做的不多。也許我需要一些像Photoshop一樣的羽毛過濾器。或者我不知道。

這是我的代碼。

import SimpleOpenNI.*; 
SimpleOpenNI context; 
PImage mask; 
void setup() 
{ 
    size(640*2, 480); 
    context = new SimpleOpenNI(this); 
    if (context.isInit() == false) 
    {   
    exit(); 
    return; 
    } 
    context.enableDepth(); 
    context.enableRGB(); 
    context.enableUser(); 
    context.alternativeViewPointDepthToImage();  
} 

void draw() 
{ 
    frame.setTitle(int(frameRate) + " fps");  
    context.update(); 
    int[] userMap = context.userMap(); 
    background(0, 0, 0); 
    mask = loadImage("black640.jpg"); //just a black image 
    int xSize = context.depthWidth(); 
    int ySize = context.depthHeight(); 
    mask.loadPixels(); 
    for (int y = 0; y < ySize; y++) {  
    for (int x = 0; x < xSize; x++) {   
     int index = x + y*xSize;  
     if (userMap[index]>0) { 
     mask.pixels[index]=color(255, 255, 255); 
     } 
    } 
    } 
    mask.updatePixels(); 
    image(mask, 0, 0); 
    mask.filter(DILATE); 
    mask.filter(DILATE);   
    PImage rgb = context.rgbImage(); 
    rgb.mask(mask); 
    image(rgb, context.depthWidth() + 10, 0); 
} 

回答

1

我已經嘗試過內置侵蝕擴張模糊處理。但他們效率很低。每次我在img.filter(BLUR,blurAmount)中增加blurAmount時,我的FPS減少5幀。 所以我決定嘗試opencv。相比之下,它好得多。結果令人滿意。

import SimpleOpenNI.*; 
import processing.video.*; 
import gab.opencv.*; 
SimpleOpenNI context; 
OpenCV opencv; 
PImage mask; 
int numPixels = 640*480; 
int dilateAmt = 1; 
int erodeAmt = 1; 
int blurAmt = 1; 
Movie mov; 
void setup(){ 
    opencv = new OpenCV(this, 640, 480); 
    size(640*2, 480); 
    context = new SimpleOpenNI(this); 
    if (context.isInit() == false) {   
    exit(); 
    return; 
    } 
    context.enableDepth(); 
    context.enableRGB(); 
    context.enableUser(); 
    context.alternativeViewPointDepthToImage(); 
    mask = createImage(640, 480, RGB); 
    mov = new Movie(this, "wild.mp4"); 
    mov.play(); 
    mov.speed(5); 
    mov.volume(0); 
} 
void movieEvent(Movie m) { 
    m.read(); 
} 
void draw() { 
    frame.setTitle(int(frameRate) + " fps");  
    context.update(); 
    int[] userMap = context.userMap(); 
    background(0, 0, 0); 
    mask.loadPixels(); 
    for (int i = 0; i < numPixels; i++) { 
    mask.pixels[i] = userMap[i] > 0 ? color(255) : color(0); 
    } 
    mask.updatePixels(); 
    opencv.loadImage(mask); 
    opencv.gray(); 
    for (int i = 0; i < erodeAmt; i++) { 
    opencv.erode(); 
    } 
    for (int i = 0; i < dilateAmt; i++) { 
    opencv.dilate(); 
    } 
    if (blurAmt>0) {//blur with 0 amount causes error 
    opencv.blur(blurAmt); 
    } 
    mask = opencv.getSnapshot(); 
    image(mask, 0, 0); 
    PImage rgb = context.rgbImage(); 
    rgb.mask(mask); 
    image(mov, context.depthWidth() + 10, 0); 
    image(rgb, context.depthWidth() + 10, 0); 
    fill(255); 
    text("erodeAmt: " + erodeAmt + "\tdilateAmt: " + dilateAmt + "\tblurAmt: " + blurAmt, 15, 15); 
} 
void keyPressed() { 
    if (key == 'e') erodeAmt--; 
    if (key == 'E') erodeAmt++; 
    if (key == 'd') dilateAmt--; 
    if (key == 'D') dilateAmt++; 
    if (key == 'b') blurAmt--; 
    if (key == 'B') blurAmt++; 
    //constrain values 
    if (erodeAmt < 0) erodeAmt = 0; 
    if (dilateAmt < 0) dilateAmt = 0; 
    if (blurAmt < 0) blurAmt = 0; 
} 
+0

好點,關於OpenCV和你使用的包裝是相當不錯的。我應該從一開始就建議這樣做(但試圖用依賴關係來簡化事情)。 –

1

這是很好你對齊的RGB和深度流。 有可能在效率方面得到改善幾件事情:

無需重新加載一個黑色圖像的每一個幀(抽獎()循環),因爲你無論如何修改的所有像素:

mask = loadImage("black640.jpg"); //just a black image 

而且,因爲你不需要的X,Y座標,你通過用戶數據循環,你可以使用一個for循環應該是快了一點:

for(int i = 0 ; i < numPixels ; i++){ 
    mask.pixels[i] = userMap[i] > 0 ? color(255) : color(0); 
    } 

代替:

for (int y = 0; y < ySize; y++) {  
    for (int x = 0; x < xSize; x++) {   
     int index = x + y*xSize;  
     if (userMap[index]>0) { 
     mask.pixels[index]=color(255, 255, 255); 
     } 
    } 
    } 

你可以做的另一件事哈克是檢索userImage()代替userData()從SimpleOpenNI,並應用THRESHOLD過濾器給它,這在理論上應該給你同樣的結果如上。

例如:

int[] userMap = context.userMap(); 
    background(0, 0, 0); 
    mask = loadImage("black640.jpg"); //just a black image 
    int xSize = context.depthWidth(); 
    int ySize = context.depthHeight(); 
    mask.loadPixels(); 
    for (int y = 0; y < ySize; y++) {  
    for (int x = 0; x < xSize; x++) {   
     int index = x + y*xSize;  
     if (userMap[index]>0) { 
     mask.pixels[index]=color(255, 255, 255); 
     } 
    } 
    } 

可能是:

mask = context.userImage(); 
mask.filter(THRESHOLD); 

在濾波方面,如果你要收縮的輪廓,你應該ERODE和bluring應該給你一點是Photoshop的像羽化。

注意某些filter()調用帶參數(如BLUR),但其他人不喜歡ERODE/DILATE形態濾波器,但你仍然可以推出自己的循環來面對這一切。

我還推薦在玩過濾器時有一些容易調整的界面(可以是花式滑塊或簡單的鍵盤快捷鍵)。

這裏是在重構草圖粗略的嘗試與上述評論:

import SimpleOpenNI.*; 
SimpleOpenNI context; 
PImage mask; 
int numPixels = 640*480; 

int dilateAmt = 1; 
int erodeAmt = 1; 
int blurAmt = 0; 
void setup() 
{ 
    size(640*2, 480); 
    context = new SimpleOpenNI(this); 

    if (context.isInit() == false) 
    {   
    exit(); 
    return; 
    } 
    context.enableDepth(); 
    context.enableRGB(); 
    context.enableUser(); 
    context.alternativeViewPointDepthToImage(); 
    mask = createImage(640,480,RGB); 
} 

void draw() 
{ 
    frame.setTitle(int(frameRate) + " fps");  
    context.update(); 
    int[] userMap = context.userMap(); 
    background(0, 0, 0); 

    //you don't need to keep reloading the image every single frame since you're updating all the pixels bellow anyway 
// mask = loadImage("black640.jpg"); //just a black image 

// mask.loadPixels(); 

// int xSize = context.depthWidth(); 
// int ySize = context.depthHeight(); 
// for (int y = 0; y < ySize; y++) {  
// for (int x = 0; x < xSize; x++) {   
//  int index = x + y*xSize;  
//  if (userMap[index]>0) { 
//  mask.pixels[index]=color(255, 255, 255); 
//  } 
// } 
// } 

    //a single loop is usually faster than a nested loop and you don't need the x,y coordinates anyway 
    for(int i = 0 ; i < numPixels ; i++){ 
    mask.pixels[i] = userMap[i] > 0 ? color(255) : color(0); 
    } 
    //erode 
    for(int i = 0 ; i < erodeAmt ; i++) mask.filter(ERODE); 
    //dilate 
    for(int i = 0 ; i < dilateAmt; i++) mask.filter(DILATE); 
    //blur 
    mask.filter(BLUR,blurAmt); 

    mask.updatePixels(); 
    //preview the mask after you process it 
    image(mask, 0, 0); 

    PImage rgb = context.rgbImage(); 
    rgb.mask(mask); 
    image(rgb, context.depthWidth() + 10, 0); 

    //print filter values for debugging purposes 
    fill(255); 
    text("erodeAmt: " + erodeAmt + "\tdilateAmt: " + dilateAmt + "\tblurAmt: " + blurAmt,15,15); 
} 
void keyPressed(){ 
    if(key == 'e') erodeAmt--; 
    if(key == 'E') erodeAmt++; 
    if(key == 'd') dilateAmt--; 
    if(key == 'D') dilateAmt++; 
    if(key == 'b') blurAmt--; 
    if(key == 'B') blurAmt++; 
    //constrain values 
    if(erodeAmt < 0) erodeAmt = 0; 
    if(dilateAmt < 0) dilateAmt = 0; 
    if(blurAmt < 0) blurAmt = 0; 
} 

可惜我不能與實際傳感器測試,現在,所以請使用的概念解釋,但要注意充分裸露草圖代碼未經測試。

上面的草圖(如果它運行)應該允許您使用鍵來控制過濾器參數(e/E降低/增加侵蝕,d/D擴張,b/B模糊)。希望你會得到滿意的結果。

在使用SimpleOpenNI時,我一般建議錄製一個。oni文件(查看RecorderPlay示例)最常見的用例。這樣可以爲您節省一些測試時間,並允許您在傳感器分離的情況下進行遠程工作。有一點要記住,深度分辨率減少到記錄的一半(但使用usingRecording布爾標誌應該保持安全)

最後也可能是最重要的一點是關於最終結果的質量。如果源圖像不容易處理,您的結果圖像不會好得多。原始Kinect傳感器的深度數據不是很好。華碩傳感器感覺稍微穩定一點,但在大多數情況下,差異仍然可以忽略不計。如果您要堅持使用其中一種傳感器,請確保您有清晰的背景和體面的照明(沒有太多直接的暖光(陽光,白熾燈泡等),因爲它們可能會干擾傳感器)

如果你想要更精確的用戶剪切,並且上面的過濾不能得到你想要的結果,請考慮切換到更好的傳感器,如KinectV2。深度質量好得多,傳感器不易受溫暖的光照。這可能意味着你需要使用Windows(我看到有一個KinectPV2包裝可用)或OpenFrameworks(類似於處理的庫的C++集合),ofxKinectV2