画像の特徴を検出したいオープンCVバックプロジェクションを使用します。
まず、単色の小さな画像のヒストグラムを計算し、それを大きな画像に適用できればとてもうれしいです。その後、その上にさらに構築することができます。C++の例私は Java でこれと同じようなことをしたいと思っています。残念ながら、OpenCV への Java インターフェースはあまりよく文書化されていません。
以下は私がこれまでに書いたコードですが、うまく動作しません(当然です。そうでなければ助けを求めません)。誰かできるととても嬉しいです。動作させるのを手伝ってくださいまたは良い資料を見つけるJava API 用です!
import java.util.ArrayList;
import org.opencv.core.*;
import org.opencv.imgproc.Imgproc;
public class ColorHistogramDetector extends ColorThresholdDetector {
//private cvHistogram histogram;
//histogram resolution for hue and saturation
static final int hbins = 30;//, sbins = 32;
public synchronized Mat detect(Mat inputFrame) {
Mat calcFrame = new Mat();
Imgproc.cvtColor(inputFrame, calcFrame, Imgproc.COLOR_RGB2HSV);
Mat hue = calcFrame;
ArrayList<Mat> dst = new ArrayList<Mat>();
dst.add(hue);
//create single color image
Mat fillImg = new Mat(16, 16, CvType.CV_8UC3);
fillImg.setTo(hsvColor);
MatOfInt histSize=new MatOfInt(hbins,hbins);
// hue varies from 0 to 179, see cvtColor
// saturation varies from 0 (black-gray-white) to
// 255 (pure spectrum color)
MatOfFloat ranges = new MatOfFloat( 0,180,0,256 );
Mat hist = new Mat();
// we compute the histogram from the 0-th and 1-st channels
MatOfInt channels = new MatOfInt(0, 1);
ArrayList<Mat> fillImgs=new ArrayList<Mat>();
fillImgs.add(fillImg);
Imgproc.calcHist(fillImgs, channels, new Mat(), hist, histSize, ranges);
outputFrame = new Mat();
Imgproc.calcBackProject(dst, channels, hist, calcFrame, ranges, 1);
int w = inputFrame.cols(); int h = inputFrame.rows();
int bin_w = (int) Math.round( (double) w / hbins );
Mat histImg = new Mat( w, h, CvType.CV_8UC3 );
for( int i = 0; i < hbins; i ++ ) {
Core.rectangle( histImg, new Point( i*bin_w, h ),
new Point( (i+1)*bin_w,
h - Math.round( hist.get(0, i)[0]*h/255.0 ) ),
new Scalar( 0, 0, 255 ), -1 );
}
hist.release();
fillImg.release();
Imgproc.cvtColor(histImg, calcFrame, Imgproc.COLOR_RGB2HSV);
return calcFrame;
}
}
ベストアンサー1
コードにいくつかおかしい点があるので、まずチュートリアルを厳密に実行し、その後自分のユースケースに合わせて変更することをお勧めします。
あなたが従っているチュートリアルにかなり近いようですが、単色の画像に適用しているようですcalcHist
。それがどのように役立つのかわかりません。通常は、代わりにいくつかのオブジェクトを含む HSV 画像である必要があります。また、手順が抜けていますnormalize
。
それを手助けするために、私はそれを変換しましたC++ バックプロジェクションチュートリアルOpenCV4Android 2.4.8 に。
Android ではなく Java を使用していますが、API はまったく同じで、定型的な入力/出力処理のみが異なります。
Android でより使いやすくするために、元のチュートリアルにいくつかの小さな変更を加えました。例:
- 静止画像ではなくライブカメラ画像を処理します。
- マウスクリックの代わりにタッチイベントを使用します。
- バックプロジェクションの出力は、カメラフィードに重ねて左上隅に表示されます。
- ノイズ低減としてガウスぼかしを追加しました。
まだすべての手順を徹底的にテストしたわけではありませんが、最終結果は問題なさそうです。
注: 現状では、バック プロジェクションを初期化するには、画面を 1 回タッチする必要があります...
ここにほとんどが載っていますが、足りないものは自分で見つけてくださいGitHubで:
private int outputWidth=300;
private int outputHeight=200;
private Mat mOutputROI;
private boolean bpUpdated = false;
private Mat mRgba;
private Mat mHSV;
private Mat mask;
private int lo = 20;
private int up = 20;
public void onCameraViewStarted(int width, int height) {
mRgba = new Mat(height, width, CvType.CV_8UC3);
mHSV = new Mat();
mIntermediateMat = new Mat();
mGray = new Mat(height, width, CvType.CV_8UC1);
mOutputROI = new Mat(outputHeight, outputWidth, CvType.CV_8UC1);
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
public Mat onCameraFrame(CvCameraViewFrame inputFrame) {
Mat mCamera = inputFrame.rgba();
Imgproc.cvtColor(mCamera, mRgba, Imgproc.COLOR_RGBA2RGB);
Mat mOutputROI = mCamera.submat(0, outputHeight, 0, outputWidth);
//Addition to remove some noise:
Imgproc.GaussianBlur(mRgba, mRgba, new Size(5, 5), 0, Imgproc.BORDER_DEFAULT);
Imgproc.cvtColor(mRgba, mHSV, Imgproc.COLOR_RGB2HSV_FULL);
if(mask!=null){
if(bpUpdated==false){
mGray = histAndBackproj();
} else {
bpUpdated = false;
}
Imgproc.resize(mGray, mIntermediateMat, mOutputROI.size(), 0, 0, Imgproc.INTER_LINEAR);
Imgproc.cvtColor(mIntermediateMat, mOutputROI, Imgproc.COLOR_GRAY2BGRA);
}
return mCamera;
}
public boolean onTouch(View arg0, MotionEvent arg1) {
Point seed = getImageCoordinates(mRgba, arg1.getX(), arg1.getY());
int newMaskVal = 255;
Scalar newVal = new Scalar( 120, 120, 120 );
int connectivity = 8;
int flags = connectivity + (newMaskVal << 8 ) + Imgproc.FLOODFILL_FIXED_RANGE + Imgproc.FLOODFILL_MASK_ONLY;
Mat mask2 = Mat.zeros( mRgba.rows() + 2, mRgba.cols() + 2, CvType.CV_8UC1 );
Rect rect = null;
Imgproc.floodFill( mRgba, mask2, seed, newVal, rect, new Scalar( lo, lo, lo ), new Scalar( up, up, up), flags );
// C++:
// mask = mask2( new Range( 1, mask2.rows() - 1 ), new Range( 1, mask2.cols() - 1 ) );
mask = mask2.submat(new Range( 1, mask2.rows() - 1 ), new Range( 1, mask2.cols() - 1 ));
mGray = histAndBackproj();
bpUpdated = true;
return true;
}
private Mat histAndBackproj() {
Mat hist = new Mat();
int h_bins = 30;
int s_bins = 32;
// C++:
//int histSize[] = { h_bins, s_bins };
MatOfInt mHistSize = new MatOfInt (h_bins, s_bins);
// C++:
//float h_range[] = { 0, 179 };
//float s_range[] = { 0, 255 };
//const float* ranges[] = { h_range, s_range };
//int channels[] = { 0, 1 };
MatOfFloat mRanges = new MatOfFloat(0, 179, 0, 255);
MatOfInt mChannels = new MatOfInt(0, 1);
// C++:
// calcHist( &hsv, 1, channels, mask, hist, 2, histSize, ranges, true, false );
boolean accumulate = false;
Imgproc.calcHist(Arrays.asList(mHSV), mChannels, mask, hist, mHistSize, mRanges, accumulate);
// C++:
// normalize( hist, hist, 0, 255, NORM_MINMAX, -1, Mat() );
Core.normalize(hist, hist, 0, 255, Core.NORM_MINMAX, -1, new Mat());
// C++:
// calcBackProject( &hsv, 1, channels, hist, backproj, ranges, 1, true );
Mat backproj = new Mat();
Imgproc.calcBackProject(Arrays.asList(mHSV), mChannels, hist, backproj, mRanges, 1);
return backproj;
}
/**
* Method to scale screen coordinates to image coordinates,
* as they have different resolutions.
*
* x - width; y - height;
* Nexus 4: xMax = 1196; yMax = 768
*
* @param displayX
* @param displayY
* @return
*/
private Point getImageCoordinates(Mat image, float displayX, float displayY){
Display display = getWindowManager().getDefaultDisplay();
android.graphics.Point outSize = new android.graphics.Point();
display.getSize(outSize);
float xScale = outSize.x / (float) image.width();
float yScale = outSize.y / (float) image.height();
return new Point(displayX/xScale, displayY/yScale);
}