So I'm trying to use opencvsharp to create an augmented reality tracker, and I'm having a problem with a memory leak.
I am trying to identify rectangles from a camera image (which are my markers) I'm fairly sure that the offending code is
CvSeq<CvPoint> firstcontour = null;
List<MarkerRectangle> rectangles = new List<MarkerRectangle>();
CvMemStorage storage = Cv.CreateMemStorage(0);
CvMemStorage storagepoly = Cv.CreateMemStorage(0);
IplImage gsImageContour = Cv.CreateImage(Cv.GetSize(thresholdedImage), thresholdedImage.Depth, thresholdedImage.NChannels);
//find contours
Cv.Copy(thresholdedImage, gsImageContour, null);
int contourCount = Cv.FindContours(gsImageContour, storage, out firstcontour, CvContour.SizeOf,
ContourRetrieval.CComp, ContourChain.ApproxSimple, Cv.Point(0, 0));
CvSeq<CvPoint> polycontour = firstcontour.ApproxPoly(CvContour.SizeOf, storagepoly, ApproxPolyMethod.DP, 4, true);
I'm fairly sure the offending lines are:
int contourCount = Cv.FindContours(gsImageContour, storage, out firstcontour, CvContour.SizeOf, ContourRetrieval.CComp, ContourChain.ApproxSimple, Cv.Point(0, 0));
and/or
CvSeq<CvPoint> polycontour = firstcontour.ApproxPoly(CvContour.SizeOf, storagepoly, ApproxPolyMethod.DP, 4, true);
However I'm not sure what I might be doing wrong, as they are not stored in any global variables i would expect the memory to be freed when they get to the end of the method.
It may be this bug, in which case I suspect I lack the technical expertise to fix it, as, with the exception of a couple of courses at uni, i haven't worked with unmanaged code. However i tried implementing the fix it lists under additional information and that didn't work so instead i'm thinking maybe it is not the cause of my problem.
Related
I'm working on a small personal application that should read some text (2 sentences at most) from a really simple Android screenshot. The text is always the same size, same font, and in approx. the same location. The background is very plain, usually a few shades of 1 color (think like bright orange fading into a little darker orange). I'm trying to figure out what would be the best way (and most importantly, the fastest way) to do this.
My first attempt involved the IronOcr C# library, and to be fair, it worked quite well! But I've noticed a few issues with it:
It's not 100% accurate
Despite having a community/trial version, it sometimes throws exceptions telling you to get a license
It takes ~400ms to read a ~600x300 pixel image, which in the case of my simple image, I consider to be rather long
As strange as it sounds, I have a feeling that libraries like IronOcr and Tesseract may just be too advanced for my needs. To improve speeds I have even written a piece of code to "treshold" my image first, making it completely black and white.
My current IronOcr settings look like this:
ImageReader = new AdvancedOcr()
{
CleanBackgroundNoise = false,
EnhanceContrast = false,
EnhanceResolution = false,
Strategy = AdvancedOcr.OcrStrategy.Fast,
ColorSpace = AdvancedOcr.OcrColorSpace.GrayScale,
DetectWhiteTextOnDarkBackgrounds = true,
InputImageType = AdvancedOcr.InputTypes.Snippet,
RotateAndStraighten = false,
ReadBarCodes = false,
ColorDepth = 1
};
And I could totally live with the results I've been getting using IronOcr, but the licensing exceptions ruin it. I also don't have $399 USD to spend on a private hobby project that won't even leave my own PC :(
But my main goal with this question is to find a better, faster or more efficient way to do this. It doesn't necessarily have to be an existing library, I'd be more than willing to make my own kind of letter-detection code that would work (only?) for screenshots like mine if someone can point me in the right direction.
I have researched about this topic and the best solution which I could find is Azure cognitive services. You can use Computer vision API to read text from an image. Here is the complete document.
How fast does it have to be?
If you are using C# I recommend the Google Cloud Vision API. You pay per request but the first 1000 per month are free (check pricing here). However, it does require a web request but I find it to be very quick
using Google.Cloud.Vision.V1;
using System;
namespace GoogleCloudSamples
{
public class QuickStart
{
public static void Main(string[] args)
{
// Instantiates a client
var client = ImageAnnotatorClient.Create();
// Load the image file into memory
var image = Image.FromFile("wakeupcat.jpg");
// Performs label detection on the image file
var response = client.DetectText(image);
foreach (var annotation in response)
{
if (annotation.Description != null)
Console.WriteLine(annotation.Description);
}
}
}
}
I find it works well for pictures and scanned documents so it should work perfectly for your situation. The SDK is also available in other languages too like Java, Python, and Node
I'm currently trying to create a swapchain for a CoreWindow using the latest SharpDX as DirectX wrapper and UWP as project base framework.
The documentation on that is so sparse it's unbelievable. Nonetheless I could find a snippet which looked promising. Inititally I always got an E_INVALIDCALL error message. Now it's "only" E_ACCESSDENIED.
So far I've done this to set up the chain:
var description = new SwapChainDescription1
{
BufferCount = 2,
Flags = SwapChainFlags.None,
SampleDescription = new SampleDescription(1, 0),
SwapEffect = SwapEffect.FlipSequential,
Usage = Usage.RenderTargetOutput,
Width = 0,
Height = 0,
Scaling = Scaling.None,
Format = Format.B8G8R8A8_UNorm,
Stereo = false
};
CoreWindow window = CoreWindow.GetForCurrentThread();
if (window == null)
{
Logging.Error("Could not retrieve core window for swap chain.");
throw new Exception("Invalid core window.");
}
using (var device = _device.QueryInterface<SharpDX.DXGI.Device2>())
{
device.MaximumFrameLatency = 1;
using (Adapter adapter = device.Adapter)
{
using (ComObject coreWindow = new ComObject(window))
{
using (Factory2 factory = adapter.GetParent<Factory2>())
_swapChain = new SwapChain1(factory, _device, coreWindow, ref description);
}
}
}
The constructor of SwapChain1 throws the SharpDX exception:
SharpDX.Result.CheckError()
SharpDX.DXGI.Factory2.CreateSwapChainForCoreWindow(ComObject deviceRef, ComObject windowRef, SwapChainDescription1& descRef, Output restrictToOutputRef, SwapChain1 swapChainOut)
SharpDX.DXGI.SwapChain1..ctor(Factory2 factory, ComObject device, ComObject coreWindow, SwapChainDescription1& description, Output restrictToOutput)
RobInspect.Visualizer.Rendering.RenderingPanel.InitializeSizeDependentResources()
RobInspect.Visualizer.Rendering.RenderingPanel.InitializeDevice()
"HRESULT: [0x80070005], Module: [General], ApiCode: [E_ACCESSDENIED/General access denied error], Message: Access is denied.
"
Can anyone explain me why? "Access denied" is quite a broad statement and I'm not that experienced with DirectX's internals.
Further information: The code is executing on the main (UI) thread. So I guess I can exclude that the CoreWindow reference is inaccessible. Since this is first-time initialisation I also exclude the possibility of DirectX objects not being freed properly before creating the swap chain.
EDIT:
That's the code for creating the device. Whereas the flags are set to DeviceCreationFlags.BgraSuuport and DeviceCreationFlags.Debug. The levels are set to FeatureLevel.Level_11_1 down to FeatureLevel.Level_9_1.
using (var device = new Device(DriverType.Hardware, flags, levels))
{
_device = device.QueryInterface<Device1>();
_context = _device.ImmediateContext1;
}
The solution to this problem is that the terms WinRT Core and WinRT XAML are rather misleading. Since UWP is based on CoreWindow and both support and use them it's not clear where to use what.
DirectX exposes two methods for WinRT and one for Desktop. One being Factory2.CreateSwapChainForCoreWindow(...) and one Factory2.CreateSwapChainForComposition(...). The difference is that one takes the CoreWindow as parameter and one does not. And here's the trap I fell into.
Core stands for the design-scheme with which one only uses IFrameworkView and IFrameworkViewSource (see here for an example with SharpDX) whereas XAML stands for the traditional scheme where you have the Windows.UI.Xaml.Application class.
When using the Core-model you have to call the ...ForCoreWindow(...) method in order to create a swap chain. While using the XAML based approach you need a composition swap chain. I for myself already tried that, but failed because I forgot to enable (tip: do this if not already done) native debugging so the DirectX Debug Layer actually showed me essential information which could have saved me hours if not days of trial and error.
The issue here is that both composition and CoreWindow swap chains require special settings in the SwapChainDescription1. I'll leave you with the MSDN documentation. Moreover if native debugging and the debug layer is enabled, DirectX will tell you exactly what setting is invalid.
I am writing a SharpDX (v4.0.1) application that displays assemblies composed of many (possibly very big) separate parts. Each part has its own vertex and index buffer. When I try to load a very big assembly, I eventually run into E_OUTOFMEMORY exception when trying to create a vertex buffer for a part:
int size = _meshVertices.Length * Utilities.SizeOf<MeshVertex>();
BufferDescription descr = new BufferDescription(size, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
_vertexBuffer = Buffer.Create(_device.Device, _meshVertices, descr);
$exception {"HRESULT: [0x8007000E], Module: [General], ApiCode: [E_OUTOFMEMORY/Out of memory], Message: Not enough storage is available to complete this operation.\r\n"} SharpDX.SharpDXException
When I was googling a possible solution, I found out that there is a size limit on a resource - but this doesn't seem to be the case as it happens even for smaller parts.
Another solution I tried was using the MemoryFailPoint class to try to reserve the memory before creating the buffer. But I suppose this checks for available RAM, while I believe the exception is caused by having not enough GPU memory. Anyway, this seems to alleviate the problem a bit but it doesn't fix it completely. It just takes longer before it fails.
int size = _meshVertices.Length * Utilities.SizeOf<MeshVertex>();
using (System.Runtime.MemoryFailPoint memFailPoint = new System.Runtime.MemoryFailPoint(size / 1024 / 0124))
{
BufferDescription descr = new BufferDescription(size, ResourceUsage.Default, BindFlags.VertexBuffer, CpuAccessFlags.None, ResourceOptionFlags.None, 0);
_vertexBuffer = Buffer.Create(_device.Device, _meshVertices, descr);
}
My problem is that after I get the exception, the device is lost. I assume that the resources such as vertex buffers are bound to the device, so to recreate the device after it is lost would mean to recreate all the resources which would be very time consuming.
So preferably, I am looking for a way to know that the device does not have enough memory before I try to create the buffer, so that I could just discard the single part if needed. Is it possible to do something like that?
Any help would be very much appreciated. I am new to SharpDX so if you think I misunderstood the problem and something else is causing it, please let me know as well. Thanks in advance for any input on this.
I am not an experienced programmer, just need to add a DICOM viewer to my VS2010 project. I can display the image in Windows Forms, however can't figure out how to change the window center and width. Here is my script:
DicomImage image = new DicomImage(_filename);
int maxV = image.NumberOfFrames;
sbSlice.Maximum = maxV - 1;
image.WindowCenter = 7.0;
double wc = image.WindowCenter;
double ww = image.WindowWidth;
Image result = image.RenderImage(0);
DisplayImage(result);
It did not work. I don't know if this is the right approach.
The DicomImage class was not created with the intention of it being used to implement an image viewer. It was created to render preview images in the DICOM Dump utility and to test the image compression/decompression codecs. Maybe it was a mistake to include it in the library at all?
It is difficult for me to find fault in the code as being buggy when it is being used for something far beyond its intended functionality.
That said, I have taken some time to modify the code so that the WindowCenter/WindowWidth properties apply to the rendered image. You can find these modifications in the Git repo.
var img = new DicomImage(fileName);
img.WindowCenter = 2048.0;
img.WindowWidth = 4096.0;
DisplayImage(img.RenderImage(0));
I looked at the code and it looked extremely buggy. https://github.com/rcd/fo-dicom/blob/master/DICOM/Imaging/DicomImage.cs
In the current buggy implementation setting the WindowCenter or WindowWidth properties has no effect unless Dataset.Get(DicomTag.PhotometricInterpretation) is either Monochrome1 or Monochrome2 during Load(). This is already ridiculous, but it still cannot be used because the _renderOptions variable is only set in a single place and is immediately used for the _pipeline creation (not giving you chance to change it using the WindowCenter property). Your only chance is the grayscale _renderOptions initialization: _renderOptions = GrayscaleRenderOptions.FromDataset(Dataset);.
The current solution: Your dataset should have
DicomTag.WindowCenter set appropriately
DicomTag.WindowWidth != 0.0
DicomTag.PhotometricInterpretation == Monochrome1 or Monochrome2
The following code accomplishes that:
DicomDataset dataset = DicomFile.Open(fileName).Dataset;
//dataset.Set(DicomTag.WindowWidth, 200.0); //the WindowWidth must be non-zero
dataset.Add(DicomTag.WindowCenter, "100.0");
//dataset.Add(DicomTag.PhotometricInterpretation, "MONOCHROME1"); //ValueRepresentations tag is broken
dataset.Add(new DicomCodeString(DicomTag.PhotometricInterpretation, "MONOCHROME1"));
DicomImage image = new DicomImage(dataset);
image.RenderImage();
The best solution: Wait while this buggy library is fixed.
i am trying to get the text in SysListView32 from another app by C#.
i can get the LVM_GETITEMCOUNT well but LVM_GETITEMW = 0x1000 + 13 always returns -1. how can i get the text by C#? i am new. thanks very much!
ParenthWnd = FindWindow(ParentClass, ParentWindow);
if (!ParenthWnd.Equals(IntPtr.Zero))
{
zWnd = FindWindowEx(ParenthWnd, zWnd, zClass, zWindow);
if (!zWnd.Equals(IntPtr.Zero))
{
int user = SendMessage(zWnd, LVM_GETITEMCOUNT, 0, 0);
}
You need to work harder to read and write the LVITEM memory since you are working with a control owned by another process. You therefore need to read and write memory in that process. You can't do that without calling ReadProcessMemory, WriteProcessMemory etc.
The most commonly cited example of the techniques involved is this Code Project article: Stealing Program's Memory. Watch out for 32/64 bit gotchas.