Opencv4nodejs: drawContours first parameter is not of Array type

Created on 4 Jun 2019  路  12Comments  路  Source: justadudewhohacks/opencv4nodejs

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

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:

img.drawContours(
            [edgePoints],
            0,
            green,  //new cv.Vec(0, 255, 0);
            { thickness: 2 }
);

I hope it can help.

Thanks

All 12 comments

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 ?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

fildaniels picture fildaniels  路  5Comments

SatoshiKawabata picture SatoshiKawabata  路  5Comments

seanquijote picture seanquijote  路  6Comments

developer239 picture developer239  路  5Comments

Paulito-7 picture Paulito-7  路  5Comments