cancel
Showing results for 
Search instead for 
Did you mean: 

Thumbnail creation during Image Upload

Former Member
0 Kudos

In my application, I am able to upload documents to content server using KPRO API's. Image upload works fine and I can display the same file in webdynpro image UI element. Now while uploading the image, i also need to create a thumbnail image and store it. I can use these thumbnail images for image navigation instead of actual file due to performance issues.

How to create thumbnail images from jpeg image file?

Edited by: ashwinsrinivas on Jul 20, 2010 8:41 PM

Accepted Solutions (1)

Accepted Solutions (1)

ChrisPaine
Active Contributor
0 Kudos

Hello,

As far as I am aware, there is no API available in SAP for generation of thumbnails.

Happy to be proved wrong! - However, you might want to think about implementing an ABAP version of the code described in the Enlightenment eThumb library - [http://sourceforge.net/projects/enlightenment/|http://sourceforge.net/projects/enlightenment/]

The code is covered by the BSD license which means you are free to use it pretty much as you want. It wouldn't be a trivial piece of work to port the code from C to ABAP - but then again it does give you a very good head start on how you might be able to code this solution. If you do go down this path, I'm sure many of us here on SCN would be interested in how you could/would solve such an issue - please do share your solution

Hope this is helpful,

Chris

Former Member
0 Kudos

Thanks for the details and suggested approach. Unfortunately I am unable to find the ethumb library or implementation in C, so that i can migrate the code in ABAP. It will be great if you can provide direct link to C implementation. I am not expert in C folder heirarchy, library concept, make files etc to get this from above link.

ChrisPaine
Active Contributor
0 Kudos

Hmm

I just looked through the code - it mainly uses other libraries to do the work:

[http://trac.enlightenment.org/e/browser/trunk/ethumb/src/lib/Ethumb.c|http://trac.enlightenment.org/e/browser/trunk/ethumb/src/lib/Ethumb.c]

so I don't think it is going to help you much...

Sorry about that...

I've seen Java code ([http://www.philreeve.com/java_high_quality_thumbnails.php|http://www.philreeve.com/java_high_quality_thumbnails.php]) that would work, but it uses standard Java libs that we don't have access to...

Way back when at Uni I wrote some code to do something like this, but it's not trivial... I guess if you knew the format of the incoming file then that would be simpler - GIF is a lot easier than JPG from memory.

Sorry I couldn't help more,

Chris

Former Member
0 Kudos

How about using adjustImageSize property and resize the image in runtime and then read back the data from image element ?

is it possible to get data of an image (not source attribute, but actual content of resized image) ?

ChrisPaine
Active Contributor
0 Kudos

>I can use these thumbnail images for image navigation instead of actual file due to performance issues.

I think serving the entire image (even with the displayed image property set to a thumbnail size) would certainly cause performance issues - but short of installing a service somewhere (looks like writing a Java web-service wouldn't be the hardest thing ever) to allow some non SAP APIs to allow image manipulation, the only option looks like trying to code a solution completely in SAP - from scratch! All the code that I've seen that actually does stuff like this is pretty complex...

for example look at [Designing a JPEG Decoder & Source Code|http://www.impulseadventure.com/photo/jpeg-decoder.html]

and then if you don't know the format of the image - well then you're looking for even more work!

I Had a thought over the weekend to have a go and see if i could write a utility to convert a JPG into a bitmap (the first step of a resize), just looking at the details in the link above made me decide that trying to run any solution in ABAP was going to be very slow. After that, plugging into Steam seems much more appealing and the idea got dropped... So sorry no new news, but the link above was very helpful in understanding how a JPG is actually stored.

Given that there isn't any WDA specific solution to the problem, it might be worth closing this thread and posting where you might get a bit more visibility - ABAP general for example!

Good luck,

Chris

thomas_jung
Developer Advocate
Developer Advocate
0 Kudos

>I Had a thought over the weekend to have a go and see if i could write a utility to convert a JPG into a bitmap (the first step of a resize),

Actually the IGS can do file type conversions for you. I use the IGS to convert various file types to Bitmap for manipulation in this example:

/people/thomas.jung/blog/2007/09/05/abap-bitmap-image-processing-class

As you can see it is possible to perform bitmap manipulation in ABAP. I perform some simple operations, like rotation and grayscale. However resizing is a bit more complex scenario. I think it would be possible, but very complex.

Too bad the IGS doesn't also have an API to do resizing.

ChrisPaine
Active Contributor
0 Kudos

>Too bad the IGS doesn't also have an API to do resizing.

indeed - but wow - I wish I had known about the work you'd done and those functions of the AGS. That's really cool stuff!

Scaling down an image isn't that hard really, it's when you want to scale up that it become tricky!

depending on the quality required then you can make it more complex/use [different algorithms|http://www.lassekolb.info/gim35_downsampling.htm]. Note that the simplest (nearest neighbour) routine still gives reasonable quality output.

Using the methods you created in your class - you could then find the average value of a group of pixels (if a 1200 wide image needed to be scaled to 100 wide, then I'd use 12x12 squares, find the average red, green and blue values and insert this into the new image.) a 1280 image into 100 wide I'd use 13x13 squares - and some of the original pixels would be sampled multiple times (this is where using weighted sampling could give better results - but the overall result wouldn't be bad... ). Nearest neighbour doesn't even go this far - it just calculates what would be the equivalent pixel in the original image and uses that as the value- e.g. in a 100x100 image reduced to a 10x10 image, the first pixel in the reduced image (0,0) is the pixel found at (5,5). (1,0) is the value of the pixel at (15,5) in the original. Depending on how noisy the originals are/and the level of reduction this could either be very quick and good, or very poor and bad! I guess I could implement both and compare... Here's a [link to a simple implementation of nearest neighbour scaling|http://tech-algorithm.com/articles/nearest-neighbor-image-scaling/]. Although I'd adjust it to read the pixel in the centre of the equivalent box in the original, not the top corner.

Might try to implement this - extend your graphics class to produce thumbnails. Will update when I've had a go...

Edited by: Chris Paine on Jul 27, 2010 11:23 AM - added a little detail about nearest neighbour sampling

ChrisPaine
Active Contributor
0 Kudos

I'm trying to put the results of my tinkering into a blog - which should hopefully get published in the next few days.

But just to update you here first

Yes it can be done! - here's the proof:

[Large image about to be resize to a thumbnail|https://weblogs.sdn.sap.com/weblogs/images/251723740/large_double_rainbow.jpg]

[Image after resizing|https://weblogs.sdn.sap.com/weblogs/images/251723740/resized_rainbow.jpg]

Thought I'd go with some double rainbows to be topical (plus I can't get the darn song out of my head!)

I created a new method in Thomas' class: TRANSFORM_RESIZE with 2 input parameters of type i, new_width and new_height.

Then most of the code is pretty similar to the rotation code that Thomas had already written...


 data l_data type xstring.
  data l_new_data type xstring.
  data l_row type xstring.
  data l_new_row type xstring.
  data l_num_pixels type i.
  data l_counter type i.
  data l_counter2 type i.
  data l_len type i.
  data l_pad type i.
  data l_mod type i.
  data l_width type i.
  data l_bytes_per_pixel type i.
  data: l_y_factor type f,
        l_x_factor type f.
  data l_new_pad type i.
  data l_null(1) type x.
* get the data from the image representation

  l_data = gx_content+data_offset.

  l_bytes_per_pixel = bpp / 8.

  l_width = width * l_bytes_per_pixel.

  l_y_factor = height / new_height.
  l_x_factor = width / new_width.


  l_mod = l_width mod 4.
  l_pad = 4 - l_mod.
  if l_pad = 4.
    l_pad = 0.
  endif.

  l_len = xstrlen( l_data ).

  data lt_rows type standard table of xstring.
  data: l_row_index type i.
  field-symbols <wa_row> like line of lt_rows.

  do new_height times.
    l_row_index = floor( ( sy-index - 1 ) * l_y_factor ).
    l_counter = l_row_index * ( l_width + l_pad ).
    append initial line to lt_rows assigning <wa_row>.
    <wa_row> = l_data+l_counter(l_width).
  enddo.



  l_mod = ( new_width * l_bytes_per_pixel ) mod 4.
  l_new_pad = 4 - l_mod.
  if l_new_pad = 4.
    l_new_pad = 0.
  endif.



  loop at lt_rows assigning <wa_row>.
    clear l_new_row.
    do new_width times.
      l_counter2 =  l_bytes_per_pixel * floor( ( sy-index - 1 ) * l_x_factor ).
      concatenate l_new_row <wa_row>+l_counter2(l_bytes_per_pixel)  into l_new_row in byte mode.
    enddo.


    if l_new_pad > 0.
      do l_new_pad times.
        concatenate  l_new_row l_null into l_new_row in byte mode.
      enddo.
    endif.

    concatenate  l_new_data l_new_row into l_new_data in byte mode.
  endloop.



  data x_new_image type xstring.
  concatenate gx_content(data_offset) l_new_data into x_new_image in byte mode.
  data x_header type xstring.
  x_header = gx_content+0(data_offset).

  data x_size(4) type x.
  data i_size type i.
  i_size = xstrlen( x_new_image ).
  write4 i_size x_size.
  concatenate x_header+0(2) x_size x_header+6 into x_header in byte mode.

  data x_data_size(4) type x.
  data i_data_size type i.
  i_data_size = xstrlen( l_new_data ).
  write4 i_data_size x_data_size.
  concatenate x_header+0(34) x_data_size x_header+38 into x_header in byte mode.

  data x_width(4) type x.
  data x_height(4) type x.
  data i_width type i.
  data i_height type i.
  i_width = new_width.
  i_height = new_height.
  write4 i_width x_width.
  write4 i_height x_height.

  concatenate x_header+0(18) x_width x_height x_header+26 into x_header in byte mode.
  data x_hres(4) type x.
  data x_vres(4) type x.

  x_hres = x_header+38(4).
  x_vres = x_header+42(4).
  concatenate x_header+0(38) x_vres x_hres x_header+46 into x_header in byte mode.

  concatenate x_header  l_new_data into x_new_image in byte mode.

  gx_content = x_new_image.
  me->read_bitmap_header( ).

ChrisPaine
Active Contributor
0 Kudos

(split to avoid formatting issues)

I enhanced the WD app a little to have the resize option - I guess I could have created a popup to prompt for the new size, but I just defaulted the height to be 100px and scale the width accordingly.


method resize .

  data o_exp type ref to zcx_abap_bitmap.
  data lo_nd_bitmap_info type ref to if_wd_context_node.
  data lo_el_bitmap_info type ref to if_wd_context_element.
  data ls_bitmap_info type wd_this->element_bitmap_info.
  data l_new_x type i.

  lo_nd_bitmap_info = wd_context->get_child_node( name = wd_this->wdctx_bitmap_info ).
  lo_el_bitmap_info = lo_nd_bitmap_info->get_element( ).
* get all declared attributes
  lo_el_bitmap_info->get_static_attributes(
    importing
      static_attributes = ls_bitmap_info ).



  l_new_x = floor( ls_bitmap_info-width * 100 / ls_bitmap_info-height ).

  try.
      wd_this->gr_bitmap->transform_resize( new_height = 100
                                            new_width = l_new_x ).
      wd_this->fill_bitmap_info( ).
      wd_this->publish_content_to_icm( ).

    catch zcx_abap_bitmap into o_exp.
*    get message manager
      data lo_api_controller     type ref to if_wd_controller.
      data lo_message_manager    type ref to if_wd_message_manager.
      lo_api_controller ?= wd_this->wd_get_api( ).
      lo_message_manager = lo_api_controller->get_message_manager( ).
*    report message
      lo_message_manager->report_exception(
          message_object           = o_exp  ).
  endtry.

endmethod.

Hope this helps you!

Cheers.

Chris

PS. weblog is titled: ["Double Rainbow resized in ABAP"|http://www.sdn.sap.com/irj/scn/weblogs?blog=/pub/wlg/20268] [original link is broken] [original link is broken] [original link is broken]; hopefully the powers that be will approve it soon!

PPS. They did - and now I'm an expert blogger! Woo hoo - No more waiting for approval

PPPS. A simpler solution was suggested in the comments to my blog....


DATA:
gv_width type i,
gv_height type i,
gv_error TYPE i,
gv_error_msg TYPE string,
gv_thumb_size TYPE i,
gv_thumb TYPE xstring,
gv_xsize TYPE i,
gv_xstring TYPE xstring,

gv_xsize = XSTRLEN( gv_xstring ).
* Convert using IGS
CALL FUNCTION 'RS_IGS_IMGCONV'
EXPORTING
iv_source_image = gv_xstring
iv_source_type = 'image/jpeg'
iv_source_size = gv_xsize
iv_dest_pxwidth = gv_width
iv_dest_pxheight = gv_heigth
iv_dest_type = 'image/jpeg'
IMPORTING
ev_dest_image = gv_thumb
ev_dest_size = gv_thumb_size
ev_error_code = gv_error
ev_error_message = gv_error_msg.

Edited by: Chris Paine on Jul 28, 2010 9:28 AM added link to weblog

Edited by: Chris Paine on Jul 28, 2010 4:37 PM - added alternate code solution

Former Member
0 Kudos

Dear Chris,

Such an inspiration to see the work of yours and Mr. Thomas. I almost lost hopes on my project and now I am back on right track and I can clearly see the achievable goal. Thanks a lot for all.

Here is what I have done:

1. Referred to your blog Double Rainbow resized in ABAP

2. Followed the logic, and again was stuck up in getting data from image file based on representation!

3. Suggestion provided by Mr. Carsten of using class CL_IGS_IMAGE_CONVERTER as below made my job much earier

4. I created an instance of the class. Pushed the image using image URL which I already had, to the class attribute get_url.

Next I set the global attributes on your class instance (like input mimetype, output mimetype, width, height). Then called execute method and followed by GET_IMAGE() to recieve the converted xstring.

create object l_converter.
        l_converter->get_url = '<any get URL>'.
        l_converter->input = 'image/jpeg'. "gif'.
        l_converter->output = 'image/jpeg'.
        l_converter->width = 120.
        l_converter->height = 120.

        call method l_converter->execute
          exceptions
            communication_error = 1
            external_error      = 2
            internal_error      = 3.

        if sy-subrc is initial.

          data: l_img_type     type w3param-cont_type,
                l_img_size     type w3param-cont_len.

            data: l_img          type w3mimetabtype,
                  l_img_maintype type char32,
                  l_img_subtype  type char32,
                  l_img_url      type w3url.

            call method l_converter->get_image
              importing
                blob      = l_img
                blob_type = l_img_type
                blob_size = l_img_size.
         endif.

Answers (0)