Search code examples
androidopencvdelphifiremonkey

Using OpenCV for Android in Delphi throws an error on using JList


I would like help with using OpenCV for Android (Git: https://github.com/CarlosHe/OpenCV-Android-Firemonkey), Contours example.

I don't know how can I use contours list (JList)? I would like to loop through all the contours and output each one in a Memo1.

Here's the code (returns an error "Access violation at address C8E40F40, access address 47F027B5"):

    var
      LSrcMat: JMat;
      LDstMat: JMat;
      LHierarchyMat: JMat;
      LJBitmap: JBitmap;
      LThreshold: Double;
      LContoursList: JList;
      trash, maxval: Double;
      i, type_: Integer;
    begin
      FOpenCVInProgress := True;
      try
        trash := StrToFloat(edtTrash.Text);
        maxval := StrToFloat(edtMaxVal.Text);
        type_ := StrToInt(edtType.Text);
        LSrcMat := TJMat.JavaClass.init;
        LDstMat := TJMat.JavaClass.init;
        LHierarchyMat:= TJMat.JavaClass.init;
        LContoursList:= JList(TJArrayList.JavaClass.init(0));
        LJBitmap := TJBitmap.JavaClass.createBitmap(Trunc(FCamBitmap.Width), Trunc(FCamBitmap.Height), TJBitmap_Config.JavaClass.ARGB_8888);
        TJandroid_Utils.JavaClass.bitmapToMat(BitmapToJBitmap(FCamBitmap), LSrcMat);
        TJImgproc.JavaClass.cvtColor(LSrcMat, LDstMat, TJImgproc.JavaClass.COLOR_RGB2GRAY);   // change picture to gray
        LThreshold := TJImgproc.JavaClass.threshold(LDstMat, LDstMat, trash, maxval, type_);  // classify pixels into two different groups
        TJImgproc.JavaClass.Canny(LDstMat, LDstMat, LThreshold, LThreshold * 2);              // Edge detection
        TJImgproc.JavaClass.findContours(LDstMat, LContoursList, LHierarchyMat, TJImgproc.JavaClass.RETR_EXTERNAL, TJImgproc.JavaClass.CHAIN_APPROX_SIMPLE ); // find edge
        TJImgproc.JavaClass.cvtColor(LDstMat, LDstMat, TJImgproc.JavaClass.COLOR_GRAY2RGB);   // change picture to color
        TJImgproc.JavaClass.drawContours(LDstMat, LContoursList, -1, TJScalar.JavaClass.init(255,255,0) , 3);  // draw edge
        TJandroid_Utils.JavaClass.MatToBitmap(LDstMat, LJBitmap);
        FCamBitmap := JBitmapToBitmap(LJBitmap);
 // returns an error "Access violation at address C8E40F40, access address 47F027B5"
 For i := 0 to LContoursList.size - 1 do
 begin
         Memo1.Lines.Add(JStringToString((LContoursList.get(i).toString)));
 end;

Solution

  • You need JList instance to pass to findContours, but to get the JList instance you cannot typecast it from JArrayList the way you did.

    In Delphi JList and JArrayList belong to different interface hierarchies and cannot be directly typecasted. The problem is in following line

    LContoursList:= JList(TJArrayList.JavaClass.init(0));
    

    Above forced typecast will convert two incompatible interfaces without giving you any warnings and then when you try to use the LContoursList it will crash.

    If you write the above typecast in safe manner:

    LContoursList := TJArrayList.Create as JList;
    

    you will get runtime exception, telling you that JArrayList does not support JList interface.

    Exception class EIntfCastError with message 'Interface not supported'.

    Because underlying Java class supports both interfaces, you can do the typecast differently and expose underlying Java object as JList using following code:

    var
      LArr: JArrayList;
      LContoursList: JList;
    
    
      LArr := TJArrayList.Create;
      LContoursList:= TJList.Wrap(LArr);
    

    Note: In Delphi if you want to invoke default Java constructor you can merely say TJXXX.Create instead TJXXX.JavaClass.init. In your case calling Java constructor with 0 capacity does not make much difference than using default constructor.

    Note: Original FMX example run is in background thread. Since you haven't shown how you are running your example, if you are using background thread, you will need to make sure that you synchronize any usage of GUI controls with the main thread.