我正在使用javacv包(Opencv)开发组件识别项目。我使用一种方法在图像上返回一组矩形作为“CvSeq”我需要知道的是如何做以下事情
这是返回矩形的方法
public static CvSeq findSquares( final IplImage src, CvMemStorage storage)
{
CvSeq squares = new CvContour();
squares = cvCreateSeq(0, sizeof(CvContour.class), sizeof(CvSeq.class), storage);
IplImage pyr = null, timg = null, gray = null, tgray;
timg = cvCloneImage(src);
CvSize sz = cvSize(src.width() & -2, src.height() & -2);
tgray = cvCreateImage(sz, src.depth(), 1);
gray = cvCreateImage(sz, src.depth(), 1);
pyr = cvCreateImage(cvSize(sz.width()/2, sz.height()/2), src.depth(), src.nChannels());
// down-scale and upscale the image to filter out the noise
cvPyrDown(timg, pyr, CV_GAUSSIAN_5x5);
cvPyrUp(pyr, timg, CV_GAUSSIAN_5x5);
cvSaveImage("ha.jpg", timg);
CvSeq contours = new CvContour();
// request closing of the application when the image window is closed
// show image on window
// find squares in every color plane of the image
for( int c = 0; c < 3; c++ )
{
IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
cvSplit(timg, channels[0], channels[1], channels[2], null);
}else{
tgray = cvCloneImage(timg);
}
tgray = channels[c]; // try several threshold levels
for( int l = 0; l < N; l++ )
{
// hack: use Canny instead of zero threshold level.
// Canny helps to catch squares with gradient shading
if( l == 0 )
{
// apply Canny. Take the upper threshold from slider
// and set the lower to 0 (which forces edges merging)
cvCanny(tgray, gray, 0, thresh, 5);
// dilate canny output to remove potential
// // holes between edge segments
cvDilate(gray, gray, null, 1);
}
else
{
// apply threshold if l!=0:
cvThreshold(tgray, gray, (l+1)*255/N, 255, CV_THRESH_BINARY);
}
// find contours and store them all as a list
cvFindContours(gray, storage, contours, sizeof(CvContour.class), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE);
CvSeq approx;
// test each contour
while (contours != null && !contours.isNull()) {
if (contours.elem_size() > 0) {
approx = cvApproxPoly(contours, Loader.sizeof(CvContour.class),storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0);
if( approx.total() == 4
&&
Math.abs(cvContourArea(approx, CV_WHOLE_SEQ, 0)) > 1000 &&
cvCheckContourConvexity(approx) != 0
){
double maxCosine = 0;
//
for( int j = 2; j < 5; j++ )
{
// find the maximum cosine of the angle between joint edges
double cosine = Math.abs(angle(new CvPoint(cvGetSeqElem(approx, j%4)), new CvPoint(cvGetSeqElem(approx, j-2)), new CvPoint(cvGetSeqElem(approx, j-1))));
maxCosine = Math.max(maxCosine, cosine);
}
if( maxCosine < 0.2 ){
cvSeqPush(squares, approx);
}
}
}
contours = contours.h_next();
}
contours = new CvContour();
}
}
return squares;
}
这是我使用的示例原始图像
这是我在匹配的矩形周围绘制线条后得到的图像
实际上在上面的图像中,我想要删除那些大矩形,只需要识别其他矩形,所以我需要一些代码示例来了解如何归档上述目标。请善意与我分享您的经验。谢谢 !
答案 0 :(得分:4)
OpenCV在黑色背景中查找白色物体的轮廓。在你的情况下,它是相反的,物体是黑色的。以这种方式,甚至图像边界也是一个对象。所以为了避免这种情况,只需反转图像,使背景为黑色。
下面我演示了它(使用OpenCV-Python):
import numpy as np
import cv2
im = cv2.imread('sofsqr.png')
img = cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(img,127,255,1)
请记住,我没有使用单独的函数进行反转,而是在阈值中使用它。只需将阈值类型转换为BINARY_INV(即&#39; 1&#39;)。
现在您的图像如下:
现在我们找到了轮廓。然后对于每个轮廓,我们近似它并通过查看近似轮廓的长度来检查它是否是矩形,对于矩形应该是4。
如果画出来,你会这样:
同时,我们也找到了每个轮廓的边界矩形。边界矩形的形状如下:[初始点x,初始点y,矩形宽度,矩形高度]
所以你得到宽度和高度。
以下是代码:
contours,hierarchy = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
for cnt in contours:
approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True)
if len(approx)==4:
cv2.drawContours(im,[approx],0,(0,0,255),2)
x,y,w,h = cv2.boundingRect(cnt)
编辑:
经过一些评论后,我明白了,问题的真正目的是避免使用较大的矩形,只选择较小的矩形。
可以使用我们获得的边界矩形值来完成。即,仅选择长度小于阈值或宽度或面积的矩形。举个例子,在这张图片中,我拍摄的面积应该小于10000.(粗略估计)。如果小于10000,则应选择它,我们用红色表示,否则,用蓝色表示的假候选(仅用于可视化)。
for cnt in contours:
approx = cv2.approxPolyDP(cnt,cv2.arcLength(cnt,True)*0.02,True)
if len(approx)==4:
x,y,w,h = cv2.boundingRect(approx)
if w*h < 10000:
cv2.drawContours(im,[approx],0,(0,0,255),-1)
else:
cv2.drawContours(im,[approx],0,(255,0,0),-1)
以下是我得到的输出:
如何获得该阈值? :强>
这完全取决于您和您的应用程序。或者你可以通过试错法找到它。 (我这样做了。)
希望能解决您的问题。所有功能都是标准的opencv功能。所以我认为你没有找到任何转换为JavaCV的问题。
答案 1 :(得分:2)
注意到问题中提供的代码中存在错误:
IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
cvSplit(timg, channels[0], channels[1], channels[2], null);
}else{
tgray = cvCloneImage(timg);
}
tgray = channels[c];
这意味着如果有一个频道,则tgray将是一个空图像。 它应该是:
IplImage channels[] = {cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1), cvCreateImage(sz, 8, 1)};
channels[c] = cvCreateImage(sz, 8, 1);
if(src.nChannels() > 1){
cvSplit(timg, channels[0], channels[1], channels[2], null);
tgray = channels[c];
}else{
tgray = cvCloneImage(timg);
}