Tool Native Library Generator (C/C++ to B4A)

logo.png

Native Library Generator 4.20 (2018-11-11)
Written in B4J, this program allows you to write or import C/C++ code and have all the functions compiled into a native (.so) library, which in turn is included in a ready-to-use B4A Java lib. When using already existing code, if present, the main() function will be automatically ignored. If you're familiar with C/C++ but not with JNI or NDK, this is the right tool for you! No JAVA required.

Requirements:

Open Source:
This project is open source, under the GNU General Public License. Enjoy! :)
https://gitlab.com/brunowonder/NativeLibraryGenerator-CommunityEdition

Price (pay what you want):
If you wish to buy me a beer (or even dinner!) just follow the link below.
https://www.paypal.me/ninjadynamics
0317.png
0317.png
0317.png

How does it work:
upload_2017-10-27_15-57-1-png.61061


Video Demos:

Updates:
[Version 4.21] [2018-11-12] [CRITICAL]
QuickSource mode fixed
For background music add "nlg.mp3" to the NLG folder ;)

[Version 4.20] [2018-11-11] [NEW FEATURES]
Now supporting multiple source files
Select which files should be exposed to B4A
Requires NDK r18b (please update)
Bug fixes

[Version 4.01] [2017-05-07] [NEW FEATURES]
The 'cpu-features' library can now be used
Custom Makefiles may now be used
Custom Java wrappers may now be used
Bug fixes

[Version 4.00] [2017-05-07] [NEW FEATURES]
Complete visual overhaul
Specifying additional include paths is now possible
Specifying additional prebuilt libraries is now possible
Specifying the target ABI's is now possible
Compatible ABI's are now auto-detected if possible
Handling
multiple projects is now possible

[Version 3.0] [2017-05-07] [NEW FEATURES]
Returning arrays is now possible.
B4A logging with printf() is now possible.


[Version 2.9] [2016-08-12] [NEW FEATURES]
Small UI redesign and bug fixes.
Code clean-up.

Little bit more user-friendly.
Custom makefiles.

[Version 2.8] [2016-07-12] [NEW FEATURES]
Now supporting local #include files.
Now supporting external project locations.
New video demo.
Minor bug fixes.

[Version 2.7] [2016-03-14] [CRITICAL]
Major bug fixes.

[Version 2.6] [2016-01-31] [IMPORTANT]
Important bug fixes.
MazeSolver 1.5 Included as example source code.

[Version 2.5] [2016-01-31] [NEW FEATURES]
Now supporting comment transcription from C++ to B4A.
New splash screen.
Minor bug fixes.

[Version 2.4] [2016-01-27] [CRITICAL]
Critical bug fix regarding string (char*) support.
Cool new intro/exit screens added.


[Version 2.3] [2016-01-25] [NEW FEATURES]
Now supporting Strings (char*), both passing and returning.

[Version 2.2] [2016-01-20] [IMPORTANT]
Now supporting simple C/C++ typedef struct.
Now supporting C/C++ inline functions.

Minor bug fixes.

[Version 2.1] [
2016-01-12] [CRITICAL]
Included example (NinjaCore) source code updated. Several bug fixes.

[Version 2.1] [2016-01-12] [IMPORTANT]
Now supporting C++ headers, such as <vector> or <algorithm>.

[Version 2.0] [2016-01-10] [CRITICAL]
Major issue corrected. Please download Native Library Generator again.

Included Example:

In order to showcase this software's capabilities, I decided to include my MazeSolver project.

A. Download link:

B. Instructions (QuickSource Mode):
In QuickSouce mode all you have to do is write some code and hit the Generate Library button.

1. Before anything else, please make sure you have installed the following required software:
B4A, Simple Library Compiler and Android NDK


2. Download the .jar file into a system-writable folder (NOT Program Files).

3. Run NLG and provide the necessary software paths, as seen below.
upload_2018-11-12_11-56-16.png


4. Compile the example by clicking the big blue button.
upload_2018-11-12_11-56-37.png



5. Test the library you just compiled with this B4A project:
https://www.b4x.com/android/forum/t...orithm-w-path-optimization.61998/#post-391230

6. As you can see, it works!
upload_2018-11-12_12-14-11.png

C. LibFastMath Example code (Copy/Paste to NLG):
B4X:
//LibFastMath 1.00 - Native Library Example

//FastSin() and FastCos() by Allen Chou
//http://allenchou.net/2014/02/game-math-faster-sine-cosine-with-polynomial-curves

