Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Xamarin Camera Control and the Cloud: A Merge Made in Heaven with LEADTOOLS

3 Jun 2019 1  
Use LEAD'S Cloud Services while quickly leveraging the cameras on mobile devices without needing to know the native camera APIs.

This article is in the Product Showcase section for our sponsors at CodeProject. These articles are intended to provide you with information on products and services that we consider useful and of value to developers.

Introduction

Our previous product showcase focused on how to use LEAD’s new Xamarin Camera Control to easily leverage a mobile device camera. We also incorporated the Barcode SDK into the application to read barcodes and display the data found to the device’s screen.

Staying on the Xamarin Camera Control train, we are now going to incorporate one of the new LEADTOOLS Cloud Services methods to our application, the Merge method. This method merges specific pages into another file and can be called with a POST Request to the following URL:

[POST]  https://azure.leadtools.com/api/Conversion/Merge

The LEADTOOLS Cloud Services are Web API endpoints for customers to access the high-powered functionality provided by LEADTOOLS SDK without the hassle of setting up their own servers. We currently offer Document Conversion, Document Merging, Redaction, Barcode, OCR, MICR, AAMVAID, and Business Card Extraction services.

The Merge method we are using for this application gives users the ability to choose which pages from the input documents to be merged. Specify a range of pages, an array of pages, or the entire document to be merged. If the target format is a document type, merge retains the text and only uses OCR when needed.

Applying both the Xamarin Camera Control and LEADTOOLS Cloud Services has limitless potential for applications across various industries. For instance, say an insurance company requires pictures of damaged property for proof of damages. Instead of sending 20 individual images, you can create one file that includes those 20 images.

Setting up Your App

The first step is to retrieve your free 30-day evaluation license by registering at https://www.leadtools.com/downloads/nuget and create an account for your free trial of LEADTOOLS Cloud Services at https://services.leadtools.com/account/register.

Once you have a LEADTOOLS license, open Visual Studio 2017 and create a new project. First, add the Leadtools.Camera.Xamarin assembly in the MainPage.xaml.

xmlns:leadtools="clr-namespace:Leadtools.Camera.Xamarin;assembly=Leadtools.Camera.Xamarin"

Now replace the default auto-generated label within the StackLayout with the Xamarin CameraView, as well as a button that will be used to take the picture and one that will merge all the images that have been taken.

<StackLayout>
	<leadtools:CameraView x:Name="leadCamera" CameraOptions="Rear" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand"/>
	<Button x:Name="snapBtn" HorizontalOptions="FillAndExpand" Text="Snap Picture" Clicked="SnapClicked" />
	<Button x:Name="mergeBtn" HorizontalOptions="FillAndExpand" Text="Merge" Clicked="performMerge" />
	<Button x:Name="downloadFileBtn" HorizontalOptions="FillAndExpand" Text="Go to file!" Clicked="downloadFile"/>
</StackLayout>

For more of an introduction to using the LEADTOOLS Camera Control for Xamarin as well as setting the LEADTOOLS license, check out the previous article, https://www.codeproject.com/Articles/1349139/Finally-a-Camera-Control-for-Xamarin. Please note that the previous article is necessary to complete before continuing.

Now that the Xamarin Camera Control has been added and the license has been set, add the code to take the picture. The CameraView Class has an event called PictureReceived which will be fired when an image is captured after calling ICamera.TakePicture().

The call to the TakePicture method will go in the button click event.

void SnapClicked(object sender, EventArgs args)
{
    snapBtn.IsEnabled = false;
    leadCamera.Camera.TakePicture();
}

To create the event handler for PictureReceived, add the following line of code after InitializeComponent():

leadCamera.PictureReceived += LeadCamera_PictureReceived;

The Code

First, create some global variables and create a new class.

private static HttpClient client;
private List<MergeRequestObject> MergeImages = new List<MergeRequestObject>();
private string hostedServicesUrl = "https://azure.leadtools.com/api/";
private static string fileUrl;
public class MergeRequestObject
{
    public string FileId { get; set; }
}

As mentioned above, and from our previous article, MainPage.xaml.cs has everything needed to start the Xamarin Camera Control. On top of that, add code to auto-rotate the image, enable the merge button, disable the download button, and an InitClient method. You should have the following below InitializeComponent().

leadCamera.CameraOptions.AutoRotateImage = true;
leadCamera.PictureReceived += LeadCamera_PictureReceived;
mergeBtn.IsEnabled = false;
downloadFileBtn.IsEnabled = false;
InitClient();

InitClient is needed for authenticating your application with the LEADTOOLS Cloud Services.

private void InitClient()
{
    // Once you created your app in your account https://services.leadtools.com/manage 
    // you will receive an email with the AppId and Password
    string AppId = "Enter AppID Here";
    string Password = "Enter SecretKey Here";

    client = new HttpClient
    {
        BaseAddress = new Uri(hostedServicesUrl)
    };

    client.DefaultRequestHeaders.Accept.Clear();
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

    string authData = string.Format("{0}:{1}", AppId, Password);
    string authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);
}

