Skip to Content

Imagine this: your friend is a plant maintenance supervisor. His colleagues running the plant fill out hand-written forms identifying broken down equipment. Half those papers he loses somewhere in his disaster of a desk. The other half he has to mash out on his desktop keyboard over lunch. His job is fixing problems, but he’s spending more time pushing papers.

Enter you. You’re a phenomenally skilled SAP developer, known through the company for creative solutions. You hand your buddy an iPhone. On it runs an SAPUI5 app that lets him take a picture of the paper as soon as it’s handed to him. The app interprets the things written on the paper and puts them in the appropriate fields on his screen. He gives it a once-over and hits “Submit” – ready to actually enjoy a distraction-free lunch break.

You are a hero to your friend and he tells everyone how awesome you are. The CEO gets wind of this innovation and makes you CTO. You retire wealthy and start that surf shop on the beach you always dreamed of.

I’m going to show you how to give your Gateway system “eyes” that can interpret content in a photo. In a ridiculously easy way. No promises on the surf shop, though.

Google recently made their Cloud Vision API available for anyone to try. I love when the big software companies give me things to play with. It means I can try out wild and crazy ideas from the comfort of my keyboard. So as soon as I could, I took some free time to tinker with the Vision API and mash it up with SAP Gateway.

I present here a simple prototype for using these two tools in tandem. There are about a billion ways this could be useful, so I hope my little slice of code helps someone along the way.

I’ll show you how to use Gateway to request Google Vision API processing. I picked a couple Vision abilities that I find awesome, but the API is capable of more.

Without further ado – let’s get started!


Setup


Before you write any code, you’ll need:

  • An SAP Gateway system. If you’re reading this blog and don’t know what that is, then I apologize because you’re probably really bored.
  • Configuration to allow that system to HTTP POST to an external internet API. See here for setting up STRUST to allow that.
  • A Google account, with the Cloud Vision API enabled. Be warned: if you use it more than 1,000 times a month, it’s not free. Just make sure it takes you less than 1,000 tries to get it right.
  • An API key set up in the Google account. I suggest using the browser API key for prototyping, and service accounts for productive use. Getting an API key is covered in the Google getting started guide.

Once you have the above configured, it’s time to cracking on the code.

Show Me The Code Already

Now that we have things ready to roll, fire up your Gateway system and go to t-code SEGW. I set up a very simple entity that will just hold the description of what Google thinks an image is. Just 3 fields:


Screen Shot 2016-02-26 at 4.56.55 PM.png


Make sure to flag that entity as a “Media” entity:


Screen Shot 2016-02-26 at 4.57.38 PM.png


And that’s it for our bare-bones service definition. You could get a lot more architecturally crazy and set up a bunch of entities and fields to capture every single thing that comes out of the Google API – but I just wanted to get it up and running to see what I could get.


Only two steps left in the setup: coding the model enhancement necessary for media entities and coding the CREATE_STREAM method for processing the file data.


First, the model enhancement. Navigate to the *MPC_EXT class in your project and do a redefinition of the DEFINE method. This code should get you what you need. It’s so short that it’s basically self-explanatory:



  METHOD define.

    super->define( ).

    DATA:
    lo_entity   TYPE REF TO /iwbep/if_mgw_odata_entity_typ,
    lo_property TYPE REF TO /iwbep/if_mgw_odata_property.

    lo_entity = model->get_entity_type( iv_entity_name = 'VisionDemo' ).

    IF lo_entity IS BOUND.
      lo_property = lo_entity->get_property( iv_property_name = 'ContentType' ).
      lo_property->set_as_content_type( ).
    ENDIF.

  ENDMETHOD.