//FastSqrt() adapted from Quake 3's Fast Inverse Square Root algorithm
//https://en.wikipedia.org/wiki/Fast_inverse_square_root

#define PI         (3.1415926535f)
#define HALF_PI    (0.5f * PI)
#define TWO_PI     (2.0f * PI)
#define TWO_PI_INV (1.0f / TWO_PI)

float FastSin(float x);
float FastCos(float x);
float FastSqrt(float x);

inline float Hill(float x)
{
  const float a0 = 1.0f;
  const float a2 = 2.0f / PI - 12.0f / (PI * PI);
  const float a3 = 16.0f / (PI * PI * PI) - 4.0f / (PI * PI);
  const float xx = x * x;
  const float xxx = xx * x;

  return a0 + a2 * xx + a3 * xxx;
}

float FastSin(float x)
{
  // wrap x within [0, TWO_PI)
  const float a = x * TWO_PI_INV;
  x -= static_cast<int>(a) * TWO_PI;
  if (x < 0.0f)
    x += TWO_PI;

  // 4 pieces of hills
  if (x < HALF_PI)
    return Hill(HALF_PI - x);
  else if (x < PI)
    return Hill(x - HALF_PI);
  else if (x < 3.0f * HALF_PI)
    return -Hill(3.0f * HALF_PI - x);
  else
    return -Hill(x - 3.0f * HALF_PI);
}

float FastCos(float x)
{
  return FastSin(x + HALF_PI);
}

float FastSqrt(float x)
{
    //This is the method used in Quake3
    const float xhalf = 0.5f*x;

    //Get bits for floating value
    union
    {
        float x;
        int i;
    } u;
    u.x = x;

    //Give initial guess y0
    u.i = 0x5f3759df - (u.i >> 1);

    //Newton step, repeating increases accuracy
    return (x*u.x*(1.5f - xhalf*u.x*u.x));
}

D. How does it work:
Initially, I created this program in order to speed-up the process of writing JNI C/C++ code. Following B4A's RAD philosophy, it allows me to write native code in Visual Studio and compile it into a fully-working B4A lib within a single click.
E. Internal Functions:
If you wish any of your native function not to be exposed in the B4A library, you may do so by either changing their name from foo() into privateFoo() or simply adding a "::ignore" the function's comments.
B4X:
//This function will not be visible in B4A because its name starts with "private".
int privateSum(int a, int b)
{
    return a + b;
}

//This function will not be visible in B4A because this comment contains the keyword "::ignore".
int multiplication(int a, int b)
{
    return a * b;
}

//This function will be exposed in B4A.
int result(int a, int b)
{
    return privateSum(a, b) + multiplication(a, b);
}
F. Current Limitations:
As happy as I am to share this work with the B4x community, let's not forget that this is just my own internal tool, a never ending work-in-progress personal project.
Hence, it does not yet support several features such as passing Arrays into the native layer.
G. Reminder:
Please test your C/C++ code before trying to build a B4A library, as some kinds of syntax errors may crash the application.
 

Attachments

  • upload_2018-11-12_11-53-30.png
    upload_2018-11-12_11-53-30.png
    88.2 KB · Views: 425
Last edited:

wonder

Expert
Licensed User
Longtime User
Can you try copying it to the SLC folder?

By the way, I've successfully compiled the demo library with the newest SLC (just downloaded) and newest NDK (r12b).

Capture.png


Windows 10 - 64bit
Java version: JDK 1.8.051
 
Last edited:

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
Yes, I do! Now The problem is with Path with NDK, look:

C:\Users\Iglesias\AppData\Roaming\Microsoft\Windows\Start Menu\Programacao\Basic4android\NativeLibraryGenerator\workspace>cd workspace
O sistema nao pode encontrar o caminho especificado.
C:\Users\Iglesias\AppData\Roaming\Microsoft\Windows\Start Menu\Programacao\Basic4android\NativeLibraryGenerator\workspace>ndk-build -B
'ndk-build' nao é reconhecido como um comando interno
ou externo, um programa operável ou um arquivo em lotes.
C:\Users\Iglesias\AppData\Roaming\Microsoft\Windows\Start Menu\Programacao\Basic4android\NativeLibraryGenerator\workspace>

I will try to put the NDK build folder in path
 

wonder

Expert
Licensed User
Longtime User
...but your first screenshot shows there were no problems with the NDK build.

