Search code examples
c#halcon

Halcon FindNccModel causes memory leak in C#


Using the Halcon 13 function FindNccModel in C# causes the following error:

HALCON error #6001: Not enough memory available in operator find_ncc_model

class Program
{
    static void Main(string[] args)
    {
        HImage Image = new HImage(@"08_09_09_41_33_582_OK_000000153000.png");
        double MidpointRow = 1053.5210373923057, MidpointCol = 1223.5205413999142;

        int iCounter = 0;

        while (true)
        {
            HNCCModel model = new HNCCModel(@"000000135000Mark_0.ncm");

            HXLDCont hxCont = new HXLDCont();
            hxCont.GenRectangle2ContourXld(
                721.9213759213759,
                1775.862648221344,
                -0.99483767363676778,
                72,
                14.5);

            HTuple htRowXLD, htColXLD;
            hxCont.GetContourXld(out htRowXLD, out htColXLD);
            HTuple htRadius = new HTuple();
            htRadius = new HTuple(htRowXLD.TupleSub(MidpointRow).TuplePow(2) + htColXLD.TupleSub(MidpointCol).TuplePow(2)).TupleSqrt();
            HRegion hrAnnulus = new HRegion();
            hrAnnulus = hrAnnulus.GenAnnulus(MidpointRow, MidpointCol, htRadius.TupleMin() - 5.0, htRadius.TupleMax() + 5.0);

            HImage hiTemp = Image.Clone();
            HImage hiTemp2 = hiTemp.Rgb1ToGray();
            HImage hiTemp3 = hiTemp2.ReduceDomain(hrAnnulus);

            HTuple htRow, htColumn, Angle, Score;

            model.FindNccModel(hiTemp3, -0.39, 6.29, 0.65, 1, 0, "true", 0, out htRow, out htColumn, out Angle, out Score);

            hxCont.DisposeIfNotNull();
            hrAnnulus.DisposeIfNotNull();
            model.Dispose();

            hiTemp.DisposeIfNotNull();
            hiTemp2.DisposeIfNotNull();
            hiTemp3.DisposeIfNotNull();

            Console.WriteLine(iCounter++.ToString());
        }
    }
}

public static class DL_HalconUtilityClass
{
    public static HRegion GenAnnulus(this HRegion region, double dCenterRow, double dCenterColumn, double dRadiusSmall, double dRadiusBig)
    {
        region.GenEmptyRegion();

        if (dRadiusSmall > dRadiusBig)
        {
            throw new NotSupportedException("Wrong input parameters. Small radius is bigger than big radius.");
        }

        HRegion hrCircleSmall = new HRegion(dCenterRow, dCenterColumn, dRadiusSmall);
        HRegion hrCircleBig = new HRegion(dCenterRow, dCenterColumn, dRadiusBig);

        region = new HRegion();
        region = hrCircleBig.Difference(hrCircleSmall);

        hrCircleSmall.Dispose();
        hrCircleBig.Dispose();

        return region;
    }

    public static void DisposeIfNotNull(this HImage hiImage)
    {
        if (hiImage != null) hiImage.Dispose();
    }

    public static void DisposeIfNotNull(this HRegion hrRegion)
    {
        if (hrRegion != null) hrRegion.Dispose();
    }

    public static void DisposeIfNotNull(this HObject hoObject)
    {
        if (hoObject != null) hoObject.Dispose();
    }
}

The function itself can run endlessly in an while loop, but if it's combined with our program it causes a memory exception. On the other hand the program itself can run endlessly without this function. It is also interesting that the error happens before the program reaches typical 1,1 Gb of memory which means that there is a memory leak.

I didn't find any references to this problem in Halcon documentation and upgrading to the newest Halcon 13 version or using Halcon XL did not help. Does anyone know what could cause this problem?


Solution

  • Halcon has two memory management optimization system settings: global_mem_cache and temporary_mem_cache. The global_mem_cache had no influence, but set the temporary_mem_cache parameter to "idle" or "shared" solved the problem.

    Default setting is "exclusive" where temporary memory is cached locally for each thread. This is an excerpt from Halcon documentation:


    'temporary_mem_cache' *), 'tsp_temporary_mem_cache' This parameter controls the operating mode of the temporary memory cache. The temporary memory cache is used to speed up an application by caching memory used temporarily during the execution of an operator. For most applications the default setting ('exclusive') will produce the best results. The following modes are supported:

    • 'idle' The temporary memory cache is turned off. This mode will use the least memory, but will also reduce performance compared to the other modes.

    • 'shared' All temporary memory is cached globally in the temporary memory reservoir. This mode will use less memory than 'exclusive' mode, but will also generally offer less performance.

    • 'exclusive' All temporary memory is cached locally for each thread. This mode will use the most memory, but will generally also offer the best performance.

    • 'aggregate' Temporary memory blocks that are larger than the threshold set with the 'alloctmp_max_blocksize' parameter are cached in the global memory reservoir, while all smaller blocks are aggregated into a single block that is cached locally for each thread. If the global memory reservoir is disabled, the large blocks are freed instead. The aggregated block will be sized according to the temporary memory usage the thread has seen so far, but it will not be larger than 'alloctmp_max_blocksize' (if set) or smaller than 'alloctmp_min_blocksize' (if set). This mode balances memory usage and speed, but requires correctly setting 'alloctmp_min_blocksize' and 'alloctmp_max_blocksize' for the application's memory usage pattern for effectiveness.

    Note that cache mode 'idle' is set in exclusive run mode, whereas the other modes are set in reentrant mode.

    For backward compatibility, the values 'false' and 'true' are also accepted; they correspond to 'idle' and 'exclusive', respectively.