Wikitude plugin in Xamarin captured image

2019-07-30 04:01发布

问题:

I have created a new plugin for Wikitude. I want to send the recognized image to another API . in the first place I have to get the image but I'm failed. I have asked this question in Wikitude forums too but no one answer. Here is my plugin code: I'm using Wikitude 6.0 and VisualStudio 2017

 public class Plugin02 : Com.Wikitude.Common.Plugins.Plugin
{
    public Plugin02(string p0) : base(p0)
    {
    }
    public override void CameraFrameAvailable(Frame p0)
    {
        try
        {
            // try 1
            BitmapFactory.Options Options = new BitmapFactory.Options { InSampleSize = 1 };
            Bitmap bitmap = BitmapFactory.DecodeByteArray(p0.GetData(), 0, p0.GetData().Length, Options);
            SaveBitmap(bitmap);
            // result : bitmap is always null


            //try 2
            var yuv = new YuvImage(p0.GetData(), ImageFormatType.Nv21, p0.Size.Width, p0.Size.Height, null);
            // var bmp = a.b(currentFrame.GetData(), currentFrame.GetData().Length, currentFrame.Size.Width, currentFrame.Size.Height);
            var ms = new MemoryStream();
            var quality = 100;   // adjust this as needed
            yuv.CompressToJpeg(new Rect(0, 0, yuv.Width, yuv.Height), quality, ms);
            var jpegData = ms.ToArray();
            Bitmap bitmap2 = BitmapFactory.DecodeByteArray(jpegData, 0, jpegData.Length);
            SaveBitmap(bitmap2);
            // result : image is not clear
        }
        catch (System.Exception ex) { }
    }
    void SaveBitmap(Bitmap bitmap)
    {
        var sdCardPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
        var filePath = System.IO.Path.Combine(sdCardPath, DateTime.Now.Millisecond.ToString() + ".jpg");
        var stream = new FileStream(filePath, FileMode.Create);
        bitmap.Compress(Bitmap.CompressFormat.Png, 100, stream);
        stream.Close();
    }
    public override void Update(RecognizedTarget[] p0)
    {

    }
    protected override void Initialize()
    {
        base.Initialize();
    }
    protected override void Pause()
    {
        base.Pause();
    }
    protected override void Resume(long p0)
    {
        base.Resume(p0);
    }
    protected override void Destroy()
    {
        base.Destroy();
    }

}

Here is my new code:

 public class Plugin01 : Com.Wikitude.Common.Plugins.Plugin
{
    TesseractApi api;
    public Plugin01(string p0) : base(p0)
    {
        api = new TesseractApi(Application.Context, AssetsDeployment.OncePerVersion);
        api.Init("eng");
    }


    Frame currentFrame = null;
    public override void CameraFrameAvailable(Frame p0)
    {
        try
        {
            var data = p0.GetData();
            currentFrame = p0;
        }
        catch (System.Exception ex) { }
    }

    public override void Update(RecognizedTarget[] p0)
    {
        if (p0 != null)
        {
            if (currentFrame != null)
            {
                ConvertYuvToJpeg(currentFrame, p0[0]);
            }
        }
    }
    protected override void Initialize()
    {
        base.Initialize();
    }
    protected override void Pause()
    {
        base.Pause();
    }
    protected override void Resume(long p0)
    {
        base.Resume(p0);
    }
    protected override void Destroy()
    {
        base.Destroy();
    }