This is the NDK build code:
B4X:
    Sub RunNDK
        ok = False
        jobPhase = 0
        Dim fileList As List
        fileList = File.ListFiles(File.DirApp)
        For Each item As String In fileList
            If item.EndsWith(".h") Or item.EndsWith(".c") Or item.EndsWith(".cpp") Then
                File.Copy(File.DirApp, item, File.DirApp & "\workspace", item)
                File.Delete(File.DirApp, item)
            End If
        Next
        Dim compileCommand, copyCommand As String   
        compileCommand = "path " & Chr(34) & "%PATH%" & Chr(34) & ";" & Chr(34) & pathNDK & Chr(34) & CRLF & _
                         "cd workspace" & CRLF & _
                         "ndk-build -B"                   
        copyCommand    = "xcopy workspace\libs project\additional\lib /e /h /y"   
        File.WriteString(File.DirApp, "compile.bat", compileCommand)
        File.WriteString(File.DirApp, "copylib.bat", copyCommand)   
        Dim ndk As Shell
        ndk.Initialize("ndk", "compile.bat", Null)
        ndk.Run(-1)       
    End Sub
   
    Sub ndk_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
        Dim xcopy As Shell
        xcopy.Initialize("xcopy", "copylib.bat", Null)
        xcopy.Run(-1)
    End Sub

    Sub xcopy_ProcessCompleted (Success As Boolean, ExitCode As Int, StdOut As String, StdErr As String)
        File.Delete(File.DirApp, "copylib.bat")
        If File.ListFiles(File.DirApp & "\project\additional\lib").Size > 0 Then
            File.Delete(File.DirApp, "compile.bat")
            txtStatsBox.Text = txtStatsBox.Text & "[" & DateTime.Time(DateTime.Now) & "] Native library compiled." & CRLF       
            ok = True
        Else
            File.Copy(File.DirApp, "compile.bat", File.DirApp & "\workspace", "compile.bat")
            File.Delete(File.DirApp, "compile.bat")
            txtStatsBox.Text = txtStatsBox.Text & "[" & DateTime.Time(DateTime.Now) & "] NDK compilation FAILED." & CRLF
            txtStatsBox.Text = txtStatsBox.Text & "[" & DateTime.Time(DateTime.Now) & "] Run 'workspace\compile.bat' to see what went wrong." & CRLF           
            ok = False
        End If
        Log("NDK build successful:  " & ok)
        txtStatsBox.SetSelection(txtStatsBox.Text.Length - 1, txtStatsBox.Text.Length - 1)
        txtStatsBox.RequestFocus
        jobPhase = 2
    End Sub
 
Last edited:

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
OK, now it´s working, look:

[17:53:38] Initializing...
[17:53:38] Local project folder purged.
[17:53:38] Local workspace folder purged.
[17:53:38] Work in progress, please wait...
[17:53:39] Java file generated.
[17:53:39] JNI header file generated.
[17:53:39] JNI source file generated.
[17:53:39] NDK compilation in progress...
[17:54:03] Native library compiled.
[17:54:03] Deleting old JAR file.
[17:54:06] B4A library compiled.
[17:54:06] Job complete.

My problems and how I fix:

* in the folder where my SLC (LibraryCompiler.exe) is, don´t have ICSharpCode.SharpZipLib.dll and BADoclet.*
* After configure NLG, I move it to another folder (WITHOUT config.txt, only jar) and I lost my configuration with my paths.

I hope I was helpful!
 

wonder

Expert
Licensed User
Longtime User
Great!!! I'm glad you got it working!! :)

I try my best to make this tool as functional as possible.
Please remember that ALL functions will be exposed to B4A, unless you add ::ignore in the comment above or prefix the function name with "private".
See section E. in the first post, for a practical example.

Happy coding!
 

wonder

Expert
Licensed User
Longtime User
Version 2.92 is out!
- Minor bug fixes.
- You have now the option to use your own custom make files. Just place Application.mk and/or Android.mk in the NLG root folder.
 

monic

Active Member
Licensed User
Longtime User
Hi,

A small little bug in the generation of the xml file, if the package name starts with org because NLG prefixes with org,com.android,com.example,com.hoho on the simple library compiler -b4aignore field it won't generate the xml file. Is their a way in the NLG (v2.93) to avoid this situation?

Edit: the behaviour of SLC is defaulted to show these prefixes and is not a bug with NLG
 
Last edited:

wonder

Expert
Licensed User
Longtime User
Hi!

So if I understood correctly, the prefixes [ org ; com.android ; com.example ; com.hoho ] cause SLC not to generate the XML file, correct?

I'll try to have a look at it during the following week.

EDIT: Hello again, Jamie. I wasn't able to reproduce the behavior you described. I'll be releasing the NLG's source code in a few minutes, so feel free to have a look.
 
