lukaszkurantdev / react-native-fast-opencv

A powerful port of OpenCV for React Native.
https://lukaszkurantdev.github.io/react-native-fast-opencv/
MIT License
66 stars 7 forks source link

Unable to access connectedComponentsWithStats results #25

Closed DavDag closed 2 weeks ago

DavDag commented 2 weeks ago

Hi :)

How can I access the result of connectedComponentsWithStats ?

Here's the code (that do not work):

const labels = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_8U, undefined);
const stats = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_32S, undefined);
const centroids = OpenCV.createObject(ObjectType.Mat, 0, 0, DataTypes.CV_64F, undefined);
const { value } = OpenCV.invoke('connectedComponentsWithStats', mask, labels, stats, centroids);
console.log('Connected components:', value);

const statsData = OpenCV.matToBuffer(stats, 'float32');
for (let i = 0; i < value; i++) {
  const x = statsData?.buffer[i * 5 + ConnectedComponentsTypes.CC_STAT_LEFT];
  const y = statsData?.buffer[i * 5 + ConnectedComponentsTypes.CC_STAT_TOP];
  const width = statsData?.buffer[i * 5 + ConnectedComponentsTypes.CC_STAT_WIDTH];
  const height = statsData?.buffer[i * 5 + +ConnectedComponentsTypes.CC_STAT_HEIGHT];
  const area = statsData?.buffer[i * 5 + +ConnectedComponentsTypes.CC_STAT_AREA];

  console.log(`Component ${i}: x=${x.toFixed(2)}, y=${y.toFixed(2)}, width=${width.toFixed(2)}, height=${height.toFixed(2)}, area=${area?.toFixed(2)}`);
}

I get:

LOG  Connected components: 29
 LOG  Component 0: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 1: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 2: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 3: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 4: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 5: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 6: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 7: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 8: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 9: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 10: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 11: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 12: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 13: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 14: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 15: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 16: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 17: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 18: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 19: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 20: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 21: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 22: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 23: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 24: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 25: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 26: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 27: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00
 LOG  Component 28: x=0.00, y=0.00, width=0.00, height=0.00, area=0.00

If I try using the other conversion (uint8):

const statsData = OpenCV.matToBuffer(stats, 'uint8');
for (let i = 0; i < value; i++) {
  const x = statsData?.buffer[i * 5 + ConnectedComponentsTypes.CC_STAT_LEFT];
  const y = statsData?.buffer[i * 5 + ConnectedComponentsTypes.CC_STAT_TOP];
  const width = statsData?.buffer[i * 5 + ConnectedComponentsTypes.CC_STAT_WIDTH];
  const height = statsData?.buffer[i * 5 + +ConnectedComponentsTypes.CC_STAT_HEIGHT];
  const area = statsData?.buffer[i * 5 + +ConnectedComponentsTypes.CC_STAT_AREA];
  console.log(`Component ${i}: x=${x}, y=${y}, width=${width}, height=${height}, area=${area}`);
}

I get wrong results:

 LOG  Connected components: 27
 LOG  Component 0: x=0, y=0, width=0, height=0, area=98
 LOG  Component 1: x=0, y=0, width=0, height=123, area=0
 LOG  Component 2: x=0, y=0, width=110, height=0, area=0
 LOG  Component 3: x=0, y=106, width=0, height=0, area=0
 LOG  Component 4: x=107, y=0, width=0, height=0, area=52
 LOG  Component 5: x=0, y=0, width=0, height=70, area=0
 LOG  Component 6: x=0, y=0, width=85, height=0, area=0
 LOG  Component 7: x=0, y=130, width=0, height=0, area=0
 LOG  Component 8: x=146, y=0, width=0, height=0, area=106
 LOG  Component 9: x=0, y=0, width=0, height=146, area=0
 LOG  Component 10: x=0, y=0, width=146, height=0, area=0
 LOG  Component 11: x=0, y=100, width=0, height=0, area=0
 LOG  Component 12: x=95, y=0, width=0, height=0, area=129
 LOG  Component 13: x=0, y=0, width=0, height=148, area=0
 LOG  Component 14: x=0, y=0, width=100, height=0, area=0
 LOG  Component 15: x=0, y=148, width=0, height=0, area=0
 LOG  Component 16: x=148, y=0, width=0, height=0, area=102
 LOG  Component 17: x=0, y=0, width=0, height=110, area=0
 LOG  Component 18: x=0, y=0, width=117, height=0, area=0
 LOG  Component 19: x=0, y=120, width=0, height=0, area=0
 LOG  Component 20: x=169, y=0, width=0, height=0, area=174
 LOG  Component 21: x=0, y=0, width=0, height=0, area=0
 LOG  Component 22: x=0, y=0, width=0, height=0, area=0
 LOG  Component 23: x=0, y=0, width=0, height=0, area=0
 LOG  Component 24: x=2, y=0, width=0, height=0, area=10
 LOG  Component 25: x=0, y=0, width=0, height=19, area=0
 LOG  Component 26: x=0, y=0, width=21, height=0, area=0

The same goes for extracting centroids. Am I missing something? Thanks!

lukaszkurantdev commented 2 weeks ago

Hi @DavDag, thanks for this issue. There was a gap in the library related to the inability to convert to selected types. In v.0.2.12 I've added more types in matToBuffer function, this is a definition:

matToBuffer<T extends keyof BufferType>(
  mat: Mat,
  type: T
): {
  cols: number;
  rows: number;
  channels: number;
  buffer: BufferType[T];
};

where BufferType is:

type BufferType = {
  uint8: Uint8Array;
  uint16: Uint16Array;
  uint32: Uint32Array;
  int8: Int8Array;
  int16: Int16Array;
  int32: Int32Array;
  float32: Float32Array;
  float64: Float64Array;
};

So for example, for your case, when you want to get stats data (which is CV_32S Mat) you need to convert it to 32-bit signed value:

const statsData = OpenCV.matToBuffer(stats, 'int32');

The result will be in Int32Array type.

Similar for centroids (which is CV_64F), it will be 64-bit floating value, and the result will be in Float64Array type:

const statsData = OpenCV.matToBuffer(centroids, 'float64');
DavDag commented 2 weeks ago

Hi @lukaszkurantdev, thank you very much! I'll try it asap and close the issue. Thanks again for the speed, really appreciate your efforts 🚀

DavDag commented 2 weeks ago

It works like magic! Thanks again!