    private void ConvertYuvToJpeg(Frame frame, RecognizedTarget p00)
    {
        try
        {
            YuvImage yuvimage = new YuvImage(frame.GetData(), ImageFormatType.Nv21, frame.Size.Width, frame.Size.Height, null);
            var outStream = new MemoryStream();
            var left = p00.TargetPositionInCameraFrame.Origin.GetY() + 10;
            var top = p00.TargetPositionInCameraFrame.Origin.GetX()+20;
            var right = left + p00.TargetPositionInCameraFrame.Size.Width;
            var bottom = top + p00.TargetPositionInCameraFrame.Size.Height;
            Rect rect = new Rect(left, top, right, bottom);

            yuvimage.CompressToJpeg(rect, 100, outStream);
            Bitmap bmp = BitmapFactory.DecodeByteArray(outStream.ToArray(), 0, outStream.ToArray().Length);

            Matrix mtx = new Matrix();
            mtx.PreRotate(90);
            bmp = Bitmap.CreateBitmap(bmp, 0, 0, bmp.Width, bmp.Height, mtx, false);

            var outStream1 = new MemoryStream();
            bmp.Compress(Bitmap.CompressFormat.Png, 100, outStream1);
            var bmpA = bmp.Copy(Bitmap.Config.Argb8888, true);

            api.SetImage(outStream1);
            //api.Results(Tesseract.PageIteratorLevel.Block);
            if (api.Text != null)
            {
            }

            var sdCardPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
            var filePath = System.IO.Path.Combine(sdCardPath, DateTime.Now.Millisecond.ToString() + ".jpg");
            var stream = new FileStream(filePath, FileMode.Create);
            bmp.Compress(Bitmap.CompressFormat.Jpeg, 100, stream);
            stream.Close();
        }
        catch (System.Exception ex) { }

    }

}

and Here is image sample:

according to what wikitude support said the image format is YV12 for Android and I couldn't find any way to convert it RGB or like that the only one was the Nv21 format.

I've added the YUV to RGB converter as you said and here is new code but something is wrong. The length of getData() is more than width * height.

public override void Update(RecognizedTarget[] p0)
    {
        if (p0 != null)
        {
            if (currentFrame != null)
            {
                //ConvertYuvToJpeg(currentFrame, p0[0]);
                ConvertYuvToRGB(currentFrame, p0[0]);
            }
        }
    }
    private void ConvertYuvToRGB(Frame frame, RecognizedTarget p00)
    {
        try
        {
            var rgbData = convertYUV420_NV21toARGB8888(frame.GetData(), frame.Size.Width, frame.Size.Height);
            var bitmap = Bitmap.CreateBitmap(100, 100, Bitmap.Config.Argb8888);
            // vector is your int[] of ARGB 
            bitmap.CopyPixelsFromBuffer(Java.Nio.IntBuffer.Wrap(rgbData));
            var sdCardPath = Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
            var filePath = System.IO.Path.Combine(sdCardPath, DateTime.Now.Millisecond.ToString() + ".jpg");
            var stream = new FileStream(filePath, FileMode.Create);
            bitmap.Compress(Bitmap.CompressFormat.Jpeg, 100, stream);
            stream.Close();
        }
        catch (System.Exception ex) { }
    }
    public static int[] convertYUV420_NV21toARGB8888(byte[] data, int width, int height)
    {
        int size = width * height;
        int offset = size;
        int[] pixels = new int[size];
        int u, v, y1, y2, y3, y4;

        // i along Y and the final pixels
        // k along pixels U and V
        for (int i = 0, k = 0; i < size; i += 2, k += 2)
        {
            y1 = data[i] & 0xff;
            y2 = data[i + 1] & 0xff;
            y3 = data[width + i] & 0xff;
            y4 = data[width + i + 1] & 0xff;

            u = data[offset + k] & 0xff;
            v = data[offset + k + size / 4] & 0xff;
            v = v - 128;
            u = u - 128;

            pixels[i] = convertYUVtoARGB(y1, u, v);
            pixels[i + 1] = convertYUVtoARGB(y2, u, v);
            pixels[width + i] = convertYUVtoARGB(y3, u, v);
            pixels[width + i + 1] = convertYUVtoARGB(y4, u, v);

            if (i != 0 && (i + 2) % width == 0)
                i += width;
        }

        return pixels;
    }
    private static int convertYUVtoARGB(int y, int u, int v)
    {
        int r = y + (int)(1.772f * v);
        int g = y - (int)(0.344f * v + 0.714f * u);
        int b = y + (int)(1.402f * u);
        r = r > 255 ? 255 : r < 0 ? 0 : r;
        g = g > 255 ? 255 : g < 0 ? 0 : g;
        b = b > 255 ? 255 : b < 0 ? 0 : b;
        return (int)(4278190080 | ((r + 16) | ((g + 8) | b)));
    }