Last edited:

wonder

Expert
Licensed User
Longtime User
Open Source:
From April 1st 2017, this project is now open source, under the GNU General Public License. Enjoy! :)
https://gitlab.com/brunowonder/NativeLibraryGenerator-CommunityEdition

Native Library Generator - Community Edition
Copyright (C) 2015-2017 Bruno Silva / Ninja Dynamics


This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program. If not, see http://www.gnu.org/licenses/

Soundtack: Kenny Chou - One Must Fall: 2097 Theme (Byproduct Remix)
Kenny Chou's Soundcloud: https://soundcloud.com/neoj1n
Byproduct's homepage: http://byproduct.koshiyoka.com/index.html
 
Last edited:

roberto64

Active Member
Licensed User
Longtime User
hi wonder,
I'm trying to figure out the NLG operation, I have a project in C ++ with more classes and I'm using opencv in C ++, NLG compile all the project by creating a libreia, or just one class at a time?
if you can put your own examples, because seeing the movie you do not see very well.
Greetings
 

wonder

Expert
Licensed User
Longtime User
Hi Roberto,

The purpose of NLG is to allow us developers to write our own C/C++ libraries.
Wrapping existing ones can be way more challenging.

Structure:
While NLG supports .cpp files with local #includes, this feature isn't well tested yet, so I would recommend you to have everything in one file (.cpp).
Note that, there is no support for multiple .cpp files.
B4X:
Unsupported: abc.cpp, def.cpp, abc.h, def.h
Supported:   abc.cpp, abc.h, def.h
Recommended: abc.cpp

Since only primitive types* (and Strings**) are currently supported to be passed around,
the structure of an NLG library should be as follows:


Capture.PNG

*) byte, short, int, long, long long, float, double and uintptr_t (for memory addresses).
**) B4A String ---> char* ---> B4A String

So, if you want to deal with classes and such, you'll have to do some wrapping on the native side.
Regarding something as complex as OpenCV, I don't think NLG can handle it, you'll probably have to write and compile your own JNI code...

Examples:
- The C++ MazeSolver library is included in the NLG executable.
- FastMath lib in the first post
- Videos on the first post
- My Sha512 library

Again (from the first post), NLG is something I created for myself and decided to share with everyone else. It's not a professional product.
I do have some plans for improvement, but I have no idea when will I be able to dedicate some time to this project.
 

roberto64

Active Member
Licensed User
Longtime User
Hi, I have this c ++ code for cc recognition with opencv, but when I try to compile it with NLG I get the falid error, if you can help me and share it in the forum
regards
B4X:
#include <cv.h>
#include <highgui.h>
#include <cxcore.h>
#include <iostream>

#include <msclr\marshal.h>
#include <stdlib.h>
#include <string.h>

using namespace msclr::interop;
using namespace std;


IplImage *src;

string Trovatarga(string src);



char* ConvertStringtoChar(string str)
{
    //char *str2 = (char*)(void*)Marshal::StringToHGlobalAnsi(str);
    marshal_context converter;
     char *str2 = converter.marshal_as<const char*>(str);
    return str2;
}



string Trovatarga(string Dirscr)
{
    PlateFinder pf;

    if(src)
    {
        src = NULL;
    }

    src = cvLoadImage(ConvertStringtoChar(Dirscr));
   
    if (!src)
    {
       return 0;
    }

    // resize image
    IplImage *resizeImg = cvCreateImage(cvSize(800, 600), src->depth, src->nChannels);    // Anh resize
    cvResize(src, resizeImg);

    // Convert sang anh xam
    //IplImage *grayImg = cvCreateImage (cvGetSize(resizeImg), IPL_DEPTH_8U, 1);    // Anh resize
    /*cvCvtColor(resizeImg, grayImg, CV_RGB2GRAY);
    cvNormalize(grayImg, grayImg, 0, 255, CV_MINMAX);*/

    //pf.ImageRestoration(grayImg);
    IplImage *plate = pf.FindPlate(resizeImg);
    if (plate)
    {
        Recognise rc;
        vector<IplImage *> cVector = rc.FindCharacter(plate);
    }
    else {
        return 0;
   
    }
}


class PlateFinder
{
private:
    IplConvKernel* S1;
    IplConvKernel* S2;
    IplImage *plate;

public:
    PlateFinder(void);
    virtual ~PlateFinder(void);
    void ImageRestoration(IplImage *src);    // tien xu ly anh goc
    IplImage* FindPlate(IplImage *src);    // tim va cat bien so
    int CountCharacter(IplImage *plate);    // dem so vung co kha nang la ki tu
};

