OpenCV version : 3.4.3
With OpenCV-contrib? (extra modules): no
OS: Windows 7 / 8 / 10? / MacOSX? / Ubuntu? OpenSuse Tumbleweed
Hi, it seems that drawContours function has issues with it's first parameter, this is what i get:
edgeContour is as follows:
[ Contour {
hierarchy: Vec4 { z: -1, y: -1, x: -1, w: -1 },
numPoints: 4,
area: 271347,
isConvex: true } ]
error message:
let emask = mask.drawContours(edgeContour, new cv.Vec3(0,255,0), 0, 5, new cv.Point(0, 0), cv.LINE_8, 5, 0);
^
Mat::DrawContours - Error: expected argument 0 to be of type array
Can you assist me with this? Thanks
Hi, i am encountering the same problem .... can anyone assist?
@cl-nati I think I figured it out, first argument is of type array, but not countour(s) array rather something like this:
const getHandContour = (handMask) => {
const mode = cv.RETR_EXTERNAL;
const method = cv.CHAIN_APPROX_SIMPLE;
const contours = handMask.findContours(mode, method);
// largest contour
return contours.sort((c0, c1) => c1.area - c0.area)[0];
};
let edgeContour = getHandContour(...);
let edgePoints = edgeContour.getPoints();
img.drawContours([edgePoints], ...
You have to extract contour points using getPoints fn.
Hi @nenadg ,
I'm working with @cl-nati , and I wanted to update, that I tried to use version 4.15.0, and in this version, it works well.
So the last version probably broke it...
Thanks @noam-zweig it was apparently the problem with last version.
Same for me - Docs show it takes the contour array, but the error returned
Error when passing the contour array:
Mat::DrawContours - Error: expected argument 0 to be of type array of arrays of Point2
Docs:
drawContours ( contours : Contour [] , color : Vec3 , contourIdx : int = 0 , maxLevel : int = INT_MAX , offset : Point2 = new Point2(0, 0) , lineType : uint = LINE_8 , thickness : uint = 1 , shift : uint = 0 ) : Result
The workaround fixes it, but I imagine it was intended to take the contour array, and the implementation can pull whats needed.
NPM installed version
Version: "opencv4nodejs": "^5.1.0"
Same problem here but still working with drawPolygon
`const getHandContour = (handMask) => {
const mode = cv.RETR_EXTERNAL;
const method = cv.CHAIN_APPROX_SIMPLE;
const contours = handMask.findContours(mode, method);
// largest contour
return contours.sort((c0, c1) => c1.area - c0.area)[0];
};
// returns distance of two points
const ptDist = (pt1, pt2) => pt1.sub(pt2).norm();
// returns center of all points
const getCenterPt = pts => pts.reduce(
(sum, pt) => sum.add(pt),
new cv.Point(0, 0)
).div(pts.length);
// get the polygon from a contours hull such that there
// will be only a single hull point for a local neighborhood
const getRoughHull = (contour, maxDist) => {
// get hull indices and hull points
const hullIndices = contour.convexHullIndices();
const contourPoints = contour.getPoints();
const hullPointsWithIdx = hullIndices.map(idx => ({
pt: contourPoints[idx],
contourIdx: idx
}));
const hullPoints = hullPointsWithIdx.map(ptWithIdx => ptWithIdx.pt);
// group all points in local neighborhood
const ptsBelongToSameCluster = (pt1, pt2) => ptDist(pt1, pt2) < maxDist;
const { labels } = cv.partition(hullPoints, ptsBelongToSameCluster);
const pointsByLabel = new Map();
labels.forEach(l => pointsByLabel.set(l, []));
hullPointsWithIdx.forEach((ptWithIdx, i) => {
const label = labels[i];
pointsByLabel.get(label).push(ptWithIdx);
});
// map points in local neighborhood to most central point
const getMostCentralPoint = (pointGroup) => {
// find center
const center = getCenterPt(pointGroup.map(ptWithIdx => ptWithIdx.pt));
// sort ascending by distance to center
return pointGroup.sort(
(ptWithIdx1, ptWithIdx2) => ptDist(ptWithIdx1.pt, center) - ptDist(ptWithIdx2.pt, center)
)[0];
};
const pointGroups = Array.from(pointsByLabel.values());
// return contour indeces of most central points
return pointGroups.map(getMostCentralPoint).map(ptWithIdx => ptWithIdx.contourIdx);
};
const getHullDefectVertices = (handContour, hullIndices) => {
const defects = handContour.convexityDefects(hullIndices);
const handContourPoints = handContour.getPoints();
// get neighbor defect points of each hull point
const hullPointDefectNeighbors = new Map(hullIndices.map(idx => [idx, []]));
defects.forEach((defect) => {
const startPointIdx = defect.at(0);
const endPointIdx = defect.at(1);
const defectPointIdx = defect.at(2);
hullPointDefectNeighbors.get(startPointIdx).push(defectPointIdx);
hullPointDefectNeighbors.get(endPointIdx).push(defectPointIdx);
});
return Array.from(hullPointDefectNeighbors.keys())
// only consider hull points that have 2 neighbor defects
.filter(hullIndex => hullPointDefectNeighbors.get(hullIndex).length > 1)
// return vertex points
.map((hullIndex) => {
const defectNeighborsIdx = hullPointDefectNeighbors.get(hullIndex);
return ({
pt: handContourPoints[hullIndex],
d1: handContourPoints[defectNeighborsIdx[0]],
d2: handContourPoints[defectNeighborsIdx[1]]
});
});
};
const filterVerticesByAngle = (vertices, maxAngleDeg) =>
vertices.filter((v) => {
const sq = x => x * x;
const a = v.d1.sub(v.d2).norm();
const b = v.pt.sub(v.d1).norm();
const c = v.pt.sub(v.d2).norm();
const angleDeg = Math.acos(((sq(b) + sq(c)) - sq(a)) / (2 * b * c)) * (180 / Math.PI);
return angleDeg < maxAngleDeg;
});
const blue = new cv.Vec(255, 0, 0);
const green = new cv.Vec(0, 255, 0);
const red = new cv.Vec(0, 0, 255);
// main
const delay = 20;
const imagem = cv.imread(path.resolve(__dirname, './teste.jpg'));
const resizedImg = imagem.resizeToMax(640);
const handMask = resizedImg.cvtColor(cv.COLOR_RGB2GRAY);
const handContour = getHandContour(handMask);
const maxPointDist = 25;
const hullIndices = getRoughHull(handContour, 25);
const vertices = getHullDefectVertices(handContour, hullIndices);
// fingertip points are those which have a sharp angle to its defect points
const maxAngleDeg = 60;
const verticesWithValidAngle = filterVerticesByAngle(vertices, maxAngleDeg);
console.log(handContour)
let edgePoints = handContour.getPoints();
const xx = edgePoints.map((item, i) => {
console.log(hullIndices[i])
const xCoords = edgePoints.map(pt => pt.x)
const yCoords = edgePoints.map(pt => pt.y)
const minX = xCoords.reduce((x, min) => x < min ? x : min, Infinity)
const minY = yCoords.reduce((y, min) => y < min ? y : min, Infinity)
return new cv.Point2(minX, minY)
})
console.log(xx)
const result = imagem.copy();
// draw bounding box and center line
resizedImg.drawContours(
[xx],
0,
blue,
{ thickness: 1 },
null,
cv.LINE_4
);
cv.imshowWait('handMask', result);
`
CHANGED TO IMAGE AND WORKS
Hi guys,
firstly I would like to thank @nenadg for post a possible solution to this problem. Unfortunately, it didn`t solve completely mine. After I implemented your code, I had another error (Mat::DrawContours - Error: expected argument 1 to be of type int). After a while coding I discovered that it will get solved if you put a zero after [edgePoints], like:
img.drawContours(
[edgePoints],
0,
green, //new cv.Vec(0, 255, 0);
{ thickness: 2 }
);
I hope it can help.
Thanks
So I had the same issue as @marcoaminotto.
I was able to draw the contours with drawContours() while passing 0 as the second argument, but this way I was drawing only a single contour.
If you have multiple contours that you would like to draw, you can pass -1 instead of 0:
image.drawContours(
arrayOfContours,
-1,
new cv.Vec3(125, 60, 0),
{ thickness: 3 },
);
Same error on version 5.5.0
Mat::DrawContours - Error: expected argument 0 to be of type array of arrays of Point2"
I try with samples code and
static getHandContour = (handMask: cv.Mat) => {
const mode = cv.RETR_EXTERNAL;
const method = cv.CHAIN_APPROX_SIMPLE;
const contours = handMask.findContours(mode, method);
// largest contour
return contours;
};
image.drawContours(edgePoints,
Crop.blue
);
So I had the same error on version 5.5.0
And get the same error.
How can ih fix it ? i dont understand how the array shoud look like, so then i can change die Object.
I'm using 5.5.0, thanks to :https://overflowjs.com/posts/Image-Processing-OpenCV-and-Nodejs-Part-3.html
The following code works:
let contours = grayImage.findContours(cv.RETR_TREE, cv.CHAIN_APPROX_NONE, new cv.Point2(0, 0));
const color = new cv.Vec3(41, 176, 218);
contours = contours.sort((c0, c1) => c1.area - c0.area);
const imgContours = contours.map((contour) => {
return contour.getPoints();
});
this.image.drawContours(imgContours, -1, color, 2);
however the declaration of findCoutours in lib/typings/Mat.d.ts is like (#135)
drawContours(contours: Contour[], color: Vec3, contourIdx?: number, maxLevel?: number, offset?: Point2, lineType?: number, thickness?: number, shift?: number): void;
and the functiion in native opencv delared like:
void cv::drawContours | ( | InputOutputArray | image,
-- | -- | -- | --
聽 | 聽 | InputArrayOfArrays | contours,
聽 | 聽 | int | contourIdx,
聽 | 聽 | const聽Scalar聽& | color,
聽 | 聽 | int | thickness聽=聽1,
聽 | 聽 | int | lineType聽=聽LINE_8,
聽 | 聽 | InputArray | hierarchy聽=聽noArray(),
聽 | 聽 | int | maxLevel聽=聽INT_MAX,
聽 | 聽 | Point | offset聽=聽Point()
聽 | ) | 聽 | 聽
not only the first parameter is wrong and the follwing parameters are declared in wrong order
it should be fixed as a BUG I think.
anybody confirm that ?
Most helpful comment
Hi guys,
firstly I would like to thank @nenadg for post a possible solution to this problem. Unfortunately, it didn`t solve completely mine. After I implemented your code, I had another error (Mat::DrawContours - Error: expected argument 1 to be of type int). After a while coding I discovered that it will get solved if you put a zero after [edgePoints], like:
I hope it can help.
Thanks