The model is now ready to support media stuff (in our case pictures) coming in. The other side of the equation is to prepare the request to be sent to Google for processing. We’ll do that in the CREATE_STREAM method of the *DPC_EXT class that the SEGW project generated. Same deal as before, do a redefine of that method and put in the following code:



  METHOD /iwbep/if_mgw_appl_srv_runtime~create_stream.
    TYPES: BEGIN OF feature,
             type TYPE string,
             max_results TYPE i,
           END OF feature.

    TYPES: features TYPE STANDARD TABLE OF feature WITH DEFAULT KEY.

    TYPES: BEGIN OF image,
             content TYPE string,
           END OF image.

    TYPES: BEGIN OF request,
             image TYPE image,
             features TYPE features,
           END OF request.

    TYPES: requests TYPE STANDARD TABLE OF request WITH DEFAULT KEY.

    TYPES: BEGIN OF overall_request,
             requests TYPE requests,
           END OF overall_request.

    DATA overall_request TYPE overall_request.
    DATA requests TYPE TABLE OF request.
    DATA request TYPE request.
    DATA feature TYPE feature.
    DATA lv_b64_content TYPE string.
    DATA lo_http_client  TYPE REF TO if_http_client.
    DATA lv_response_data TYPE string.
    DATA lv_url TYPE string.
    DATA lv_request_json TYPE string.
    DATA lv_response_json TYPE string.
    DATA lo_descr TYPE REF TO cl_abap_structdescr.
    DATA lv_start TYPE i.
    DATA lv_end TYPE i.
    DATA lv_total_chars TYPE i.
    DATA ls_visiondemo TYPE zcl_zgoogle_vision_mpc=>ts_visiondemo.
    DATA lv_end_marker TYPE string.

    CALL FUNCTION 'SCMS_BASE64_ENCODE_STR'      EXPORTING        input  = is_media_resource-value      IMPORTING        output = lv_b64_content.

    lv_url = 'https://vision.googleapis.com/v1/images:annotate?key=GET_YOUR_OWN_KEY'.

    request-image-content = lv_b64_content.
    feature-type = iv_slug.
    feature-max_results = 1.
    APPEND feature TO request-features.
    APPEND request TO requests.
    overall_request-requests = requests.

    lo_descr ?= cl_abap_typedescr=>describe_by_data( overall_request ).

    lv_request_json = /ui2/cl_json=>dump( data = overall_request type_descr = lo_descr pretty_name = abap_true ).

    cl_http_client=>create_by_url(
      EXPORTING        url                = lv_url      IMPORTING        client             = lo_http_client ).

    lo_http_client->request->set_method( method = 'POST' ).
    lo_http_client->request->set_content_type( content_type = 'application/json' ).
    lo_http_client->request->append_cdata2( EXPORTING data = lv_request_json ).
    lo_http_client->send( ).
    lo_http_client->receive( ).
    lv_response_data = lo_http_client->response->get_cdata( ).

    IF iv_slug = 'LOGO_DETECTION'.
      lv_end_marker = '"score":'.
    ELSE.
      lv_end_marker = '"boundingPoly":'.
    ENDIF.

    SEARCH lv_response_data FOR '"description":'.
    lv_start = sy-fdpos + 16.

    SEARCH lv_response_data FOR lv_end_marker.
    lv_end = sy-fdpos.

    lv_total_chars = lv_end - lv_start.
    ls_visiondemo-id = 1.
    ls_visiondemo-description = lv_response_data+lv_start(lv_total_chars).

    copy_data_to_ref( EXPORTING is_data = ls_visiondemo                       CHANGING cr_data = er_entity ).

  ENDMETHOD.


Note the following about this code snippet:

  • I’m using the IV_SLUG parameter to control what kind of request (logo or text detection) I’m making to Google. This means using the “slug” header in an HTTP request, which I’ll show you below.
  • Google expects picture data to be base64 encoded, so the FM SCMS_BASE64_ENCODE_STR handles that for us.
  • Get your own API key – the string at the end of my URL will not work for you. Replace GET_YOUR_OWN_KEY with your actual key.
  • There are a number of ways to handle JSON type data in ABAP. I used the /ui2/cl_json method purely out of simplicity for a demo. For a more robust solution see how to use JSON with the XML parsing tools.
  • There is basically no error handling here. That’s the great thing about prototyping. 🙂
  • I know the way I pull the description out of the response is a total hack.

Try It Out

The easiest way to try this out is through the Gateway Client (/iwfnd/gw_client). Here’s how:


Navigate to /iwfnd/gw_client on your Gateway system. Enter the request parameters as seen here (assuming you’ve named things the same that I have):


gw_client setup.PNG


The two possible values I verified for “slug” are TEXT_DETECTION and LOGO_DETECTION – though the API supports many more than that.


Next, put a picture in the body of the request by clicking “Add File”. If you choose TEXT_DETECTION as the slug, then make sure your image actually has text. Here’s what the result looks like if I put in a picture of my business card. Look at the “Description” field in the right hand text (and note that Google automatically puts in newline characters if there are line breaks in the picture):


result from business card.PNG



And check it out if I put in a logo with the LOGO_DETECTION slug parameter (“Skunk Works” is the right answer for this picture):


logo detection result.PNG


Wrap It Up, Paul

So I’ve proved out that I can use the Google Cloud Vision API in conjunction with SAP Gateway – but I haven’t really done anything truly useful. However, I have some really exciting ideas for this and can’t wait to continue using these mega-powerful cloud services! I hope that my small example helps someone else dream big and make something amazing.

To report this post you need to login first.

10 Comments

You must be Logged on to comment or reply to a post.

  1. Pavan Golesar

    Superb Paul,

    Thanks for again sharing something amazing (After the weather based – Odata service). 😉

    You’ve also listed IMP FM required so don’t need to dig again for FM REQUIRED.

    Cheers!

    Pavan G

    (0) 
  2. Andre Fischer

    Hi Paul,

    very nice blog.

    It’s good to know that my SAP Gateway can see everything 😎 .

    There is a link missing in the blog that should point to a URL that explains setting up STRUST to allow system to perform a HTTP POST to an external internet API.

    Best Regards,

    Andre

    (0) 
  3. Pavan Golesar

    While understanding the code, I wonder whats ‘/ui2/cl_json=>dump(….)

    Is it some error handling stuff.. or sumthing else..

    Thanks

    Pavan G

    (0) 
    1. Paul J. Modderman Post author

      The dump( ) call is taking the ABAP data object and making it into a string formatted in json style. The HTTP POST request out to google requires a json body that looks something like the below, and the dump( ) method arranges that with the ABAP object.

      e.g.

      {

        “requests”: [

        {

        “image”: {

        “content”: “<paste the contents of your base64-encoded output_file here>”

        },

        “features”: [

        {

        “type”: “LABEL_DETECTION”,

        “maxResults”: 1

        }

        ]

        }

        ]

      }

      (0) 

Leave a Reply