class Recognise {
public:
    Recognise();
    vector<IplImage *> FindCharacter(IplImage *plate);
};

const CvScalar RED = CV_RGB(255, 0, 0);
const CvScalar GREEN = CV_RGB(0, 255, 0);
const CvScalar BLUE = CV_RGB(0, 0, 255);

PlateFinder::PlateFinder(void)
{
    S1 = cvCreateStructuringElementEx(3, 1, 1, 0, CV_SHAPE_RECT, NULL);
    S2 = cvCreateStructuringElementEx(6, 1, 3, 0, CV_SHAPE_RECT, NULL);
    plate = NULL;
}

PlateFinder::~PlateFinder(void)
{
    if (plate)
    {
        S1 = NULL;
        S2 = NULL;
        plate = NULL;
    }
}

void PlateFinder::ImageRestoration(IplImage *src)
{
    int w = src->width;
    int h = src->height;

    IplImage *mImg = cvCreateImage(cvSize(w / 2, h / 2), IPL_DEPTH_8U, 1);        // Anh su dung cho bien doi hinh thai hoc
    IplImage *src_pyrdown = cvCreateImage(cvSize(w / 2, h / 2), IPL_DEPTH_8U, 1);
    IplImage *tmp = cvCreateImage(cvSize(w / 2, h / 2), IPL_DEPTH_8U, 1);
    IplImage *thresholed = cvCreateImage(cvSize(w / 2, h / 2), IPL_DEPTH_8U, 1);    // Anh nhi phan voi nguong
    IplImage *mini_thresh = cvCreateImage(cvSize(w / 2, h / 2), IPL_DEPTH_8U, 1);
    IplImage *dst = cvCreateImage(cvSize(w / 2, h / 2), IPL_DEPTH_8U, 1);            // Anh lam ro vung bien so

    cvPyrDown(src, src_pyrdown);

    cvMorphologyEx(src_pyrdown, mImg, tmp, S2, CV_MOP_BLACKHAT);
    cvNormalize(mImg, mImg, 0, 255, CV_MINMAX);


    // Nhi phan hoa anh mImg
    cvThreshold(mImg, thresholed, (int)10 * cvAvg(mImg).val[0], 255, CV_THRESH_BINARY);
    cvZero(dst);
    cvCopy(thresholed, mini_thresh);

    // Su dung hinh chu nhat co size = 8x16 truot tren toan bo anh

    int cnt;
    int nonZero1, nonZero2, nonZero3, nonZero4;
    CvRect rect;

    for (int i = 0; i < mini_thresh->width - 32; i += 4)
    {
        for (int j = 0; j < mini_thresh->height - 16; j += 4)
        {
            rect = cvRect(i, j, 16, 8);
            cvSetImageROI(mini_thresh, rect);    //ROI = Region of Interest
            nonZero1 = cvCountNonZero(mini_thresh);
            cvResetImageROI(mini_thresh);

            rect = cvRect(i + 16, j, 16, 8);
            cvSetImageROI(mini_thresh, rect);    //ROI = Region of Interest
            nonZero2 = cvCountNonZero(mini_thresh);
            cvResetImageROI(mini_thresh);

            rect = cvRect(i, j + 8, 16, 8);
            cvSetImageROI(mini_thresh, rect);    //ROI = Region of Interest
            nonZero3 = cvCountNonZero(mini_thresh);
            cvResetImageROI(mini_thresh);

            rect = cvRect(i + 16, j + 8, 16, 8);
            cvSetImageROI(mini_thresh, rect);    //ROI = Region of Interest
            nonZero4 = cvCountNonZero(mini_thresh);
            cvResetImageROI(mini_thresh);

            cnt = 0;
            if (nonZero1 > 15) { cnt++; }
            if (nonZero2 > 15) { cnt++; }
            if (nonZero3 > 15) { cnt++; }
            if (nonZero4 > 15) { cnt++; }

            if (cnt > 2)
            {
                rect = cvRect(i, j, 32, 16);
                cvSetImageROI(dst, rect);
                cvSetImageROI(mini_thresh, rect);
                cvCopy(mini_thresh, dst);
                cvResetImageROI(dst);
                cvResetImageROI(mini_thresh);
            }
        }
    }

    IplImage* dst_clone = cvCloneImage(dst);

    cvDilate(dst, dst, NULL, 2);
    cvErode(dst, dst, NULL, 2);
    cvDilate(dst, dst, S1, 9);
    cvErode(dst, dst, S1, 10);
    cvDilate(dst, dst);

    /*cvShowImage("Source" , src);
    cvShowImage("mImg", mImg);
    cvShowImage("mini_thresh", mini_thresh);
    cvShowImage("dst_clone", dst_clone);
    cvShowImage("dst", dst);*/

    cvPyrUp(dst, src);

    cvReleaseImage(&mini_thresh);
    cvReleaseImage(&mImg);
    cvReleaseImage(&tmp);
    cvReleaseImage(&dst);
    cvReleaseImage(&src_pyrdown);
    cvReleaseImage(&thresholed);
    cvReleaseImage(&dst_clone);
}