The code inside PictureReceived will be for saving each image that has been taken and then uploading them to the LEADTOOLS Cloud Services. In this example, the images will be stored in a MemoryStream as a PNG. After the image is saved, it will need to be uploaded to the LEADTOOLS Cloud Services. To do that, a method called UploadForMerge will be used. This method will need the memory stream and will return a GUID. Once you have the GUID, it will be stored in a list, List<MergeRequestObject> mergeImages. The MergeRequestObject is a class that we created that contains a string called FileID. FileID will now be the GUID that was returned from UploadForMerge. Each image that is taken will go through this process and will have its own GUID that will be added to the list.

private void LeadCamera_PictureReceived(FrameHandlerEventArgs e)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        snapBtn.Text = "Image is uploading to LEADTOOLS Cloud Services, please wait";
        mergeBtn.IsEnabled = false;
    });

    using (MemoryStream ms = new MemoryStream())
    {
        using (RasterCodecs codecs = new RasterCodecs())
        {
            codecs.Save(e.Image, ms, RasterImageFormat.Png, 0);

            var id = UploadForMerge(ms);

            MergeImages.Add(new MergeRequestObject { FileId = id.ToString() });

            Device.BeginInvokeOnMainThread(() =>
            {
                snapBtn.IsEnabled = true;
                snapBtn.Text = "Snap Picture";
                mergeBtn.Text = $"Merge {MergeImages.Count} file(s)";
                DisplayAlert("Image saved!", "Image has been uploaded", "OK");
                mergeBtn.IsEnabled = true;
            });
        }
    }
}

The UploadForMerge method is where the image is getting sent to the LEADTOOLS Cloud Services and then returning a GUID.

public Guid UploadForMerge(MemoryStream uploadForMergeStream)
{
    HttpContent byteContent = new ByteArrayContent(uploadForMergeStream.ToArray());

    using (var formData = new MultipartFormDataContent())
    {
        formData.Add(byteContent, "imageStream");

        var url = "UploadFile?forMerge=true";
        var res = client.PostAsync(url, formData).Result;
        var guid = Guid.Empty;

        if (res.IsSuccessStatusCode)
        {
            var id = res.Content.ReadAsStringAsync().Result;
            return Guid.Parse(id);
        }
        else
            return Guid.Empty;
    }
}

Once all the pictures have been taken and are uploaded to the LEADTOOLS Cloud Services, it’s time to merge them to a single file. For this, create a new method called PerformMerge.

void performMerge(object sender, EventArgs args)
{
    Device.BeginInvokeOnMainThread(() =>
    {
        mergeBtn.Text = "Merging files, please wait";
        snapBtn.IsEnabled = false;
        mergeBtn.IsEnabled = false;
    });

    var id = PostMerge();

    if (id == Guid.Empty)
        Device.BeginInvokeOnMainThread(() =>
        {
            DisplayAlert("Error", "GUID is empty", "OK");
        });

    var results = Query(id.ToString());

    // Parse results and add the URL to the fileUrl button
    JArray array = JArray.Parse(results);
    foreach (var requestReturn in array)
    {
        var UrlArray = JArray.Parse(requestReturn.SelectToken("urls").ToString());
        foreach (var uri in UrlArray)
        {
            Device.BeginInvokeOnMainThread(() =>
            {
                fileUrl = uri.ToString();
            });
        }
    }

    // Clear list to create a new single file
    MergeImages.Clear();
    Device.BeginInvokeOnMainThread(() =>
    {
        downloadFileBtn.IsEnabled = true;
        mergeBtn.Text = "File(s) have been merged";
        snapBtn.IsEnabled = true;
    });
}

Inside PerformMerge is another method called PostMerge method which will also return a GUID.

public Guid PostMerge()
{
    var stringContent = new StringContent(JsonConvert.SerializeObject(MergeImages), Encoding.UTF8, "application/json");
    // Format 4 will save merged files to a PDF
    var url = $"Conversion/Merge?Format=4";
    var res = client.PostAsync(url, stringContent).Result;

    if (res.IsSuccessStatusCode)
    {
        var id = res.Content.ReadAsStringAsync().Result;
        return Guid.Parse(id);
    }
    else
        return Guid.Empty;
}

Continuing with the PerformMerge method, this is where the results will be returned from using the ID returned from PostMerge and then queried using a new method called Query which takes in a string and returns results.

private string Query(string id)
{
    string queryUrl = $"Query?id={id.ToString()}";
    int status = 100;
    string results = "";
    JObject returnedData = null;
    while (status == 100 || status == 123)
    {
        Task.Delay(500).Wait();
        var result = client.PostAsync(queryUrl, null).Result;
        var returnedContent = result.Content.ReadAsStringAsync().Result;
        returnedData = JObject.Parse(returnedContent);
        status = (int)returnedData.SelectToken("FileStatus");
    }

    if (status == 200)
        results = returnedData.SelectToken("RequestData").ToString();

    return results;
}

Add the downloadFile method for downloading the file.

void downloadFile(object sender, EventArgs args)
{
    Device.OpenUri(new Uri(fileUrl));
}

Conclusion

These two technologies provide developers with one, a high-level API to access a mobile devices camera, and two, a programmer-friendly Web API. Combining these gives developers the ability to create on the go mobile service applications.

Create an account and get your first 50 pages for free!

https://services.leadtools.com/account/register

Support

Need help getting this sample up and going? Contact our support team for free technical support! For pricing or licensing questions, you can contact our sales team (sales@leadtools.com) or call us at 704-332-5532.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here