読者です 読者をやめる 読者になる 読者になる

Natural Software

KinectなどのDepthセンサーを中心に活動しています

Kinect で漫画風加工をしてみる

Kinect for Windows

最近何かと流行りの漫画風加工の方法を紹介しているブログがあったので、これをKinectでやってみました。

こんな感じになりました。面白いですw

元々はiPhone用のコードなので、ところどころObjective-Cです。これをC++にしてみました(OpenCVはCのインタフェースのままです)。Kinect アクセスの部分はまだ公開してない自作のライブラリを使ってるので、そのままでは実行できませんが、そこだけ変えてあげれば動くと思います。

Objective-CC++よりも、OpenCVのCインタフェース→OpenCVC++インタフェース変換のほうが難しいですね(個人的には)。

関数名も変かもしれませんが、気にせず^^;

// thanks:http://dev.classmethod.jp/smartphone/opencv-manga-2/

#include <opencv2\opencv.hpp>

#include "../../NaturalSoftwareCpp/NaturalSoftware.Kinect/KinectSensor.h"

void SerAlpha( IplImage *img, int aplha )

{

for ( int i = 0; i < img->imageSize; i += 4 ) {

if ( ((unsigned char)img->imageData[i] == aplha) &&

((unsigned char)img->imageData[i+1] == aplha) &&

((unsigned char)img->imageData[i+2] == aplha) ) {

img->imageData[i+3] = 0;

}

else {

img->imageData[i+3] = 255;

}

}

}

IplImage* LineFilter( IplImage* source )

{

cv::Ptr<IplImage> grayscaleImage = cvCreateImage(cvGetSize(source), IPL_DEPTH_8U, 1);

cv::Ptr<IplImage> edgeImage = cvCreateImage(cvGetSize(source), IPL_DEPTH_8U, 1);

// 2.グレースケール画像に変換

cvCvtColor(source, grayscaleImage, CV_BGR2GRAY);

// 3.グレースケール画像を平滑化

cvSmooth(grayscaleImage, grayscaleImage, CV_GAUSSIAN, 3, 0, 0);

// 4.エッジ検出画像を作成

cvCanny(grayscaleImage, edgeImage, 20, 120);

// 5.エッジ検出画像色を反転

cvNot(edgeImage, edgeImage);

// 6.BGRAに変換

IplImage* dest = cvCreateImage(cvGetSize(source), IPL_DEPTH_8U, 4);

cvCvtColor(edgeImage, dest, CV_GRAY2BGRA);

// 8.白色の部分を透過する

SerAlpha( dest, 255 );

return dest;

}

IplImage* MonochromeFilter( IplImage* source )

{

// 2.グレースケール画像に変換

cv::Ptr<IplImage> grayScaleImage = cvCreateImage(cvGetSize(source), IPL_DEPTH_8U, 1);

cvCvtColor(source, grayScaleImage, CV_BGR2GRAY);

int gray = 100;

int filterLow = 70;

int filterHigh = 120;

// 3.グレースケール画像を1画素ずつ走査して3値化する

for(int y = 0; y < grayScaleImage->height; ++y) {

for(int x = 0; x < grayScaleImage->width; ++x) {

int index = (y * grayScaleImage->widthStep) + x;

uchar pixel = grayScaleImage->imageData[index];

if (pixel < filterLow) {

grayScaleImage->imageData[index] = 0;

}

else if ( (filterLow <= pixel) && (pixel < filterHigh) ) {

grayScaleImage->imageData[index] = gray;

}

else {

grayScaleImage->imageData[index] = 255;

}

}

}

// 4.BGRAに変換

IplImage* dest = cvCreateImage(cvGetSize(source), IPL_DEPTH_8U, 4);

cvCvtColor(grayScaleImage, dest, CV_GRAY2BGRA);

// グレー部分をマスク

SerAlpha( dest, gray );

return dest;

}

void AlphaBlend(IplImage *src,IplImage *dest)

{

for ( int y = 0; y < src->height; ++y ) {

for ( int x = 0; x < src->widthStep; x += 4 ) {

int index = (y * dest->widthStep) + x;

if ( ((unsigned char)src->imageData[index+3]) != 0 ) {

dest->imageData[index] = src->imageData[index];

dest->imageData[index+1] = src->imageData[index+1];

dest->imageData[index+2] = src->imageData[index+2];

}

}

}

}

void main()

{

KinectSensor kinect;

kinect.CreateInstance( 0 );

kinect.GetColorImageStream().Enable( ColorImageFormat::RgbResolution640x480Fps30 );

kinect.GetDepthImageStream().Enable( DepthImageFormat::Resolution640x480Fps30 );

kinect.GetSkeletonStream().Enable();

cv::Ptr<IplImage> back = ::cvLoadImage( "back.jpg" );

while ( 1 ) {

ColorImageFrame colorFrame = kinect.GetColorImageStream().OpenNextFrame();

cv::Mat image( (int)colorFrame.GetHeight(), (int)colorFrame.GetWidth(),

CV_8UC4, (byte*)colorFrame.GetPixelData() );

cv::Ptr<IplImage> dest = ::cvCreateImage(cvGetSize(back), IPL_DEPTH_8U, 4);

::cvCvtColor( back, dest, CV_BGR2BGRA );

cv::Ptr<IplImage> mono = MonochromeFilter( &(IplImage)image );

cv::Ptr<IplImage> line = LineFilter( &(IplImage)image );

AlphaBlend( mono, dest );

AlphaBlend( line, dest );

::cvShowImage( "mono", mono );

::cvShowImage( "line", line );

::cvShowImage( "dest", dest );

int key = cv::waitKey( 10 );

if ( key == 'q' ) {

break;

}

}

}