IplImage* PlateFinder::FindPlate(IplImage *src) {
    IplImage* plate;
    IplImage* contourImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);    // anh tim contour
    IplImage* grayImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 1);    // anh xam
    cvCvtColor(src, grayImg, CV_RGB2GRAY);

    IplImage* cloneImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
    cloneImg = cvCloneImage(src);

    // tien xu ly anh
    cvCopy(grayImg, contourImg);
    cvNormalize(contourImg, contourImg, 0, 255, CV_MINMAX);
    ImageRestoration(contourImg);

    IplImage* rectImg = cvCreateImage(cvGetSize(src), IPL_DEPTH_8U, 3);
    cvMerge(contourImg, contourImg, contourImg, NULL, rectImg); // tron anh

                                                                // tim contour cua buc anh
    CvMemStorage *storagePlate = cvCreateMemStorage(0);
    CvSeq *contours = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storagePlate);
    cvFindContours(contourImg, storagePlate, &contours, sizeof(CvContour), CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0));

    //cvShowImage("contourImg", contourImg);


    int xmin, ymin, xmax, ymax, w, h, s, r;
    int count;
    double ratio;    // ty le chieu rong tren chieu cao
    CvRect rectPlate;

    // luu lai cac anh co kha nang la bien so
    IplImage** plateArr = new IplImage *[5];
    int j = 0;
    for (int i = 0; i < 5; i++)
    {
        plateArr[i] = NULL;
    }

    while (contours) {
        count = contours->total;
        CvPoint *PointArray = new CvPoint[count];
        cvCvtSeqToArray(contours, PointArray, CV_WHOLE_SEQ);

        for (int i = 0; i < count; i++)
        {
            if (i == 0)
            {
                xmin = xmax = PointArray[i].x;
                ymin = ymax = PointArray[i].y;
            }

            if (PointArray[i].x > xmax) {
                xmax = PointArray[i].x;
            }
            if (PointArray[i].x < xmin) {
                xmin = PointArray[i].x;
            }

            if (PointArray[i].y > ymax) {
                ymax = PointArray[i].y;
            }
            if (PointArray[i].y < ymin) {
                ymin = PointArray[i].y;
            }
        }

        w = xmax - xmin;
        h = ymax - ymin;
        s = w * h;

        cvRectangle(rectImg, cvPoint(xmin, ymin), cvPoint(xmax, ymax), RED);

        // loai bo nhung hinh chu nhat co ti le khong dung
        if (s != 0) {
            r = (contourImg->height * contourImg->width) / s;
        }
        else {
            r = 1000;
        }

        if (w == 0 && h == 0) {
            ratio = 0;
        }
        else {
            ratio = (double)w / h;
        }

        if (r > 30 && r < 270) {
            // ve ra hcn mau xanh la
            cvRectangle(rectImg, cvPoint(xmin, ymin), cvPoint(xmax, ymax), GREEN);

            if (ratio > 2.6 && ratio < 7) {
                cvRectangle(rectImg, cvPoint(xmin, ymin), cvPoint(xmax, ymax), BLUE);

                if (w > 80 && w < 250 && h > 25 && h < 150) {
                    rectPlate = cvRect(xmin, ymin, w, h);

                    cvRectangle(cloneImg, cvPoint(rectPlate.x, rectPlate.y),
                        cvPoint(rectPlate.x + rectPlate.width, rectPlate.y + rectPlate.height), RED, 3);

                    // cat bien so
                    plate = cvCreateImage(cvSize(rectPlate.width, rectPlate.height), IPL_DEPTH_8U, 3);
                    cvSetImageROI(src, rectPlate);
                    cvCopy(src, plate, NULL);
                    cvResetImageROI(src);

                    // luu vao mang cac bien so plateArr
                    int cnt = CountCharacter(plate);
                    if (cnt >= 5) {
                        plateArr[j] = cvCloneImage(plate);
                        j++;
                    }
                }
            }
        }

        delete[]PointArray;

        contours = contours->h_next;
    }

    // sap xep
    if (plateArr[0])
    {
        int w = plateArr[0]->width;

        int flag;
        for (int i = 1; i < 4; i++)
        {
            if (plateArr[i] && plateArr[i]->width < w)
            {
                flag = i;
            }
        }

        plateArr[0] = plateArr[flag];
    }

    cvShowImage("cloneImg", cloneImg);
    //cvShowImage("rectImg", rectImg);
    //cvShowImage("plate", plateArr[0]);

    cvReleaseImage(&contourImg);
    cvReleaseImage(&rectImg);
    cvReleaseImage(&plate);

    return plateArr[0];
}


int PlateFinder::CountCharacter(IplImage *plate) {
    int cnt = 0;
    IplImage *resizeImg, *binaryImg;

    resizeImg = cvCreateImage(cvSize(408, 70), IPL_DEPTH_8U, 3);
    binaryImg = cvCreateImage(cvSize(408, 70), IPL_DEPTH_8U, 1);

    cvResize(plate, resizeImg);
    cvCvtColor(resizeImg, binaryImg, CV_RGB2GRAY);
    cvAdaptiveThreshold(binaryImg, binaryImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 13, 2);

    //cvShowImage("binaryImg", binaryImg);

    CvMemStorage *storage = cvCreateMemStorage(0);
    CvSeq *contours = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
    cvFindContours(binaryImg, storage, &contours);

    //cvShowImage("contours", binaryImg);

    //CvSeq *contours = 0;
    //cvFindContours(binaryImg, storage, &contours);

    while (contours) {
        CvRect rect = cvBoundingRect(contours);

        if (rect.width > 15 && rect.width < 50
            && rect.height > 40 && rect.height < 65
            && rect.width * rect.height > 1000)
        {
            cvRectangle(resizeImg, cvPoint(rect.x, rect.y),
                cvPoint(rect.x + rect.width, rect.y + rect.height), GREEN, 2);

            cnt++;
        }
        contours = contours->h_next;
    }

    //cvShowImage("resizeImg", resizeImg);

    return cnt;
}

const CvScalar RED = CV_RGB(255, 0, 0);
const CvScalar GREEN = CV_RGB(0, 255, 0);
const CvScalar BLUE = CV_RGB(0, 0, 255);

Recognise::Recognise() {
}

vector<IplImage *> Recognise::FindCharacter(IplImage *plate) {
    vector<IplImage *> charImgVector;
    vector<CvRect> rect;
    IplImage *resizeImg, *binaryImg, *cloneImg;

    resizeImg = cvCreateImage(cvSize(408, 70), IPL_DEPTH_8U, 3);
    binaryImg = cvCreateImage(cvSize(408, 70), IPL_DEPTH_8U, 1);

    cvResize(plate, resizeImg);
    cvCvtColor(resizeImg, binaryImg, CV_RGB2GRAY);
    cvAdaptiveThreshold(binaryImg, binaryImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 13, 2);

    cloneImg = cvCloneImage(resizeImg);

    CvMemStorage *storage = cvCreateMemStorage(0);
    CvSeq *contours = cvCreateSeq(CV_SEQ_ELTYPE_POINT, sizeof(CvSeq), sizeof(CvPoint), storage);
    cvFindContours(binaryImg, storage, &contours);

    double ratio, ratioWhite;
    int s, white;

    while (contours) {
        CvRect r = cvBoundingRect(contours, 0);

        ratio = (double)r.width / r.height;
        s = r.width * r.height;

        cvSetImageROI(binaryImg, r);
        white = cvCountNonZero(binaryImg);
        cvResetImageROI(binaryImg);
        ratioWhite = (double)white / s;

        if (ratio > 0.3 && ratio < 1.1 &&
            s > 375 && r.width > 15 && r.width < 50 &&
            r.height > 30 && r.height < 65 &&
            ratioWhite > 0.3 && ratioWhite < 0.75 && r.x > 2)
        {
            cvRectangle(cloneImg, cvPoint(r.x, r.y), cvPoint(r.x + r.width, r.y + r.height), BLUE, 2);

            rect.push_back(r);
        }

        contours = contours->h_next;
    }

    // sap xep
    for (int i = 0; i < rect.size() - 1; i++)
    {
        for (int j = i + 1; j < rect.size(); j++)
        {
            if (rect[i].x > rect[j].x)
            {
                CvRect sw;
                sw = rect[i];
                rect[i] = rect[j];
                rect[j] = sw;
            }
        }
    }

    // cat ky tu
    IplImage *charImg;
    IplImage *saveImg;

    for (int i = 0; i < rect.size(); i++)
    {
        charImg = cvCreateImage(cvSize(rect[i].width, rect[i].height), IPL_DEPTH_8U, 3);
        cvSetImageROI(resizeImg, rect[i]);
        cvCopy(resizeImg, charImg, NULL);
        cvResetImageROI(resizeImg);

        // add anh vao vector ki tu
        charImgVector.push_back(charImg);

        // show
        char name[8];
        sprintf(name, "Anh %d", i + 1);
        cvShowImage(name, charImg);


        // Luu anh lam mau de training
        saveImg = cvCreateImage(cvSize(rect[i].width, rect[i].height), IPL_DEPTH_8U, 1);
        cvCvtColor(charImgVector[i], saveImg, CV_RGB2GRAY);
        cvAdaptiveThreshold(saveImg, saveImg, 255, CV_ADAPTIVE_THRESH_MEAN_C, CV_THRESH_BINARY, 13, 2);

        char sname[8];
        sprintf(sname, "Data/%d.jpg", i + 1);
        //cvShowImage(sname, saveImg);

        cvSaveImage(sname, saveImg);
    }


    cvShowImage("character", cloneImg);

    return charImgVector;
}
 

wonder

Expert
Licensed User
Longtime User
Hi Roberto, please be so kind to have another look at what I've written above.

NLG only supports Primitive Types and Strings (as char* data type).
The code you posted appears to be too complex for NLG.

Does this code compile with standard C++ compilers such as GCC, Code::Blocks or Visual Studio?
If so, were you able to test this code before deploying to NLG?
Which functions do you want to expose to B4A?
What kind of data do you want to pass between C++ and B4A?
 

roberto64

Active Member
Licensed User
Longtime User
Hi wonder, you are perfectly right, the code written in c ++ visual studio, the data to be exposed between c ++ and b4a must process a license plate image and extract the data, thanks for your availability, however to the little I understand best in java .
regards
 

wonder

Expert
Licensed User
Longtime User
NLG 3.00 RELEASED!! :D
- It is now possible to return arrays from the native layer
- The printf() function will now output directly into B4A / logcat.
- Some bug fixes
- Minor code refactoring

nerdy dynamics.png

Picture unrelated

Returning arrays:
To return an array, you must indicate its size with the following comment:
//@NLG: Returns array (size)

Supported pointer types:
B4X:
returned as B4A Long():
- uintptr_t*, size_t*, uint64_t*, int64_t*, long*

returned as B4A Int():
- uint32_t*, int32_t*, int*

returned as B4A Short():
- uint16_t*, int16_t*, short*

returned as B4A Float():
- float*

returned as B4A Double():
- double*

returned as B4A Byte():
- byte* (as defined by "typedef unsigned char byte;")

returned as B4A String:
- char*


Examples:
B4X:
//Example 1 - Fixed size array
int myArray[5] = {1, 2, 3, 4, 5};
...
...

int *getMyArray(void)
{
    printf("Returning an array of size 5.");
    //@NLG: Returns array (5) 
    return myArray;
}
B4X:
//Example 2 - Dynamically sized array
int sizeOfMyArray = 5;
int *myArray = malloc(sizeof(int) * sizeOfMyArray); //new int[sizeOfMyArray];
...
...

int *getMyArray(void)
{
    printf("Returning an array of size %d.", sizeOfMyArray);
    //@NLG: Returns array (sizeOfMyArray) 
    return myArray;
}
...
...

free(myArray); //delete[] myArray;

How NOT to return an array:
B4X:
//Example 1 - Fixed size array
int *getMyArray(void)
{
    int myArray[5] = {1, 2, 3, 4, 5};
    printf("Returning an array of size 5.");
    //@NLG: Returns array (5)
    return myArray; //YOU CANNOT RETURN A POINTER TO A LOCAL VARIABLE!!!
}
B4X:
//Example 2 - Dynamically sized array
int *getMyArray(void)
{
    int sizeOfMyArray = 5;
    int *myArray = malloc(sizeof(int) * sizeOfMyArray); //new int[sizeOfMyArray];
    printf("Returning an array of size %d.", sizeOfMyArray);
    //@NLG: Returns array (sizeOfMyArray)
    return myArray; //YOU CANNOT RETURN A POINTER TO A LOCAL VARIABLE!!!
    free(myArray); //delete[] myArray; //THIS MAKES NO SENSE AT ALL!!
}
 
Last edited:
Top